(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DGE = {})); }(this, (function (exports) { 'use strict'; /** @license when.js - https://github.com/cujojs/when MIT License (c) copyright B Cavalier & J Hann * A lightweight CommonJS Promises/A and when() implementation * when is part of the cujo.js family of libraries (http://cujojs.com/) * * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * * @version 1.7.1 */ var reduceArray, slice, undef; // // Public API // when.defer = defer; // Create a deferred when.resolve = resolve$1; // Create a resolved promise when.reject = reject; // Create a rejected promise when.join = join; // Join 2 or more promises when.all = all; // Resolve a list of promises when.map = map; // Array.map() for promises when.reduce = reduce; // Array.reduce() for promises when.any = any; // One-winner race when.some = some; // Multi-winner race when.chain = chain; // Make a promise trigger another resolver when.isPromise = isPromise; // Determine if a thing is a promise /** * Register an observer for a promise or immediate value. * * @param {*} promiseOrValue * @param {function?} [onFulfilled] callback to be called when promiseOrValue is * successfully fulfilled. If promiseOrValue is an immediate value, callback * will be invoked immediately. * @param {function?} [onRejected] callback to be called when promiseOrValue is * rejected. * @param {function?} [onProgress] callback to be called when progress updates * are issued for promiseOrValue. * @returns {Promise} a new {@link Promise} that will complete with the return * value of callback or errback or the completion value of promiseOrValue if * callback and/or errback is not supplied. */ function when(promiseOrValue, onFulfilled, onRejected, onProgress) { // Get a trusted promise for the input promiseOrValue, and then // register promise handlers return resolve$1(promiseOrValue).then(onFulfilled, onRejected, onProgress); } /** * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if * promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} * whose value is promiseOrValue if promiseOrValue is an immediate value. * * @param {*} promiseOrValue * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} * whose resolution value is: * * the resolution value of promiseOrValue if it's a foreign promise, or * * promiseOrValue if it's a value */ function resolve$1(promiseOrValue) { var promise, deferred; if(promiseOrValue instanceof Promise$1) { // It's a when.js promise, so we trust it promise = promiseOrValue; } else { // It's not a when.js promise. See if it's a foreign promise or a value. if(isPromise(promiseOrValue)) { // It's a thenable, but we don't know where it came from, so don't trust // its implementation entirely. Introduce a trusted middleman when.js promise deferred = defer(); // IMPORTANT: This is the only place when.js should ever call .then() on an // untrusted promise. Don't expose the return value to the untrusted promise promiseOrValue.then( function(value) { deferred.resolve(value); }, function(reason) { deferred.reject(reason); }, function(update) { deferred.progress(update); } ); promise = deferred.promise; } else { // It's a value, not a promise. Create a resolved promise for it. promise = fulfilled(promiseOrValue); } } return promise; } /** * Returns a rejected promise for the supplied promiseOrValue. The returned * promise will be rejected with: * - promiseOrValue, if it is a value, or * - if promiseOrValue is a promise * - promiseOrValue's value after it is fulfilled * - promiseOrValue's reason after it is rejected * @param {*} promiseOrValue the rejected value of the returned {@link Promise} * @returns {Promise} rejected {@link Promise} */ function reject(promiseOrValue) { return when(promiseOrValue, rejected); } /** * Trusted Promise constructor. A Promise created from this constructor is * a trusted when.js promise. Any other duck-typed promise is considered * untrusted. * @constructor * @name Promise */ function Promise$1(then) { this.then = then; } Promise$1.prototype = { /** * Register a callback that will be called when a promise is * fulfilled or rejected. Optionally also register a progress handler. * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) * @param {function?} [onFulfilledOrRejected] * @param {function?} [onProgress] * @returns {Promise} */ always: function(onFulfilledOrRejected, onProgress) { return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); }, /** * Register a rejection handler. Shortcut for .then(undefined, onRejected) * @param {function?} onRejected * @returns {Promise} */ otherwise: function(onRejected) { return this.then(undef, onRejected); }, /** * Shortcut for .then(function() { return value; }) * @param {*} value * @returns {Promise} a promise that: * - is fulfilled if value is not a promise, or * - if value is a promise, will fulfill with its value, or reject * with its reason. */ yield: function(value) { return this.then(function() { return value; }); }, /** * Assumes that this promise will fulfill with an array, and arranges * for the onFulfilled to be called with the array as its argument list * i.e. onFulfilled.spread(undefined, array). * @param {function} onFulfilled function to receive spread arguments * @returns {Promise} */ spread: function(onFulfilled) { return this.then(function(array) { // array may contain promises, so resolve its contents. return all(array, function(array) { return onFulfilled.apply(undef, array); }); }); } }; /** * Create an already-resolved promise for the supplied value * @private * * @param {*} value * @returns {Promise} fulfilled promise */ function fulfilled(value) { var p = new Promise$1(function(onFulfilled) { // TODO: Promises/A+ check typeof onFulfilled try { return resolve$1(onFulfilled ? onFulfilled(value) : value); } catch(e) { return rejected(e); } }); return p; } /** * Create an already-rejected {@link Promise} with the supplied * rejection reason. * @private * * @param {*} reason * @returns {Promise} rejected promise */ function rejected(reason) { var p = new Promise$1(function(_, onRejected) { // TODO: Promises/A+ check typeof onRejected try { return onRejected ? resolve$1(onRejected(reason)) : rejected(reason); } catch(e) { return rejected(e); } }); return p; } /** * Creates a new, Deferred with fully isolated resolver and promise parts, * either or both of which may be given out safely to consumers. * The Deferred itself has the full API: resolve, reject, progress, and * then. The resolver has resolve, reject, and progress. The promise * only has then. * * @returns {Deferred} */ function defer() { var deferred, promise, handlers, progressHandlers, _then, _progress, _resolve; /** * The promise for the new deferred * @type {Promise} */ promise = new Promise$1(then); /** * The full Deferred object, with {@link Promise} and {@link Resolver} parts * @class Deferred * @name Deferred */ deferred = { then: then, // DEPRECATED: use deferred.promise.then resolve: promiseResolve, reject: promiseReject, // TODO: Consider renaming progress() to notify() progress: promiseProgress, promise: promise, resolver: { resolve: promiseResolve, reject: promiseReject, progress: promiseProgress } }; handlers = []; progressHandlers = []; /** * Pre-resolution then() that adds the supplied callback, errback, and progback * functions to the registered listeners * @private * * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler */ _then = function(onFulfilled, onRejected, onProgress) { // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress var deferred, progressHandler; deferred = defer(); progressHandler = typeof onProgress === 'function' ? function(update) { try { // Allow progress handler to transform progress event deferred.progress(onProgress(update)); } catch(e) { // Use caught value as progress deferred.progress(e); } } : function(update) { deferred.progress(update); }; handlers.push(function(promise) { promise.then(onFulfilled, onRejected) .then(deferred.resolve, deferred.reject, progressHandler); }); progressHandlers.push(progressHandler); return deferred.promise; }; /** * Issue a progress event, notifying all progress listeners * @private * @param {*} update progress event payload to pass to all listeners */ _progress = function(update) { processQueue(progressHandlers, update); return update; }; /** * Transition from pre-resolution state to post-resolution state, notifying * all listeners of the resolution or rejection * @private * @param {*} value the value of this deferred */ _resolve = function(value) { value = resolve$1(value); // Replace _then with one that directly notifies with the result. _then = value.then; // Replace _resolve so that this Deferred can only be resolved once _resolve = resolve$1; // Make _progress a noop, to disallow progress for the resolved promise. _progress = noop; // Notify handlers processQueue(handlers, value); // Free progressHandlers array since we'll never issue progress events progressHandlers = handlers = undef; return value; }; return deferred; /** * Wrapper to allow _then to be replaced safely * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} new promise */ function then(onFulfilled, onRejected, onProgress) { // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress return _then(onFulfilled, onRejected, onProgress); } /** * Wrapper to allow _resolve to be replaced */ function promiseResolve(val) { return _resolve(val); } /** * Wrapper to allow _reject to be replaced */ function promiseReject(err) { return _resolve(rejected(err)); } /** * Wrapper to allow _progress to be replaced */ function promiseProgress(update) { return _progress(update); } } /** * Determines if promiseOrValue is a promise or not. Uses the feature * test from http://wiki.commonjs.org/wiki/Promises/A to determine if * promiseOrValue is a promise. * * @param {*} promiseOrValue anything * @returns {boolean} true if promiseOrValue is a {@link Promise} */ function isPromise(promiseOrValue) { return promiseOrValue && typeof promiseOrValue.then === 'function'; } /** * Initiates a competitive race, returning a promise that will resolve when * howMany of the supplied promisesOrValues have resolved, or will reject when * it becomes impossible for howMany to resolve, for example, when * (promisesOrValues.length - howMany) + 1 input promises reject. * * @param {Array} promisesOrValues array of anything, may contain a mix * of promises and values * @param howMany {number} number of promisesOrValues to resolve * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} promise that will resolve to an array of howMany values that * resolved first, or will reject with an array of (promisesOrValues.length - howMany) + 1 * rejection reasons. */ function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { checkCallbacks(2, arguments); return when(promisesOrValues, function(promisesOrValues) { var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; len = promisesOrValues.length >>> 0; toResolve = Math.max(0, Math.min(howMany, len)); values = []; toReject = (len - toResolve) + 1; reasons = []; deferred = defer(); // No items in the input, resolve immediately if (!toResolve) { deferred.resolve(values); } else { progress = deferred.progress; rejectOne = function(reason) { reasons.push(reason); if(!--toReject) { fulfillOne = rejectOne = noop; deferred.reject(reasons); } }; fulfillOne = function(val) { // This orders the values based on promise resolution order // Another strategy would be to use the original position of // the corresponding promise. values.push(val); if (!--toResolve) { fulfillOne = rejectOne = noop; deferred.resolve(values); } }; for(i = 0; i < len; ++i) { if(i in promisesOrValues) { when(promisesOrValues[i], fulfiller, rejecter, progress); } } } return deferred.then(onFulfilled, onRejected, onProgress); function rejecter(reason) { rejectOne(reason); } function fulfiller(val) { fulfillOne(val); } }); } /** * Initiates a competitive race, returning a promise that will resolve when * any one of the supplied promisesOrValues has resolved or will reject when * *all* promisesOrValues have rejected. * * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} promise that will resolve to the value that resolved first, or * will reject with an array of all rejected inputs. */ function any(promisesOrValues, onFulfilled, onRejected, onProgress) { function unwrapSingleResult(val) { return onFulfilled ? onFulfilled(val[0]) : val[0]; } return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); } /** * Return a promise that will resolve only once all the supplied promisesOrValues * have resolved. The resolution value of the returned promise will be an array * containing the resolution values of each of the promisesOrValues. * @memberOf when * * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} */ function all(promisesOrValues, onFulfilled, onRejected, onProgress) { checkCallbacks(1, arguments); return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); } /** * Joins multiple promises into a single returned promise. * @returns {Promise} a promise that will fulfill when *all* the input promises * have fulfilled, or will reject when *any one* of the input promises rejects. */ function join(/* ...promises */) { return map(arguments, identity); } /** * Traditional map function, similar to `Array.prototype.map()`, but allows * input to contain {@link Promise}s and/or values, and mapFunc may return * either a value or a {@link Promise} * * @param {Array|Promise} promise array of anything, may contain a mix * of {@link Promise}s and values * @param {function} mapFunc mapping function mapFunc(value) which may return * either a {@link Promise} or value * @returns {Promise} a {@link Promise} that will resolve to an array containing * the mapped output values. */ function map(promise, mapFunc) { return when(promise, function(array) { var results, len, toResolve, resolve, i, d; // Since we know the resulting length, we can preallocate the results // array to avoid array expansions. toResolve = len = array.length >>> 0; results = []; d = defer(); if(!toResolve) { d.resolve(results); } else { resolve = function resolveOne(item, i) { when(item, mapFunc).then(function(mapped) { results[i] = mapped; if(!--toResolve) { d.resolve(results); } }, d.reject); }; // Since mapFunc may be async, get all invocations of it into flight for(i = 0; i < len; i++) { if(i in array) { resolve(array[i], i); } else { --toResolve; } } } return d.promise; }); } /** * Traditional reduce function, similar to `Array.prototype.reduce()`, but * input may contain promises and/or values, and reduceFunc * may return either a value or a promise, *and* initialValue may * be a promise for the starting value. * * @param {Array|Promise} promise array or promise for an array of anything, * may contain a mix of promises and values. * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), * where total is the total number of items being reduced, and will be the same * in each call to reduceFunc. * @returns {Promise} that will resolve to the final reduced value */ function reduce(promise, reduceFunc /*, initialValue */) { var args = slice.call(arguments, 1); return when(promise, function(array) { var total; total = array.length; // Wrap the supplied reduceFunc with one that handles promises and then // delegates to the supplied. args[0] = function (current, val, i) { return when(current, function (c) { return when(val, function (value) { return reduceFunc(c, value, i, total); }); }); }; return reduceArray.apply(array, args); }); } /** * Ensure that resolution of promiseOrValue will trigger resolver with the * value or reason of promiseOrValue, or instead with resolveValue if it is provided. * * @param promiseOrValue * @param {Object} resolver * @param {function} resolver.resolve * @param {function} resolver.reject * @param {*} [resolveValue] * @returns {Promise} */ function chain(promiseOrValue, resolver, resolveValue) { var useResolveValue = arguments.length > 2; return when(promiseOrValue, function(val) { val = useResolveValue ? resolveValue : val; resolver.resolve(val); return val; }, function(reason) { resolver.reject(reason); return rejected(reason); }, resolver.progress ); } // // Utility functions // /** * Apply all functions in queue to value * @param {Array} queue array of functions to execute * @param {*} value argument passed to each function */ function processQueue(queue, value) { var handler, i = 0; while (handler = queue[i++]) { handler(value); } } /** * Helper that checks arrayOfCallbacks to ensure that each element is either * a function, or null or undefined. * @private * @param {number} start index at which to start checking items in arrayOfCallbacks * @param {Array} arrayOfCallbacks array to check * @throws {Error} if any element of arrayOfCallbacks is something other than * a functions, null, or undefined. */ function checkCallbacks(start, arrayOfCallbacks) { // TODO: Promises/A+ update type checking and docs var arg, i = arrayOfCallbacks.length; while(i > start) { arg = arrayOfCallbacks[--i]; if (arg != null && typeof arg != 'function') { throw new Error('arg '+i+' must be a function'); } } } /** * No-Op function used in method replacement * @private */ function noop() {} slice = [].slice; // ES5 reduce implementation if native not available // See: http://es5.github.com/#x15.4.4.21 as there are many // specifics and edge cases. reduceArray = [].reduce || function(reduceFunc /*, initialValue */) { /*jshint maxcomplexity: 7*/ // ES5 dictates that reduce.length === 1 // This implementation deviates from ES5 spec in the following ways: // 1. It does not check if reduceFunc is a Callable var arr, args, reduced, len, i; i = 0; // This generates a jshint warning, despite being valid // "Missing 'new' prefix when invoking a constructor." // See https://github.com/jshint/jshint/issues/392 arr = Object(this); len = arr.length >>> 0; args = arguments; // If no initialValue, use first item of array (we know length !== 0 here) // and adjust i to start at second item if(args.length <= 1) { // Skip to the first real element in the array for(;;) { if(i in arr) { reduced = arr[i++]; break; } // If we reached the end of the array without finding any real // elements, it's a TypeError if(++i >= len) { throw new TypeError(); } } } else { // If initialValue provided, use it reduced = args[1]; } // Do the actual reduce for(;i < len; ++i) { // Skip holes if(i in arr) { reduced = reduceFunc(reduced, arr[i], i, arr); } } return reduced; }; function identity(x) { return x; } /** * @function * * @param {*} value The object. * @returns {Boolean} Returns true if the object is defined, returns false otherwise. * * @example * if (Cesium.defined(positions)) { * doSomething(); * } else { * doSomethingElse(); * } */ function defined(value) { return value !== undefined && value !== null; } /** * 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. *

* 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 * @constructor * @extends Error * * @param {String} [message] The error message for this exception. * * @see RuntimeError */ function DeveloperError(message) { /** * 'DeveloperError' indicating that this exception was thrown due to a developer error. * @type {String} * @readonly */ this.name = "DeveloperError"; /** * The explanation for why this exception was thrown. * @type {String} * @readonly */ this.message = message; //Browsers such as IE don't have a stack property until you actually throw the error. var stack; try { throw new Error(); } catch (e) { stack = e.stack; } /** * The stack trace of this exception, if available. * @type {String} * @readonly */ this.stack = stack; } if (defined(Object.create)) { DeveloperError.prototype = Object.create(Error.prototype); DeveloperError.prototype.constructor = DeveloperError; } DeveloperError.prototype.toString = function () { var str = this.name + ": " + this.message; if (defined(this.stack)) { str += "\n" + this.stack.toString(); } return str; }; /** * @private */ DeveloperError.throwInstantiationError = function () { throw new DeveloperError( "This function defines an interface and should not be called directly." ); }; /** * Contains functions for checking that supplied arguments are of a specified type * or meet specified conditions * @private */ var Check = {}; /** * Contains type checking functions, all using the typeof operator */ Check.typeOf = {}; function getUndefinedErrorMessage(name) { return name + " is required, actual value was undefined"; } function getFailedTypeErrorMessage(actual, expected, name) { return ( "Expected " + name + " to be typeof " + expected + ", actual typeof was " + actual ); } /** * Throws if test is not defined * * @param {String} name The name of the variable being tested * @param {*} test The value that is to be checked * @exception {DeveloperError} test must be defined */ Check.defined = function (name, test) { if (!defined(test)) { throw new DeveloperError(getUndefinedErrorMessage(name)); } }; /** * Throws if test is not typeof 'function' * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @exception {DeveloperError} test must be typeof 'function' */ Check.typeOf.func = function (name, test) { if (typeof test !== "function") { throw new DeveloperError( getFailedTypeErrorMessage(typeof test, "function", name) ); } }; /** * Throws if test is not typeof 'string' * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @exception {DeveloperError} test must be typeof 'string' */ Check.typeOf.string = function (name, test) { if (typeof test !== "string") { throw new DeveloperError( getFailedTypeErrorMessage(typeof test, "string", name) ); } }; /** * Throws if test is not typeof 'number' * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @exception {DeveloperError} test must be typeof 'number' */ Check.typeOf.number = function (name, test) { if (typeof test !== "number") { throw new DeveloperError( getFailedTypeErrorMessage(typeof test, "number", name) ); } }; /** * Throws if test is not typeof 'number' and less than limit * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @param {Number} limit The limit value to compare against * @exception {DeveloperError} test must be typeof 'number' and less than limit */ Check.typeOf.number.lessThan = function (name, test, limit) { Check.typeOf.number(name, test); if (test >= limit) { throw new DeveloperError( "Expected " + name + " to be less than " + limit + ", actual value was " + test ); } }; /** * Throws if test is not typeof 'number' and less than or equal to limit * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @param {Number} limit The limit value to compare against * @exception {DeveloperError} test must be typeof 'number' and less than or equal to limit */ Check.typeOf.number.lessThanOrEquals = function (name, test, limit) { Check.typeOf.number(name, test); if (test > limit) { throw new DeveloperError( "Expected " + name + " to be less than or equal to " + limit + ", actual value was " + test ); } }; /** * Throws if test is not typeof 'number' and greater than limit * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @param {Number} limit The limit value to compare against * @exception {DeveloperError} test must be typeof 'number' and greater than limit */ Check.typeOf.number.greaterThan = function (name, test, limit) { Check.typeOf.number(name, test); if (test <= limit) { throw new DeveloperError( "Expected " + name + " to be greater than " + limit + ", actual value was " + test ); } }; /** * Throws if test is not typeof 'number' and greater than or equal to limit * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @param {Number} limit The limit value to compare against * @exception {DeveloperError} test must be typeof 'number' and greater than or equal to limit */ Check.typeOf.number.greaterThanOrEquals = function (name, test, limit) { Check.typeOf.number(name, test); if (test < limit) { throw new DeveloperError( "Expected " + name + " to be greater than or equal to" + limit + ", actual value was " + test ); } }; /** * Throws if test is not typeof 'object' * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @exception {DeveloperError} test must be typeof 'object' */ Check.typeOf.object = function (name, test) { if (typeof test !== "object") { throw new DeveloperError( getFailedTypeErrorMessage(typeof test, "object", name) ); } }; /** * Throws if test is not typeof 'boolean' * * @param {String} name The name of the variable being tested * @param {*} test The value to test * @exception {DeveloperError} test must be typeof 'boolean' */ Check.typeOf.bool = function (name, test) { if (typeof test !== "boolean") { throw new DeveloperError( getFailedTypeErrorMessage(typeof test, "boolean", name) ); } }; /** * Throws if test1 and test2 is not typeof 'number' and not equal in value * * @param {String} name1 The name of the first variable being tested * @param {String} name2 The name of the second variable being tested against * @param {*} test1 The value to test * @param {*} test2 The value to test against * @exception {DeveloperError} test1 and test2 should be type of 'number' and be equal in value */ Check.typeOf.number.equals = function (name1, name2, test1, test2) { Check.typeOf.number(name1, test1); Check.typeOf.number(name2, test2); if (test1 !== test2) { throw new DeveloperError( name1 + " must be equal to " + name2 + ", the actual values are " + test1 + " and " + test2 ); } }; /** * Returns the first parameter if not undefined, otherwise the second parameter. * Useful for setting a default value for a parameter. * * @function * * @param {*} a * @param {*} b * @returns {*} Returns the first parameter if not undefined, otherwise the second parameter. * * @example * param = Cesium.defaultValue(param, 'default'); */ function defaultValue(a, b) { if (a !== undefined && a !== null) { return a; } return b; } /** * A frozen empty object that can be used as the default value for options passed as * an object literal. * @type {Object} * @memberof defaultValue */ defaultValue.EMPTY_OBJECT = Object.freeze({}); /* 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) */ function MersenneTwister(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>> 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>> 1) ^ mag01[y & 0x1]; } for (;kk>> 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 */ }; /** * Math functions. * * @exports CesiumMath * @alias Math */ 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; /** * 0.000000000000000000001 * @type {Number} * @constant */ CesiumMath.EPSILON21 = 0.000000000000000000001; /** * The gravitational parameter of the Earth in meters cubed * per second squared as defined by the WGS84 model: 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; /** * 4 * 1024 * 1024 * 1024 * @type {Number} * @constant */ CesiumMath.FOUR_GIGABYTES = 4 * 1024 * 1024 * 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. * * @function * @param {Number} value The value to return the sign of. * @returns {Number} The sign of value. */ // eslint-disable-next-line es/no-math-sign CesiumMath.sign = defaultValue(Math.sign, function sign(value) { value = +value; // coerce to number if (value === 0 || value !== value) { // zero or NaN return value; } return value > 0 ? 1 : -1; }); /** * Returns 1.0 if the given value is positive or zero, and -1.0 if it is negative. * This is similar to {@link CesiumMath#sign} except that returns 1.0 instead of * 0.0 when the input value is 0.0. * @param {Number} value The value to return the sign of. * @returns {Number} The sign of value. */ CesiumMath.signNotZero = function (value) { return value < 0.0 ? -1.0 : 1.0; }; /** * Converts a scalar value in the range [-1.0, 1.0] to a SNORM in the range [0, rangeMaximum] * @param {Number} value The scalar value in the range [-1.0, 1.0] * @param {Number} [rangeMaximum=255] The maximum value in the mapped range, 255 by default. * @returns {Number} A SNORM value, where 0 maps to -1.0 and rangeMaximum maps to 1.0. * * @see CesiumMath.fromSNorm */ CesiumMath.toSNorm = function (value, rangeMaximum) { rangeMaximum = defaultValue(rangeMaximum, 255); return Math.round( (CesiumMath.clamp(value, -1.0, 1.0) * 0.5 + 0.5) * rangeMaximum ); }; /** * Converts a SNORM value in the range [0, rangeMaximum] to a scalar in the range [-1.0, 1.0]. * @param {Number} value SNORM value in the range [0, rangeMaximum] * @param {Number} [rangeMaximum=255] The maximum value in the SNORM range, 255 by default. * @returns {Number} Scalar in the range [-1.0, 1.0]. * * @see CesiumMath.toSNorm */ CesiumMath.fromSNorm = function (value, rangeMaximum) { rangeMaximum = defaultValue(rangeMaximum, 255); return ( (CesiumMath.clamp(value, 0.0, rangeMaximum) / rangeMaximum) * 2.0 - 1.0 ); }; /** * Converts a scalar value in the range [rangeMinimum, rangeMaximum] to a scalar in the range [0.0, 1.0] * @param {Number} value The scalar value in the range [rangeMinimum, rangeMaximum] * @param {Number} rangeMinimum The minimum value in the mapped range. * @param {Number} rangeMaximum The maximum value in the mapped range. * @returns {Number} A scalar value, where rangeMinimum maps to 0.0 and rangeMaximum maps to 1.0. */ CesiumMath.normalize = function (value, rangeMinimum, rangeMaximum) { rangeMaximum = Math.max(rangeMaximum - rangeMinimum, 0.0); return rangeMaximum === 0.0 ? 0.0 : CesiumMath.clamp((value - rangeMinimum) / rangeMaximum, 0.0, 1.0); }; /** * Returns the hyperbolic sine of a number. * The hyperbolic sine of value is defined to be * (ex - e-x)/2.0 * where e is Euler's number, approximately 2.71828183. * *

Special cases: *

    *
  • If the argument is NaN, then the result is NaN.
  • * *
  • If the argument is infinite, then the result is an infinity * with the same sign as the argument.
  • * *
  • If the argument is zero, then the result is a zero with the * same sign as the argument.
  • *
*

* * @function * @param {Number} value The number whose hyperbolic sine is to be returned. * @returns {Number} The hyperbolic sine of value. */ // eslint-disable-next-line es/no-math-sinh CesiumMath.sinh = defaultValue(Math.sinh, function sinh(value) { return (Math.exp(value) - Math.exp(-value)) / 2.0; }); /** * Returns the hyperbolic cosine of a number. * The hyperbolic cosine of value is defined to be * (ex + e-x)/2.0 * where e is Euler's number, approximately 2.71828183. * *

Special cases: *

    *
  • If the argument is NaN, then the result is NaN.
  • * *
  • If the argument is infinite, then the result is positive infinity.
  • * *
  • If the argument is zero, then the result is 1.0.
  • *
*

* * @function * @param {Number} value The number whose hyperbolic cosine is to be returned. * @returns {Number} The hyperbolic cosine of value. */ // eslint-disable-next-line es/no-math-cosh CesiumMath.cosh = defaultValue(Math.cosh, function cosh(value) { return (Math.exp(value) + Math.exp(-value)) / 2.0; }); /** * Computes the linear interpolation of two values. * * @param {Number} p The start value to interpolate. * @param {Number} q The end value to interpolate. * @param {Number} time The time of interpolation generally in the range [0.0, 1.0]. * @returns {Number} The linearly interpolated value. * * @example * var n = Cesium.Math.lerp(0.0, 2.0, 0.5); // returns 1.0 */ CesiumMath.lerp = function (p, q, time) { return (1.0 - time) * p + time * q; }; /** * pi * * @type {Number} * @constant */ CesiumMath.PI = Math.PI; /** * 1/pi * * @type {Number} * @constant */ CesiumMath.ONE_OVER_PI = 1.0 / Math.PI; /** * pi/2 * * @type {Number} * @constant */ CesiumMath.PI_OVER_TWO = Math.PI / 2.0; /** * pi/3 * * @type {Number} * @constant */ CesiumMath.PI_OVER_THREE = Math.PI / 3.0; /** * pi/4 * * @type {Number} * @constant */ CesiumMath.PI_OVER_FOUR = Math.PI / 4.0; /** * pi/6 * * @type {Number} * @constant */ CesiumMath.PI_OVER_SIX = Math.PI / 6.0; /** * 3pi/2 * * @type {Number} * @constant */ CesiumMath.THREE_PI_OVER_TWO = (3.0 * Math.PI) / 2.0; /** * 2pi * * @type {Number} * @constant */ CesiumMath.TWO_PI = 2.0 * Math.PI; /** * 1/2pi * * @type {Number} * @constant */ CesiumMath.ONE_OVER_TWO_PI = 1.0 / (2.0 * Math.PI); /** * The number of radians in a degree. * * @type {Number} * @constant */ CesiumMath.RADIANS_PER_DEGREE = Math.PI / 180.0; /** * The number of degrees in a radian. * * @type {Number} * @constant */ CesiumMath.DEGREES_PER_RADIAN = 180.0 / Math.PI; /** * The number of radians in an arc second. * * @type {Number} * @constant */ 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) { //>>includeStart('debug', pragmas.debug); if (!defined(degrees)) { throw new DeveloperError("degrees is required."); } //>>includeEnd('debug'); 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) { //>>includeStart('debug', pragmas.debug); if (!defined(radians)) { throw new DeveloperError("radians is required."); } //>>includeEnd('debug'); return radians * CesiumMath.DEGREES_PER_RADIAN; }; /** * Converts a longitude value, in radians, to the range [-Math.PI, Math.PI). * * @param {Number} angle The longitude value, in radians, to convert to the range [-Math.PI, Math.PI). * @returns {Number} The equivalent longitude value in the range [-Math.PI, Math.PI). * * @example * // Convert 270 degrees to -90 degrees longitude * var longitude = Cesium.Math.convertLongitudeRange(Cesium.Math.toRadians(270.0)); */ CesiumMath.convertLongitudeRange = function (angle) { //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { throw new DeveloperError("angle is required."); } //>>includeEnd('debug'); 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; }; /** * Convenience function that clamps a latitude value, in radians, to the range [-Math.PI/2, Math.PI/2). * Useful for sanitizing data before use in objects requiring correct range. * * @param {Number} angle The latitude value, in radians, to clamp to the range [-Math.PI/2, Math.PI/2). * @returns {Number} The latitude value clamped to the range [-Math.PI/2, Math.PI/2). * * @example * // Clamp 108 degrees latitude to 90 degrees latitude * var latitude = Cesium.Math.clampToLatitudeRange(Cesium.Math.toRadians(108.0)); */ CesiumMath.clampToLatitudeRange = function (angle) { //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { throw new DeveloperError("angle is required."); } //>>includeEnd('debug'); return CesiumMath.clamp( angle, -1 * CesiumMath.PI_OVER_TWO, CesiumMath.PI_OVER_TWO ); }; /** * 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 [-CesiumMath.PI, CesiumMath.PI]. */ CesiumMath.negativePiToPi = function (angle) { //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { throw new DeveloperError("angle is required."); } //>>includeEnd('debug'); if (angle >= -CesiumMath.PI && angle <= CesiumMath.PI) { // Early exit if the input is already inside the range. This avoids // unnecessary math which could introduce floating point error. return angle; } return CesiumMath.zeroToTwoPi(angle + CesiumMath.PI) - CesiumMath.PI; }; /** * 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 [0, CesiumMath.TWO_PI]. */ CesiumMath.zeroToTwoPi = function (angle) { //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { throw new DeveloperError("angle is required."); } //>>includeEnd('debug'); if (angle >= 0 && angle <= CesiumMath.TWO_PI) { // Early exit if the input is already inside the range. This avoids // unnecessary math which could introduce floating point error. return angle; } var mod = CesiumMath.mod(angle, CesiumMath.TWO_PI); if ( Math.abs(mod) < CesiumMath.EPSILON14 && Math.abs(angle) > CesiumMath.EPSILON14 ) { return CesiumMath.TWO_PI; } return mod; }; /** * The modulo operation that also works for negative dividends. * * @param {Number} m The dividend. * @param {Number} n The divisor. * @returns {Number} The remainder. */ CesiumMath.mod = function (m, n) { //>>includeStart('debug', pragmas.debug); if (!defined(m)) { throw new DeveloperError("m is required."); } if (!defined(n)) { throw new DeveloperError("n is required."); } if (n === 0.0) { throw new DeveloperError("divisor cannot be 0."); } //>>includeEnd('debug'); if (CesiumMath.sign(m) === CesiumMath.sign(n) && Math.abs(m) < Math.abs(n)) { // Early exit if the input does not need to be modded. This avoids // unnecessary math which could introduce floating point error. return m; } return ((m % n) + n) % n; }; /** * Determines if two values are equal using an absolute or relative tolerance test. This is useful * to avoid problems due to roundoff error when comparing floating-point values directly. The values are * first compared using an absolute tolerance test. If that fails, a relative tolerance test is performed. * Use this test if you are unsure of the magnitudes of left and right. * * @param {Number} left The first value to compare. * @param {Number} right The other value to compare. * @param {Number} [relativeEpsilon=0] The maximum inclusive delta between left and right for the relative tolerance test. * @param {Number} [absoluteEpsilon=relativeEpsilon] The maximum inclusive delta between left and right for the absolute tolerance test. * @returns {Boolean} true if the values are equal within the epsilon; otherwise, false. * * @example * var a = Cesium.Math.equalsEpsilon(0.0, 0.01, Cesium.Math.EPSILON2); // true * var b = Cesium.Math.equalsEpsilon(0.0, 0.1, Cesium.Math.EPSILON2); // false * var c = Cesium.Math.equalsEpsilon(3699175.1634344, 3699175.2, Cesium.Math.EPSILON7); // true * var d = Cesium.Math.equalsEpsilon(3699175.1634344, 3699175.2, Cesium.Math.EPSILON9); // false */ CesiumMath.equalsEpsilon = function ( left, right, relativeEpsilon, absoluteEpsilon ) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("left is required."); } if (!defined(right)) { throw new DeveloperError("right is required."); } //>>includeEnd('debug'); relativeEpsilon = defaultValue(relativeEpsilon, 0.0); absoluteEpsilon = defaultValue(absoluteEpsilon, relativeEpsilon); var absDiff = Math.abs(left - right); return ( absDiff <= absoluteEpsilon || absDiff <= relativeEpsilon * Math.max(Math.abs(left), Math.abs(right)) ); }; /** * Determines if the left value is less than the right value. If the two values are within * absoluteEpsilon of each other, they are considered equal and this function returns false. * * @param {Number} left The first number to compare. * @param {Number} right The second number to compare. * @param {Number} absoluteEpsilon The absolute epsilon to use in comparison. * @returns {Boolean} true if left is less than right by more than * absoluteEpsilon. false if left is greater or if the two * values are nearly equal. */ CesiumMath.lessThan = function (left, right, absoluteEpsilon) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("first is required."); } if (!defined(right)) { throw new DeveloperError("second is required."); } if (!defined(absoluteEpsilon)) { throw new DeveloperError("absoluteEpsilon is required."); } //>>includeEnd('debug'); return left - right < -absoluteEpsilon; }; /** * Determines if the left value is less than or equal to the right value. If the two values are within * absoluteEpsilon of each other, they are considered equal and this function returns true. * * @param {Number} left The first number to compare. * @param {Number} right The second number to compare. * @param {Number} absoluteEpsilon The absolute epsilon to use in comparison. * @returns {Boolean} true if left is less than right or if the * the values are nearly equal. */ CesiumMath.lessThanOrEquals = function (left, right, absoluteEpsilon) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("first is required."); } if (!defined(right)) { throw new DeveloperError("second is required."); } if (!defined(absoluteEpsilon)) { throw new DeveloperError("absoluteEpsilon is required."); } //>>includeEnd('debug'); return left - right < absoluteEpsilon; }; /** * Determines if the left value is greater the right value. If the two values are within * absoluteEpsilon of each other, they are considered equal and this function returns false. * * @param {Number} left The first number to compare. * @param {Number} right The second number to compare. * @param {Number} absoluteEpsilon The absolute epsilon to use in comparison. * @returns {Boolean} true if left is greater than right by more than * absoluteEpsilon. false if left is less or if the two * values are nearly equal. */ CesiumMath.greaterThan = function (left, right, absoluteEpsilon) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("first is required."); } if (!defined(right)) { throw new DeveloperError("second is required."); } if (!defined(absoluteEpsilon)) { throw new DeveloperError("absoluteEpsilon is required."); } //>>includeEnd('debug'); return left - right > absoluteEpsilon; }; /** * Determines if the left value is greater than or equal to the right value. If the two values are within * absoluteEpsilon of each other, they are considered equal and this function returns true. * * @param {Number} left The first number to compare. * @param {Number} right The second number to compare. * @param {Number} absoluteEpsilon The absolute epsilon to use in comparison. * @returns {Boolean} true if left is greater than right or if the * the values are nearly equal. */ CesiumMath.greaterThanOrEquals = function (left, right, absoluteEpsilon) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("first is required."); } if (!defined(right)) { throw new DeveloperError("second is required."); } if (!defined(absoluteEpsilon)) { throw new DeveloperError("absoluteEpsilon is required."); } //>>includeEnd('debug'); return left - right > -absoluteEpsilon; }; var factorials = [1]; /** * Computes the factorial of the provided number. * * @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. * * @exception {DeveloperError} A number greater than or equal to 0 is required. * * * @example * //Compute 7!, which is equal to 5040 * var computedFactorial = Cesium.Math.factorial(7); * * @see {@link http://en.wikipedia.org/wiki/Factorial|Factorial on Wikipedia} */ CesiumMath.factorial = function (n) { //>>includeStart('debug', pragmas.debug); if (typeof n !== "number" || n < 0) { throw new DeveloperError( "A number greater than or equal to 0 is required." ); } //>>includeEnd('debug'); var length = factorials.length; if (n >= length) { var sum = factorials[length - 1]; for (var i = length; i <= n; i++) { var next = sum * i; factorials.push(next); sum = next; } } return factorials[n]; }; /** * Increments a number with a wrapping to a minimum value if the number exceeds the maximum value. * * @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. * * @exception {DeveloperError} Maximum value must be greater than minimum value. * * @example * var n = Cesium.Math.incrementWrap(5, 10, 0); // returns 6 * var n = Cesium.Math.incrementWrap(10, 10, 0); // returns 0 */ CesiumMath.incrementWrap = function (n, maximumValue, minimumValue) { minimumValue = defaultValue(minimumValue, 0.0); //>>includeStart('debug', pragmas.debug); if (!defined(n)) { throw new DeveloperError("n is required."); } if (maximumValue <= minimumValue) { throw new DeveloperError("maximumValue must be greater than minimumValue."); } //>>includeEnd('debug'); ++n; if (n > maximumValue) { n = minimumValue; } return n; }; /** * Determines if a non-negative integer is a power of two. * The maximum allowed input is (2^32)-1 due to 32-bit bitwise operator limitation in Javascript. * * @param {Number} n The integer to test in the range [0, (2^32)-1]. * @returns {Boolean} true if the number if a power of two; otherwise, false. * * @exception {DeveloperError} A number between 0 and (2^32)-1 is required. * * @example * var t = Cesium.Math.isPowerOfTwo(16); // true * var f = Cesium.Math.isPowerOfTwo(20); // false */ CesiumMath.isPowerOfTwo = function (n) { //>>includeStart('debug', pragmas.debug); if (typeof n !== "number" || n < 0 || n > 4294967295) { throw new DeveloperError("A number between 0 and (2^32)-1 is required."); } //>>includeEnd('debug'); return n !== 0 && (n & (n - 1)) === 0; }; /** * Computes the next power-of-two integer greater than or equal to the provided non-negative integer. * The maximum allowed input is 2^31 due to 32-bit bitwise operator limitation in Javascript. * * @param {Number} n The integer to test in the range [0, 2^31]. * @returns {Number} The next power-of-two integer. * * @exception {DeveloperError} A number between 0 and 2^31 is required. * * @example * var n = Cesium.Math.nextPowerOfTwo(29); // 32 * var m = Cesium.Math.nextPowerOfTwo(32); // 32 */ CesiumMath.nextPowerOfTwo = function (n) { //>>includeStart('debug', pragmas.debug); if (typeof n !== "number" || n < 0 || n > 2147483648) { throw new DeveloperError("A number between 0 and 2^31 is required."); } //>>includeEnd('debug'); // 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; }; /** * Computes the previous power-of-two integer less than or equal to the provided non-negative integer. * The maximum allowed input is (2^32)-1 due to 32-bit bitwise operator limitation in Javascript. * * @param {Number} n The integer to test in the range [0, (2^32)-1]. * @returns {Number} The previous power-of-two integer. * * @exception {DeveloperError} A number between 0 and (2^32)-1 is required. * * @example * var n = Cesium.Math.previousPowerOfTwo(29); // 16 * var m = Cesium.Math.previousPowerOfTwo(32); // 32 */ CesiumMath.previousPowerOfTwo = function (n) { //>>includeStart('debug', pragmas.debug); if (typeof n !== "number" || n < 0 || n > 4294967295) { throw new DeveloperError("A number between 0 and (2^32)-1 is required."); } //>>includeEnd('debug'); n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; n |= n >> 32; // The previous bitwise operations implicitly convert to signed 32-bit. Use `>>>` to convert to unsigned n = (n >>> 0) - (n >>> 1); return n; }; /** * Constraint a value to lie between two values. * * @param {Number} value The value to constrain. * @param {Number} min The minimum value. * @param {Number} max The maximum value. * @returns {Number} The value clamped so that min <= value <= max. */ CesiumMath.clamp = function (value, min, max) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(min)) { throw new DeveloperError("min is required."); } if (!defined(max)) { throw new DeveloperError("max is required."); } //>>includeEnd('debug'); 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}. * * @param {Number} seed An integer used as the seed. */ CesiumMath.setRandomNumberSeed = function (seed) { //>>includeStart('debug', pragmas.debug); if (!defined(seed)) { throw new DeveloperError("seed is required."); } //>>includeEnd('debug'); randomNumberGenerator = new MersenneTwister(seed); }; /** * Generates a random floating point number in the range of [0.0, 1.0) * using a Mersenne twister. * * @returns {Number} A random number in the range of [0.0, 1.0). * * @see CesiumMath.setRandomNumberSeed * @see {@link http://en.wikipedia.org/wiki/Mersenne_twister|Mersenne twister on Wikipedia} */ CesiumMath.nextRandomNumber = function () { return randomNumberGenerator.random(); }; /** * Generates a random number between two numbers. * * @param {Number} min The minimum value. * @param {Number} max The maximum value. * @returns {Number} A random number between the min and max. */ CesiumMath.randomBetween = function (min, max) { return CesiumMath.nextRandomNumber() * (max - min) + min; }; /** * Computes Math.acos(value), but first clamps value to the range [-1.0, 1.0] * so that the function will never return NaN. * * @param {Number} value The value for which to compute acos. * @returns {Number} The acos of the value if the value is in the range [-1.0, 1.0], or the acos of -1.0 or 1.0, * whichever is closer, if the value is outside the range. */ CesiumMath.acosClamped = function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); return Math.acos(CesiumMath.clamp(value, -1.0, 1.0)); }; /** * Computes Math.asin(value), but first clamps value to the range [-1.0, 1.0] * so that the function will never return NaN. * * @param {Number} value The value for which to compute asin. * @returns {Number} The asin of the value if the value is in the range [-1.0, 1.0], or the asin of -1.0 or 1.0, * whichever is closer, if the value is outside the range. */ CesiumMath.asinClamped = function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); return Math.asin(CesiumMath.clamp(value, -1.0, 1.0)); }; /** * Finds the chord length between two points given the circle's radius and the angle between the points. * * @param {Number} angle The angle between the two points. * @param {Number} radius The radius of the circle. * @returns {Number} The chord length. */ CesiumMath.chordLength = function (angle, radius) { //>>includeStart('debug', pragmas.debug); if (!defined(angle)) { throw new DeveloperError("angle is required."); } if (!defined(radius)) { throw new DeveloperError("radius is required."); } //>>includeEnd('debug'); return 2.0 * radius * Math.sin(angle * 0.5); }; /** * Finds the logarithm of a number to a base. * * @param {Number} number The number. * @param {Number} base The base. * @returns {Number} The result. */ CesiumMath.logBase = function (number, base) { //>>includeStart('debug', pragmas.debug); if (!defined(number)) { throw new DeveloperError("number is required."); } if (!defined(base)) { throw new DeveloperError("base is required."); } //>>includeEnd('debug'); return Math.log(number) / Math.log(base); }; /** * Finds the cube root of a number. * Returns NaN if number is not provided. * * @function * @param {Number} [number] The number. * @returns {Number} The result. */ // eslint-disable-next-line es/no-math-cbrt CesiumMath.cbrt = defaultValue(Math.cbrt, function cbrt(number) { var result = Math.pow(Math.abs(number), 1.0 / 3.0); return number < 0.0 ? -result : result; }); /** * Finds the base 2 logarithm of a number. * * @function * @param {Number} number The number. * @returns {Number} The result. */ // eslint-disable-next-line es/no-math-log2 CesiumMath.log2 = defaultValue(Math.log2, function log2(number) { return Math.log(number) * Math.LOG2E; }); /** * @private */ CesiumMath.fog = function (distanceToCamera, density) { var scalar = distanceToCamera * density; return 1.0 - Math.exp(-(scalar * scalar)); }; /** * Computes a fast approximation of Atan for input in the range [-1, 1]. * * Based on Michal Drobot's approximation from ShaderFastLibs, * which in turn is based on "Efficient approximations for the arctangent function," * Rajan, S. Sichun Wang Inkol, R. Joyal, A., May 2006. * Adapted from ShaderFastLibs under MIT License. * * @param {Number} x An input number in the range [-1, 1] * @returns {Number} An approximation of atan(x) */ CesiumMath.fastApproximateAtan = function (x) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("x", x); //>>includeEnd('debug'); return x * (-0.1784 * Math.abs(x) - 0.0663 * x * x + 1.0301); }; /** * Computes a fast approximation of Atan2(x, y) for arbitrary input scalars. * * Range reduction math based on nvidia's cg reference implementation: http://developer.download.nvidia.com/cg/atan2.html * * @param {Number} x An input number that isn't zero if y is zero. * @param {Number} y An input number that isn't zero if x is zero. * @returns {Number} An approximation of atan2(x, y) */ CesiumMath.fastApproximateAtan2 = function (x, y) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("x", x); Check.typeOf.number("y", y); //>>includeEnd('debug'); // atan approximations are usually only reliable over [-1, 1] // So reduce the range by flipping whether x or y is on top based on which is bigger. var opposite; var adjacent; var t = Math.abs(x); // t used as swap and atan result. opposite = Math.abs(y); adjacent = Math.max(t, opposite); opposite = Math.min(t, opposite); var oppositeOverAdjacent = opposite / adjacent; //>>includeStart('debug', pragmas.debug); if (isNaN(oppositeOverAdjacent)) { throw new DeveloperError("either x or y must be nonzero"); } //>>includeEnd('debug'); t = CesiumMath.fastApproximateAtan(oppositeOverAdjacent); // Undo range reduction t = Math.abs(y) > Math.abs(x) ? CesiumMath.PI_OVER_TWO - t : t; t = x < 0.0 ? CesiumMath.PI - t : t; t = y < 0.0 ? -t : t; return t; }; /** * A 2D Cartesian point. * @alias Cartesian2 * @constructor * * @param {Number} [x=0.0] The X component. * @param {Number} [y=0.0] The Y component. * * @see Cartesian3 * @see Cartesian4 * @see Packable */ function Cartesian2(x, y) { /** * 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); } /** * Creates a Cartesian2 instance from x and y coordinates. * * @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. * * @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. * @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. */ 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. */ 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. * * @param {Cartesian2} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Cartesian2.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.x; array[startingIndex] = value.y; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} 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. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. */ Cartesian2.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Cartesian2(); } result.x = array[startingIndex++]; result.y = array[startingIndex]; return result; }; /** * Flattens an array of Cartesian2s into and array of components. * * @param {Cartesian2[]} array The array of cartesians to pack. * @param {Number[]} [result] The array onto which to store the result. If this is a typed array, it must have array.length * 2 components, else a {@link DeveloperError} will be thrown. If it is a regular array, it will be resized to have (array.length * 2) elements. * @returns {Number[]} The packed array. */ Cartesian2.packArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); var length = array.length; var resultLength = length * 2; if (!defined(result)) { result = new Array(resultLength); } else if (!Array.isArray(result) && result.length !== resultLength) { throw new DeveloperError( "If result is a typed array, it must have exactly array.length * 2 elements" ); } else if (result.length !== resultLength) { result.length = resultLength; } for (var i = 0; i < length; ++i) { Cartesian2.pack(array[i], result, i * 2); } return result; }; /** * Unpacks an array of cartesian components into and array of Cartesian2s. * * @param {Number[]} array The array of components to unpack. * @param {Cartesian2[]} [result] The array onto which to store the result. * @returns {Cartesian2[]} The unpacked array. */ Cartesian2.unpackArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); Check.typeOf.number.greaterThanOrEquals("array.length", array.length, 2); if (array.length % 2 !== 0) { throw new DeveloperError("array length must be a multiple of 2."); } //>>includeEnd('debug'); var length = array.length; if (!defined(result)) { result = new Array(length / 2); } else { result.length = length / 2; } for (var i = 0; i < length; i += 2) { var index = i / 2; result[index] = Cartesian2.unpack(array, i, result[index]); } return result; }; /** * Creates a Cartesian2 from two consecutive elements in an array. * @function * * @param {Number[]} 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. * * @example * // Create a Cartesian2 with (1.0, 2.0) * var v = [1.0, 2.0]; * var p = Cesium.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 = Cesium.Cartesian2.fromArray(v2, 2); */ Cartesian2.fromArray = Cartesian2.unpack; /** * Computes the value of the maximum component for the supplied Cartesian. * * @param {Cartesian2} cartesian The cartesian to use. * @returns {Number} The value of the maximum component. */ Cartesian2.maximumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return Math.max(cartesian.x, cartesian.y); }; /** * Computes the value of the minimum component for the supplied Cartesian. * * @param {Cartesian2} cartesian The cartesian to use. * @returns {Number} The value of the minimum component. */ Cartesian2.minimumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return Math.min(cartesian.x, cartesian.y); }; /** * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians. * * @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. */ Cartesian2.minimumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian2.maximumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Cartesian2} cartesian The Cartesian instance whose squared magnitude is to be computed. * @returns {Number} The squared magnitude. */ Cartesian2.magnitudeSquared = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return cartesian.x * cartesian.x + cartesian.y * cartesian.y; }; /** * Computes the Cartesian's magnitude (length). * * @param {Cartesian2} cartesian The Cartesian instance whose magnitude is to be computed. * @returns {Number} The magnitude. */ Cartesian2.magnitude = function (cartesian) { return Math.sqrt(Cartesian2.magnitudeSquared(cartesian)); }; var distanceScratch$3 = new Cartesian2(); /** * Computes the distance between two points. * * @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. * * @example * // Returns 1.0 * var d = Cesium.Cartesian2.distance(new Cesium.Cartesian2(1.0, 0.0), new Cesium.Cartesian2(2.0, 0.0)); */ Cartesian2.distance = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian2.subtract(left, right, distanceScratch$3); return Cartesian2.magnitude(distanceScratch$3); }; /** * Computes the squared distance between two points. Comparing squared distances * using this function is more efficient than comparing distances using {@link Cartesian2#distance}. * * @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. * * @example * // Returns 4.0, not 2.0 * var d = Cesium.Cartesian2.distance(new Cesium.Cartesian2(1.0, 0.0), new Cesium.Cartesian2(3.0, 0.0)); */ Cartesian2.distanceSquared = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian2.subtract(left, right, distanceScratch$3); return Cartesian2.magnitudeSquared(distanceScratch$3); }; /** * Computes the normalized form of the supplied Cartesian. * * @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. */ Cartesian2.normalize = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var magnitude = Cartesian2.magnitude(cartesian); result.x = cartesian.x / magnitude; result.y = cartesian.y / magnitude; //>>includeStart('debug', pragmas.debug); if (isNaN(result.x) || isNaN(result.y)) { throw new DeveloperError("normalized result is not a number"); } //>>includeEnd('debug'); return result; }; /** * Computes the dot (scalar) product of two Cartesians. * * @param {Cartesian2} left The first Cartesian. * @param {Cartesian2} right The second Cartesian. * @returns {Number} The dot product. */ Cartesian2.dot = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return left.x * right.x + left.y * right.y; }; /** * Computes the magnitude of the cross product that would result from implicitly setting the Z coordinate of the input vectors to 0 * * @param {Cartesian2} left The first Cartesian. * @param {Cartesian2} right The second Cartesian. * @returns {Number} The cross product. */ Cartesian2.cross = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return left.x * right.y - left.y * right.x; }; /** * Computes the componentwise product of two Cartesians. * * @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. */ Cartesian2.multiplyComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = left.x * right.x; result.y = left.y * right.y; return result; }; /** * Computes the componentwise quotient of two Cartesians. * * @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. */ Cartesian2.divideComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = left.x / right.x; result.y = left.y / right.y; return result; }; /** * Computes the componentwise sum of two Cartesians. * * @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. */ Cartesian2.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = left.x + right.x; result.y = left.y + right.y; return result; }; /** * Computes the componentwise difference of two Cartesians. * * @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. */ Cartesian2.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = left.x - right.x; result.y = left.y - right.y; return result; }; /** * Multiplies the provided Cartesian componentwise by the provided scalar. * * @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. */ Cartesian2.multiplyByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = cartesian.x * scalar; result.y = cartesian.y * scalar; return result; }; /** * Divides the provided Cartesian componentwise by the provided scalar. * * @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. */ Cartesian2.divideByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = cartesian.x / scalar; result.y = cartesian.y / scalar; return result; }; /** * Negates the provided Cartesian. * * @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. */ Cartesian2.negate = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = -cartesian.x; result.y = -cartesian.y; return result; }; /** * Computes the absolute value of the provided Cartesian. * * @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. */ Cartesian2.abs = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = Math.abs(cartesian.x); result.y = Math.abs(cartesian.y); return result; }; var lerpScratch$3 = new Cartesian2(); /** * Computes the linear interpolation or extrapolation at t using the provided cartesians. * * @param {Cartesian2} start The value corresponding to t at 0.0. * @param {Cartesian2} end The value corresponding to t at 1.0. * @param {Number} 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. */ Cartesian2.lerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); Cartesian2.multiplyByScalar(end, t, lerpScratch$3); result = Cartesian2.multiplyByScalar(start, 1.0 - t, result); return Cartesian2.add(lerpScratch$3, result, result); }; var angleBetweenScratch$1 = new Cartesian2(); var angleBetweenScratch2$1 = new Cartesian2(); /** * Returns the angle, in radians, between the provided Cartesians. * * @param {Cartesian2} left The first Cartesian. * @param {Cartesian2} right The second Cartesian. * @returns {Number} The angle between the Cartesians. */ Cartesian2.angleBetween = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian2.normalize(left, angleBetweenScratch$1); Cartesian2.normalize(right, angleBetweenScratch2$1); return CesiumMath.acosClamped( Cartesian2.dot(angleBetweenScratch$1, angleBetweenScratch2$1) ); }; var mostOrthogonalAxisScratch$2 = new Cartesian2(); /** * Returns the axis that is most orthogonal to the provided Cartesian. * * @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. */ Cartesian2.mostOrthogonalAxis = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var f = Cartesian2.normalize(cartesian, mostOrthogonalAxisScratch$2); 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 * true if they are equal, false otherwise. * * @param {Cartesian2} [left] The first Cartesian. * @param {Cartesian2} [right] The second Cartesian. * @returns {Boolean} true if left and right are equal, false otherwise. */ Cartesian2.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.x === right.x && left.y === right.y) ); }; /** * @private */ Cartesian2.equalsArray = function (cartesian, array, offset) { return cartesian.x === array[offset] && cartesian.y === array[offset + 1]; }; /** * Compares the provided Cartesians componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian2} [left] The first Cartesian. * @param {Cartesian2} [right] The second Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Cartesian2.equalsEpsilon = function ( left, right, relativeEpsilon, absoluteEpsilon ) { return ( left === right || (defined(left) && defined(right) && CesiumMath.equalsEpsilon( left.x, right.x, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.y, right.y, relativeEpsilon, absoluteEpsilon )) ); }; /** * An immutable Cartesian2 instance initialized to (0.0, 0.0). * * @type {Cartesian2} * @constant */ Cartesian2.ZERO = Object.freeze(new Cartesian2(0.0, 0.0)); /** * An immutable Cartesian2 instance initialized to (1.0, 0.0). * * @type {Cartesian2} * @constant */ Cartesian2.UNIT_X = Object.freeze(new Cartesian2(1.0, 0.0)); /** * An immutable Cartesian2 instance initialized to (0.0, 1.0). * * @type {Cartesian2} * @constant */ Cartesian2.UNIT_Y = Object.freeze(new Cartesian2(0.0, 1.0)); /** * Duplicates this Cartesian2 instance. * * @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 * true if they are equal, false otherwise. * * @param {Cartesian2} [right] The right hand side Cartesian. * @returns {Boolean} true if they are equal, false otherwise. */ Cartesian2.prototype.equals = function (right) { return Cartesian2.equals(this, right); }; /** * Compares this Cartesian against the provided Cartesian componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian2} [right] The right hand side Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ Cartesian2.prototype.equalsEpsilon = function ( right, relativeEpsilon, absoluteEpsilon ) { return Cartesian2.equalsEpsilon( this, right, relativeEpsilon, absoluteEpsilon ); }; /** * Creates a string representing this Cartesian in the format '(x, y)'. * * @returns {String} A string representing the provided Cartesian in the format '(x, y)'. */ Cartesian2.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; }; /** * 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 */ function Cartesian3(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. * * @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. */ Cartesian3.fromSpherical = function (spherical, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("spherical", spherical); //>>includeEnd('debug'); 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. * * @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. * * @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. */ 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. * * @param {Cartesian3} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Cartesian3.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.x; array[startingIndex++] = value.y; array[startingIndex] = value.z; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} 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. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. */ Cartesian3.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Cartesian3(); } result.x = array[startingIndex++]; result.y = array[startingIndex++]; result.z = array[startingIndex]; return result; }; /** * Flattens an array of Cartesian3s into an array of components. * * @param {Cartesian3[]} array The array of cartesians to pack. * @param {Number[]} [result] The array onto which to store the result. If this is a typed array, it must have array.length * 3 components, else a {@link DeveloperError} will be thrown. If it is a regular array, it will be resized to have (array.length * 3) elements. * @returns {Number[]} The packed array. */ Cartesian3.packArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); var length = array.length; var resultLength = length * 3; if (!defined(result)) { result = new Array(resultLength); } else if (!Array.isArray(result) && result.length !== resultLength) { throw new DeveloperError( "If result is a typed array, it must have exactly array.length * 3 elements" ); } else if (result.length !== resultLength) { result.length = resultLength; } for (var i = 0; i < length; ++i) { Cartesian3.pack(array[i], result, i * 3); } return result; }; /** * Unpacks an array of cartesian components into an array of Cartesian3s. * * @param {Number[]} array The array of components to unpack. * @param {Cartesian3[]} [result] The array onto which to store the result. * @returns {Cartesian3[]} The unpacked array. */ Cartesian3.unpackArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); Check.typeOf.number.greaterThanOrEquals("array.length", array.length, 3); if (array.length % 3 !== 0) { throw new DeveloperError("array length must be a multiple of 3."); } //>>includeEnd('debug'); var length = array.length; if (!defined(result)) { result = new Array(length / 3); } else { result.length = length / 3; } for (var i = 0; i < length; i += 3) { var index = i / 3; result[index] = Cartesian3.unpack(array, i, result[index]); } return result; }; /** * Creates a Cartesian3 from three consecutive elements in an array. * @function * * @param {Number[]} 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. * * @example * // Create a Cartesian3 with (1.0, 2.0, 3.0) * var v = [1.0, 2.0, 3.0]; * var p = Cesium.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 = Cesium.Cartesian3.fromArray(v2, 2); */ Cartesian3.fromArray = Cartesian3.unpack; /** * Computes the value of the maximum component for the supplied Cartesian. * * @param {Cartesian3} cartesian The cartesian to use. * @returns {Number} The value of the maximum component. */ Cartesian3.maximumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return Math.max(cartesian.x, cartesian.y, cartesian.z); }; /** * Computes the value of the minimum component for the supplied Cartesian. * * @param {Cartesian3} cartesian The cartesian to use. * @returns {Number} The value of the minimum component. */ Cartesian3.minimumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.minimumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.maximumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Cartesian3} cartesian The Cartesian instance whose squared magnitude is to be computed. * @returns {Number} The squared magnitude. */ Cartesian3.magnitudeSquared = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return ( cartesian.x * cartesian.x + cartesian.y * cartesian.y + cartesian.z * cartesian.z ); }; /** * Computes the Cartesian's magnitude (length). * * @param {Cartesian3} cartesian The Cartesian instance whose magnitude is to be computed. * @returns {Number} The magnitude. */ Cartesian3.magnitude = function (cartesian) { return Math.sqrt(Cartesian3.magnitudeSquared(cartesian)); }; var distanceScratch$2 = new Cartesian3(); /** * Computes the distance between two points. * * @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. * * @example * // Returns 1.0 * var d = Cesium.Cartesian3.distance(new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(2.0, 0.0, 0.0)); */ Cartesian3.distance = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian3.subtract(left, right, distanceScratch$2); return Cartesian3.magnitude(distanceScratch$2); }; /** * Computes the squared distance between two points. Comparing squared distances * using this function is more efficient than comparing distances using {@link Cartesian3#distance}. * * @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. * * @example * // Returns 4.0, not 2.0 * var d = Cesium.Cartesian3.distanceSquared(new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(3.0, 0.0, 0.0)); */ Cartesian3.distanceSquared = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian3.subtract(left, right, distanceScratch$2); return Cartesian3.magnitudeSquared(distanceScratch$2); }; /** * Computes the normalized form of the supplied Cartesian. * * @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. */ Cartesian3.normalize = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var magnitude = Cartesian3.magnitude(cartesian); result.x = cartesian.x / magnitude; result.y = cartesian.y / magnitude; result.z = cartesian.z / magnitude; //>>includeStart('debug', pragmas.debug); if (isNaN(result.x) || isNaN(result.y) || isNaN(result.z)) { throw new DeveloperError("normalized result is not a number"); } //>>includeEnd('debug'); return result; }; /** * Computes the dot (scalar) product of two Cartesians. * * @param {Cartesian3} left The first Cartesian. * @param {Cartesian3} right The second Cartesian. * @returns {Number} The dot product. */ Cartesian3.dot = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return left.x * right.x + left.y * right.y + left.z * right.z; }; /** * Computes the componentwise product of two Cartesians. * * @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. */ Cartesian3.multiplyComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = left.x * right.x; result.y = left.y * right.y; result.z = left.z * right.z; return result; }; /** * Computes the componentwise quotient of two Cartesians. * * @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. */ Cartesian3.divideComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.multiplyByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian3.divideByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = cartesian.x / scalar; result.y = cartesian.y / scalar; result.z = cartesian.z / scalar; return result; }; /** * Negates the provided Cartesian. * * @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. */ Cartesian3.negate = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = -cartesian.x; result.y = -cartesian.y; result.z = -cartesian.z; return result; }; /** * Computes the absolute value of the provided Cartesian. * * @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. */ Cartesian3.abs = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = Math.abs(cartesian.x); result.y = Math.abs(cartesian.y); result.z = Math.abs(cartesian.z); return result; }; var lerpScratch$2 = new Cartesian3(); /** * Computes the linear interpolation or extrapolation at t using the provided cartesians. * * @param {Cartesian3} start The value corresponding to t at 0.0. * @param {Cartesian3} end The value corresponding to t at 1.0. * @param {Number} 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. */ Cartesian3.lerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); Cartesian3.multiplyByScalar(end, t, lerpScratch$2); result = Cartesian3.multiplyByScalar(start, 1.0 - t, result); return Cartesian3.add(lerpScratch$2, result, result); }; var angleBetweenScratch = new Cartesian3(); var angleBetweenScratch2 = new Cartesian3(); /** * Returns the angle, in radians, between the provided Cartesians. * * @param {Cartesian3} left The first Cartesian. * @param {Cartesian3} right The second Cartesian. * @returns {Number} The angle between the Cartesians. */ Cartesian3.angleBetween = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); 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$1 = new Cartesian3(); /** * Returns the axis that is most orthogonal to the provided Cartesian. * * @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. */ Cartesian3.mostOrthogonalAxis = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var f = Cartesian3.normalize(cartesian, mostOrthogonalAxisScratch$1); 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; }; /** * Projects vector a onto vector b * @param {Cartesian3} a The vector that needs projecting * @param {Cartesian3} b The vector to project onto * @param {Cartesian3} result The result cartesian * @returns {Cartesian3} The modified result parameter */ Cartesian3.projectVector = function (a, b, result) { //>>includeStart('debug', pragmas.debug); Check.defined("a", a); Check.defined("b", b); Check.defined("result", result); //>>includeEnd('debug'); var scalar = Cartesian3.dot(a, b) / Cartesian3.dot(b, b); return Cartesian3.multiplyByScalar(b, scalar, result); }; /** * Compares the provided Cartesians componentwise and returns * true if they are equal, false otherwise. * * @param {Cartesian3} [left] The first Cartesian. * @param {Cartesian3} [right] The second Cartesian. * @returns {Boolean} true if left and right are equal, false 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) ); }; /** * @private */ Cartesian3.equalsArray = function (cartesian, array, offset) { return ( cartesian.x === array[offset] && cartesian.y === array[offset + 1] && cartesian.z === array[offset + 2] ); }; /** * Compares the provided Cartesians componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian3} [left] The first Cartesian. * @param {Cartesian3} [right] The second Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Cartesian3.equalsEpsilon = function ( left, right, relativeEpsilon, absoluteEpsilon ) { return ( left === right || (defined(left) && defined(right) && CesiumMath.equalsEpsilon( left.x, right.x, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.y, right.y, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.z, right.z, relativeEpsilon, absoluteEpsilon )) ); }; /** * Computes the cross (outer) product of two Cartesians. * * @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. */ Cartesian3.cross = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; result.x = x; result.y = y; result.z = z; return result; }; /** * Computes the midpoint between the right and left Cartesian. * @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 midpoint. */ Cartesian3.midpoint = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = (left.x + right.x) * 0.5; result.y = (left.y + right.y) * 0.5; result.z = (left.z + right.z) * 0.5; return result; }; /** * Returns a Cartesian3 position from longitude and latitude values given in degrees. * * @param {Number} longitude The longitude, in degrees * @param {Number} latitude The latitude, in degrees * @param {Number} [height=0.0] The height, in meters, above the ellipsoid. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The position * * @example * var position = Cesium.Cartesian3.fromDegrees(-115.0, 37.0); */ Cartesian3.fromDegrees = function ( longitude, latitude, height, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("longitude", longitude); Check.typeOf.number("latitude", latitude); //>>includeEnd('debug'); longitude = CesiumMath.toRadians(longitude); latitude = CesiumMath.toRadians(latitude); return Cartesian3.fromRadians(longitude, latitude, height, ellipsoid, result); }; var scratchN = new Cartesian3(); var scratchK = new Cartesian3(); var wgs84RadiiSquared = new Cartesian3( 6378137.0 * 6378137.0, 6378137.0 * 6378137.0, 6356752.3142451793 * 6356752.3142451793 ); /** * Returns a Cartesian3 position from longitude and latitude values given in radians. * * @param {Number} longitude The longitude, in radians * @param {Number} latitude The latitude, in radians * @param {Number} [height=0.0] The height, in meters, above the ellipsoid. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The position * * @example * var position = Cesium.Cartesian3.fromRadians(-2.007, 0.645); */ Cartesian3.fromRadians = function ( longitude, latitude, height, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("longitude", longitude); Check.typeOf.number("latitude", latitude); //>>includeEnd('debug'); height = defaultValue(height, 0.0); var radiiSquared = defined(ellipsoid) ? ellipsoid.radiiSquared : wgs84RadiiSquared; var cosLatitude = Math.cos(latitude); scratchN.x = cosLatitude * Math.cos(longitude); scratchN.y = cosLatitude * Math.sin(longitude); scratchN.z = Math.sin(latitude); scratchN = Cartesian3.normalize(scratchN, scratchN); Cartesian3.multiplyComponents(radiiSquared, scratchN, scratchK); var gamma = Math.sqrt(Cartesian3.dot(scratchN, scratchK)); scratchK = Cartesian3.divideByScalar(scratchK, gamma, scratchK); scratchN = Cartesian3.multiplyByScalar(scratchN, height, scratchN); if (!defined(result)) { result = new Cartesian3(); } return Cartesian3.add(scratchK, scratchN, result); }; /** * Returns an array of Cartesian3 positions given an array of longitude and latitude values given in degrees. * * @param {Number[]} coordinates A list of longitude and latitude values. Values alternate [longitude, latitude, longitude, latitude...]. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the coordinates lie. * @param {Cartesian3[]} [result] An array of Cartesian3 objects to store the result. * @returns {Cartesian3[]} The array of positions. * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([-115.0, 37.0, -107.0, 33.0]); */ Cartesian3.fromDegreesArray = function (coordinates, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("coordinates", coordinates); if (coordinates.length < 2 || coordinates.length % 2 !== 0) { throw new DeveloperError( "the number of coordinates must be a multiple of 2 and at least 2" ); } //>>includeEnd('debug'); var length = coordinates.length; if (!defined(result)) { result = new Array(length / 2); } else { result.length = length / 2; } for (var i = 0; i < length; i += 2) { var longitude = coordinates[i]; var latitude = coordinates[i + 1]; var index = i / 2; result[index] = Cartesian3.fromDegrees( longitude, latitude, 0, ellipsoid, result[index] ); } return result; }; /** * Returns an array of Cartesian3 positions given an array of longitude and latitude values given in radians. * * @param {Number[]} coordinates A list of longitude and latitude values. Values alternate [longitude, latitude, longitude, latitude...]. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the coordinates lie. * @param {Cartesian3[]} [result] An array of Cartesian3 objects to store the result. * @returns {Cartesian3[]} The array of positions. * * @example * var positions = Cesium.Cartesian3.fromRadiansArray([-2.007, 0.645, -1.867, .575]); */ Cartesian3.fromRadiansArray = function (coordinates, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("coordinates", coordinates); if (coordinates.length < 2 || coordinates.length % 2 !== 0) { throw new DeveloperError( "the number of coordinates must be a multiple of 2 and at least 2" ); } //>>includeEnd('debug'); var length = coordinates.length; if (!defined(result)) { result = new Array(length / 2); } else { result.length = length / 2; } for (var i = 0; i < length; i += 2) { var longitude = coordinates[i]; var latitude = coordinates[i + 1]; var index = i / 2; result[index] = Cartesian3.fromRadians( longitude, latitude, 0, ellipsoid, result[index] ); } return result; }; /** * Returns an array of Cartesian3 positions given an array of longitude, latitude and height values where longitude and latitude are given in degrees. * * @param {Number[]} coordinates A list of longitude, latitude and height values. Values alternate [longitude, latitude, height, longitude, latitude, height...]. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @param {Cartesian3[]} [result] An array of Cartesian3 objects to store the result. * @returns {Cartesian3[]} The array of positions. * * @example * var positions = Cesium.Cartesian3.fromDegreesArrayHeights([-115.0, 37.0, 100000.0, -107.0, 33.0, 150000.0]); */ Cartesian3.fromDegreesArrayHeights = function (coordinates, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("coordinates", coordinates); if (coordinates.length < 3 || coordinates.length % 3 !== 0) { throw new DeveloperError( "the number of coordinates must be a multiple of 3 and at least 3" ); } //>>includeEnd('debug'); var length = coordinates.length; if (!defined(result)) { result = new Array(length / 3); } else { result.length = length / 3; } for (var i = 0; i < length; i += 3) { var longitude = coordinates[i]; var latitude = coordinates[i + 1]; var height = coordinates[i + 2]; var index = i / 3; result[index] = Cartesian3.fromDegrees( longitude, latitude, height, ellipsoid, result[index] ); } return result; }; /** * Returns an array of Cartesian3 positions given an array of longitude, latitude and height values where longitude and latitude are given in radians. * * @param {Number[]} coordinates A list of longitude, latitude and height values. Values alternate [longitude, latitude, height, longitude, latitude, height...]. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @param {Cartesian3[]} [result] An array of Cartesian3 objects to store the result. * @returns {Cartesian3[]} The array of positions. * * @example * var positions = Cesium.Cartesian3.fromRadiansArrayHeights([-2.007, 0.645, 100000.0, -1.867, .575, 150000.0]); */ Cartesian3.fromRadiansArrayHeights = function (coordinates, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("coordinates", coordinates); if (coordinates.length < 3 || coordinates.length % 3 !== 0) { throw new DeveloperError( "the number of coordinates must be a multiple of 3 and at least 3" ); } //>>includeEnd('debug'); var length = coordinates.length; if (!defined(result)) { result = new Array(length / 3); } else { result.length = length / 3; } for (var i = 0; i < length; i += 3) { var longitude = coordinates[i]; var latitude = coordinates[i + 1]; var height = coordinates[i + 2]; var index = i / 3; result[index] = Cartesian3.fromRadians( longitude, latitude, height, ellipsoid, result[index] ); } return result; }; /** * An immutable Cartesian3 instance initialized to (0.0, 0.0, 0.0). * * @type {Cartesian3} * @constant */ Cartesian3.ZERO = Object.freeze(new Cartesian3(0.0, 0.0, 0.0)); /** * An immutable Cartesian3 instance initialized to (1.0, 0.0, 0.0). * * @type {Cartesian3} * @constant */ Cartesian3.UNIT_X = Object.freeze(new Cartesian3(1.0, 0.0, 0.0)); /** * An immutable Cartesian3 instance initialized to (0.0, 1.0, 0.0). * * @type {Cartesian3} * @constant */ Cartesian3.UNIT_Y = Object.freeze(new Cartesian3(0.0, 1.0, 0.0)); /** * An immutable Cartesian3 instance initialized to (0.0, 0.0, 1.0). * * @type {Cartesian3} * @constant */ Cartesian3.UNIT_Z = Object.freeze(new Cartesian3(0.0, 0.0, 1.0)); /** * Duplicates this Cartesian3 instance. * * @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 * true if they are equal, false otherwise. * * @param {Cartesian3} [right] The right hand side Cartesian. * @returns {Boolean} true if they are equal, false otherwise. */ Cartesian3.prototype.equals = function (right) { return Cartesian3.equals(this, right); }; /** * Compares this Cartesian against the provided Cartesian componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian3} [right] The right hand side Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ Cartesian3.prototype.equalsEpsilon = function ( right, relativeEpsilon, absoluteEpsilon ) { return Cartesian3.equalsEpsilon( this, right, relativeEpsilon, absoluteEpsilon ); }; /** * Creates a string representing this Cartesian in the format '(x, y, z)'. * * @returns {String} A string representing this Cartesian in the format '(x, y, z)'. */ Cartesian3.prototype.toString = function () { return "(" + this.x + ", " + this.y + ", " + this.z + ")"; }; /** * 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 */ function Cartesian4(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. * * @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; }; /** * Creates a Cartesian4 instance from a {@link Color}. red, green, blue, * and alpha map to x, y, z, and w, respectively. * * @param {Color} color The source color. * @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.fromColor = function (color, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("color", color); //>>includeEnd('debug'); if (!defined(result)) { return new Cartesian4(color.red, color.green, color.blue, color.alpha); } result.x = color.red; result.y = color.green; result.z = color.blue; result.w = color.alpha; return result; }; /** * Duplicates a Cartesian4 instance. * * @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. * * @param {Cartesian4} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Cartesian4.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.x; array[startingIndex++] = value.y; array[startingIndex++] = value.z; array[startingIndex] = value.w; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} 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. * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. */ Cartesian4.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); 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; }; /** * Flattens an array of Cartesian4s into and array of components. * * @param {Cartesian4[]} array The array of cartesians to pack. * @param {Number[]} [result] The array onto which to store the result. If this is a typed array, it must have array.length * 4 components, else a {@link DeveloperError} will be thrown. If it is a regular array, it will be resized to have (array.length * 4) elements. * @returns {Number[]} The packed array. */ Cartesian4.packArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); var length = array.length; var resultLength = length * 4; if (!defined(result)) { result = new Array(resultLength); } else if (!Array.isArray(result) && result.length !== resultLength) { throw new DeveloperError( "If result is a typed array, it must have exactly array.length * 4 elements" ); } else if (result.length !== resultLength) { result.length = resultLength; } for (var i = 0; i < length; ++i) { Cartesian4.pack(array[i], result, i * 4); } return result; }; /** * Unpacks an array of cartesian components into and array of Cartesian4s. * * @param {Number[]} array The array of components to unpack. * @param {Cartesian4[]} [result] The array onto which to store the result. * @returns {Cartesian4[]} The unpacked array. */ Cartesian4.unpackArray = function (array, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); Check.typeOf.number.greaterThanOrEquals("array.length", array.length, 4); if (array.length % 4 !== 0) { throw new DeveloperError("array length must be a multiple of 4."); } //>>includeEnd('debug'); var length = array.length; if (!defined(result)) { result = new Array(length / 4); } else { result.length = length / 4; } for (var i = 0; i < length; i += 4) { var index = i / 4; result[index] = Cartesian4.unpack(array, i, result[index]); } return result; }; /** * Creates a Cartesian4 from four consecutive elements in an array. * @function * * @param {Number[]} 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. * * @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 = Cesium.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 = Cesium.Cartesian4.fromArray(v2, 2); */ Cartesian4.fromArray = Cartesian4.unpack; /** * Computes the value of the maximum component for the supplied Cartesian. * * @param {Cartesian4} cartesian The cartesian to use. * @returns {Number} The value of the maximum component. */ Cartesian4.maximumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return Math.max(cartesian.x, cartesian.y, cartesian.z, cartesian.w); }; /** * Computes the value of the minimum component for the supplied Cartesian. * * @param {Cartesian4} cartesian The cartesian to use. * @returns {Number} The value of the minimum component. */ Cartesian4.minimumComponent = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.minimumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.maximumByComponent = function (first, second, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("first", first); Check.typeOf.object("second", second); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Cartesian4} cartesian The Cartesian instance whose squared magnitude is to be computed. * @returns {Number} The squared magnitude. */ Cartesian4.magnitudeSquared = function (cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); return ( cartesian.x * cartesian.x + cartesian.y * cartesian.y + cartesian.z * cartesian.z + cartesian.w * cartesian.w ); }; /** * Computes the Cartesian's magnitude (length). * * @param {Cartesian4} cartesian The Cartesian instance whose magnitude is to be computed. * @returns {Number} The magnitude. */ Cartesian4.magnitude = function (cartesian) { return Math.sqrt(Cartesian4.magnitudeSquared(cartesian)); }; var distanceScratch$1 = new Cartesian4(); /** * Computes the 4-space distance between two points. * * @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. * * @example * // Returns 1.0 * var d = Cesium.Cartesian4.distance( * new Cesium.Cartesian4(1.0, 0.0, 0.0, 0.0), * new Cesium.Cartesian4(2.0, 0.0, 0.0, 0.0)); */ Cartesian4.distance = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian4.subtract(left, right, distanceScratch$1); return Cartesian4.magnitude(distanceScratch$1); }; /** * Computes the squared distance between two points. Comparing squared distances * using this function is more efficient than comparing distances using {@link Cartesian4#distance}. * * @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. * * @example * // Returns 4.0, not 2.0 * var d = Cesium.Cartesian4.distance( * new Cesium.Cartesian4(1.0, 0.0, 0.0, 0.0), * new Cesium.Cartesian4(3.0, 0.0, 0.0, 0.0)); */ Cartesian4.distanceSquared = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); Cartesian4.subtract(left, right, distanceScratch$1); return Cartesian4.magnitudeSquared(distanceScratch$1); }; /** * Computes the normalized form of the supplied Cartesian. * * @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. */ Cartesian4.normalize = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var magnitude = Cartesian4.magnitude(cartesian); result.x = cartesian.x / magnitude; result.y = cartesian.y / magnitude; result.z = cartesian.z / magnitude; result.w = cartesian.w / magnitude; //>>includeStart('debug', pragmas.debug); if ( isNaN(result.x) || isNaN(result.y) || isNaN(result.z) || isNaN(result.w) ) { throw new DeveloperError("normalized result is not a number"); } //>>includeEnd('debug'); return result; }; /** * Computes the dot (scalar) product of two Cartesians. * * @param {Cartesian4} left The first Cartesian. * @param {Cartesian4} right The second Cartesian. * @returns {Number} The dot product. */ Cartesian4.dot = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return ( left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w ); }; /** * Computes the componentwise product of two Cartesians. * * @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. */ Cartesian4.multiplyComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 quotient of two Cartesians. * * @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. */ Cartesian4.divideComponents = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.multiplyByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.divideByScalar = function (cartesian, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.negate = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Cartesian4.abs = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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$1 = new Cartesian4(); /** * Computes the linear interpolation or extrapolation at t using the provided cartesians. * * @param {Cartesian4} start The value corresponding to t at 0.0. * @param {Cartesian4}end The value corresponding to t at 1.0. * @param {Number} 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. */ Cartesian4.lerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); Cartesian4.multiplyByScalar(end, t, lerpScratch$1); result = Cartesian4.multiplyByScalar(start, 1.0 - t, result); return Cartesian4.add(lerpScratch$1, result, result); }; var mostOrthogonalAxisScratch = new Cartesian4(); /** * Returns the axis that is most orthogonal to the provided Cartesian. * * @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. */ Cartesian4.mostOrthogonalAxis = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 * true if they are equal, false otherwise. * * @param {Cartesian4} [left] The first Cartesian. * @param {Cartesian4} [right] The second Cartesian. * @returns {Boolean} true if left and right are equal, false 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) ); }; /** * @private */ Cartesian4.equalsArray = function (cartesian, array, offset) { return ( cartesian.x === array[offset] && cartesian.y === array[offset + 1] && cartesian.z === array[offset + 2] && cartesian.w === array[offset + 3] ); }; /** * Compares the provided Cartesians componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian4} [left] The first Cartesian. * @param {Cartesian4} [right] The second Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Cartesian4.equalsEpsilon = function ( left, right, relativeEpsilon, absoluteEpsilon ) { return ( left === right || (defined(left) && defined(right) && CesiumMath.equalsEpsilon( left.x, right.x, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.y, right.y, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.z, right.z, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.w, right.w, relativeEpsilon, absoluteEpsilon )) ); }; /** * An immutable Cartesian4 instance initialized to (0.0, 0.0, 0.0, 0.0). * * @type {Cartesian4} * @constant */ Cartesian4.ZERO = Object.freeze(new Cartesian4(0.0, 0.0, 0.0, 0.0)); /** * An immutable Cartesian4 instance initialized to (1.0, 0.0, 0.0, 0.0). * * @type {Cartesian4} * @constant */ Cartesian4.UNIT_X = Object.freeze(new Cartesian4(1.0, 0.0, 0.0, 0.0)); /** * An immutable Cartesian4 instance initialized to (0.0, 1.0, 0.0, 0.0). * * @type {Cartesian4} * @constant */ Cartesian4.UNIT_Y = Object.freeze(new Cartesian4(0.0, 1.0, 0.0, 0.0)); /** * An immutable Cartesian4 instance initialized to (0.0, 0.0, 1.0, 0.0). * * @type {Cartesian4} * @constant */ Cartesian4.UNIT_Z = Object.freeze(new Cartesian4(0.0, 0.0, 1.0, 0.0)); /** * An immutable Cartesian4 instance initialized to (0.0, 0.0, 0.0, 1.0). * * @type {Cartesian4} * @constant */ Cartesian4.UNIT_W = Object.freeze(new Cartesian4(0.0, 0.0, 0.0, 1.0)); /** * Duplicates this Cartesian4 instance. * * @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 * true if they are equal, false otherwise. * * @param {Cartesian4} [right] The right hand side Cartesian. * @returns {Boolean} true if they are equal, false otherwise. */ Cartesian4.prototype.equals = function (right) { return Cartesian4.equals(this, right); }; /** * Compares this Cartesian against the provided Cartesian componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Cartesian4} [right] The right hand side Cartesian. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ Cartesian4.prototype.equalsEpsilon = function ( right, relativeEpsilon, absoluteEpsilon ) { return Cartesian4.equalsEpsilon( this, right, relativeEpsilon, absoluteEpsilon ); }; /** * Creates a string representing this Cartesian in the format '(x, y, z, w)'. * * @returns {String} A string representing the provided Cartesian in the format '(x, y, z, w)'. */ Cartesian4.prototype.toString = function () { return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; }; // scratchU8Array and scratchF32Array are views into the same buffer var scratchF32Array = new Float32Array(1); var scratchU8Array = new Uint8Array(scratchF32Array.buffer); var testU32 = new Uint32Array([0x11223344]); var testU8 = new Uint8Array(testU32.buffer); var littleEndian = testU8[0] === 0x44; /** * Packs an arbitrary floating point value to 4 values representable using uint8. * * @param {Number} value A floating point number. * @param {Cartesian4} [result] The Cartesian4 that will contain the packed float. * @returns {Cartesian4} A Cartesian4 representing the float packed to values in x, y, z, and w. */ Cartesian4.packFloat = function (value, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian4(); } // scratchU8Array and scratchF32Array are views into the same buffer scratchF32Array[0] = value; if (littleEndian) { result.x = scratchU8Array[0]; result.y = scratchU8Array[1]; result.z = scratchU8Array[2]; result.w = scratchU8Array[3]; } else { // convert from big-endian to little-endian result.x = scratchU8Array[3]; result.y = scratchU8Array[2]; result.z = scratchU8Array[1]; result.w = scratchU8Array[0]; } return result; }; /** * Unpacks a float packed using Cartesian4.packFloat. * * @param {Cartesian4} packedFloat A Cartesian4 containing a float packed to 4 values representable using uint8. * @returns {Number} The unpacked float. * @private */ Cartesian4.unpackFloat = function (packedFloat) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("packedFloat", packedFloat); //>>includeEnd('debug'); // scratchU8Array and scratchF32Array are views into the same buffer if (littleEndian) { scratchU8Array[0] = packedFloat.x; scratchU8Array[1] = packedFloat.y; scratchU8Array[2] = packedFloat.z; scratchU8Array[3] = packedFloat.w; } else { // convert from little-endian to big-endian scratchU8Array[0] = packedFloat.w; scratchU8Array[1] = packedFloat.z; scratchU8Array[2] = packedFloat.y; scratchU8Array[3] = packedFloat.x; } return scratchF32Array[0]; }; 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. * * @param {Cartesian3} cartesian The Cartesian position to scale. * @param {Cartesian3} oneOverRadii One over radii of the ellipsoid. * @param {Cartesian3} oneOverRadiiSquared One over radii squared of the ellipsoid. * @param {Number} centerToleranceSquared Tolerance for closeness to the center. * @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. * * @function scaleToGeodeticSurface * * @private */ function scaleToGeodeticSurface( cartesian, oneOverRadii, oneOverRadiiSquared, centerToleranceSquared, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(cartesian)) { throw new DeveloperError("cartesian is required."); } if (!defined(oneOverRadii)) { throw new DeveloperError("oneOverRadii is required."); } if (!defined(oneOverRadiiSquared)) { throw new DeveloperError("oneOverRadiiSquared is required."); } if (!defined(centerToleranceSquared)) { throw new DeveloperError("centerToleranceSquared is required."); } //>>includeEnd('debug'); var positionX = cartesian.x; var positionY = cartesian.y; var positionZ = cartesian.z; 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 < centerToleranceSquared) { return !isFinite(ratio) ? undefined : Cartesian3.clone(intersection, result); } 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; } /** * 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 */ function Cartographic(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 radians. * * @param {Number} longitude The longitude, in radians. * @param {Number} latitude The latitude, in radians. * @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.fromRadians = function (longitude, latitude, height, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("longitude", longitude); Check.typeOf.number("latitude", latitude); //>>includeEnd('debug'); 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; }; /** * Creates a new Cartographic instance from longitude and latitude * specified in degrees. The values in the resulting object will * be in radians. * * @param {Number} longitude The longitude, in degrees. * @param {Number} latitude 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("longitude", longitude); Check.typeOf.number("latitude", latitude); //>>includeEnd('debug'); longitude = CesiumMath.toRadians(longitude); latitude = CesiumMath.toRadians(latitude); return Cartographic.fromRadians(longitude, latitude, height, result); }; var cartesianToCartographicN$1 = new Cartesian3(); var cartesianToCartographicP$1 = new Cartesian3(); var cartesianToCartographicH$1 = new Cartesian3(); var wgs84OneOverRadii = new Cartesian3( 1.0 / 6378137.0, 1.0 / 6378137.0, 1.0 / 6356752.3142451793 ); var wgs84OneOverRadiiSquared = new Cartesian3( 1.0 / (6378137.0 * 6378137.0), 1.0 / (6378137.0 * 6378137.0), 1.0 / (6356752.3142451793 * 6356752.3142451793) ); var wgs84CenterToleranceSquared = CesiumMath.EPSILON1; /** * Creates a new Cartographic instance from a Cartesian position. The values in the * resulting object will be in radians. * * @param {Cartesian3} cartesian The Cartesian position to convert to cartographic representation. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @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. */ Cartographic.fromCartesian = function (cartesian, ellipsoid, result) { var oneOverRadii = defined(ellipsoid) ? ellipsoid.oneOverRadii : wgs84OneOverRadii; var oneOverRadiiSquared = defined(ellipsoid) ? ellipsoid.oneOverRadiiSquared : wgs84OneOverRadiiSquared; var centerToleranceSquared = defined(ellipsoid) ? ellipsoid._centerToleranceSquared : wgs84CenterToleranceSquared; //`cartesian is required.` is thrown from scaleToGeodeticSurface var p = scaleToGeodeticSurface( cartesian, oneOverRadii, oneOverRadiiSquared, centerToleranceSquared, cartesianToCartographicP$1 ); if (!defined(p)) { return undefined; } var n = Cartesian3.multiplyComponents( p, oneOverRadiiSquared, cartesianToCartographicN$1 ); n = Cartesian3.normalize(n, n); var h = Cartesian3.subtract(cartesian, p, cartesianToCartographicH$1); 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; }; /** * Creates a new Cartesian3 instance from a Cartographic input. The values in the inputted * object should be in radians. * * @param {Cartographic} cartographic Input to be converted into a Cartesian3 output. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The position */ Cartographic.toCartesian = function (cartographic, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("cartographic", cartographic); //>>includeEnd('debug'); return Cartesian3.fromRadians( cartographic.longitude, cartographic.latitude, cartographic.height, ellipsoid, result ); }; /** * Duplicates a Cartographic instance. * * @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 * true if they are equal, false otherwise. * * @param {Cartographic} [left] The first cartographic. * @param {Cartographic} [right] The second cartographic. * @returns {Boolean} true if left and right are equal, false 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 * true if they are within the provided epsilon, * false otherwise. * * @param {Cartographic} [left] The first cartographic. * @param {Cartographic} [right] The second cartographic. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Cartographic.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0); 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) ); }; /** * An immutable Cartographic instance initialized to (0.0, 0.0, 0.0). * * @type {Cartographic} * @constant */ Cartographic.ZERO = Object.freeze(new Cartographic(0.0, 0.0, 0.0)); /** * Duplicates this instance. * * @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 * true if they are equal, false otherwise. * * @param {Cartographic} [right] The second cartographic. * @returns {Boolean} true if left and right are equal, false otherwise. */ Cartographic.prototype.equals = function (right) { return Cartographic.equals(this, right); }; /** * Compares the provided against this cartographic componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Cartographic} [right] The second cartographic. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Cartographic.prototype.equalsEpsilon = function (right, epsilon) { return Cartographic.equalsEpsilon(this, right, epsilon); }; /** * Creates a string representing this cartographic in the format '(longitude, latitude, height)'. * * @returns {String} A string representing the provided cartographic in the format '(longitude, latitude, height)'. */ Cartographic.prototype.toString = function () { return "(" + this.longitude + ", " + this.latitude + ", " + this.height + ")"; }; /** * Finds an item in a sorted array. * * @function * @param {Array} array The sorted array to search. * @param {*} itemToFind The item to find in the array. * @param {binarySearchComparator} comparator The function to use to compare the item to * elements in the array. * @returns {Number} The index of itemToFind in the array, if it exists. If itemToFind * does not exist, the return value is a negative number which is the bitwise complement (~) * of the index before which the itemToFind should be inserted in order to maintain the * sorted order of the array. * * @example * // Create a comparator function to search through an array of numbers. * function comparator(a, b) { * return a - b; * }; * var numbers = [0, 2, 4, 6, 8]; * var index = Cesium.binarySearch(numbers, 6, comparator); // 3 */ function binarySearch(array, itemToFind, comparator) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); Check.defined("itemToFind", itemToFind); Check.defined("comparator", comparator); //>>includeEnd('debug'); var low = 0; var high = array.length - 1; var i; var comparison; while (low <= high) { i = ~~((low + high) / 2); comparison = comparator(array[i], itemToFind); if (comparison < 0) { low = i + 1; continue; } if (comparison > 0) { high = i - 1; continue; } return i; } return ~(high + 1); } /** * A set of Earth Orientation Parameters (EOP) sampled at a time. * * @alias EarthOrientationParametersSample * @constructor * * @param {Number} xPoleWander The pole wander about the X axis, in radians. * @param {Number} yPoleWander The pole wander about the Y axis, in radians. * @param {Number} xPoleOffset The offset to the Celestial Intermediate Pole (CIP) about the X axis, in radians. * @param {Number} yPoleOffset The offset to the Celestial Intermediate Pole (CIP) about the Y axis, in radians. * @param {Number} ut1MinusUtc The difference in time standards, UT1 - UTC, in seconds. * * @private */ function EarthOrientationParametersSample( xPoleWander, yPoleWander, xPoleOffset, yPoleOffset, ut1MinusUtc ) { /** * The pole wander about the X axis, in radians. * @type {Number} */ this.xPoleWander = xPoleWander; /** * The pole wander about the Y axis, in radians. * @type {Number} */ this.yPoleWander = yPoleWander; /** * The offset to the Celestial Intermediate Pole (CIP) about the X axis, in radians. * @type {Number} */ this.xPoleOffset = xPoleOffset; /** * The offset to the Celestial Intermediate Pole (CIP) about the Y axis, in radians. * @type {Number} */ this.yPoleOffset = yPoleOffset; /** * The difference in time standards, UT1 - UTC, in seconds. * @type {Number} */ this.ut1MinusUtc = ut1MinusUtc; } /** @license sprintf.js from the php.js project - https://github.com/kvz/phpjs Directly from https://github.com/kvz/phpjs/blob/master/functions/strings/sprintf.js php.js is copyright 2012 Kevin van Zonneveld. Portions copyright Brett Zamir (http://brett-zamir.me), Kevin van Zonneveld (http://kevin.vanzonneveld.net), Onno Marsman, Theriault, Michael White (http://getsprink.com), Waldo Malqui Silva, Paulo Freitas, Jack, Jonas Raoni Soares Silva (http://www.jsfromhell.com), Philip Peterson, Legaev Andrey, Ates Goral (http://magnetiq.com), Alex, Ratheous, Martijn Wieringa, Rafa? Kukawski (http://blog.kukawski.pl), lmeyrick (https://sourceforge.net/projects/bcmath-js/), Nate, Philippe Baumann, Enrique Gonzalez, Webtoolkit.info (http://www.webtoolkit.info/), Carlos R. L. Rodrigues (http://www.jsfromhell.com), Ash Searle (http://hexmen.com/blog/), Jani Hartikainen, travc, Ole Vrijenhoek, Erkekjetter, Michael Grier, Rafa? Kukawski (http://kukawski.pl), Johnny Mast (http://www.phpvrouwen.nl), T.Wild, d3x, http://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript, Rafa? Kukawski (http://blog.kukawski.pl/), stag019, pilus, WebDevHobo (http://webdevhobo.blogspot.com/), marrtins, GeekFG (http://geekfg.blogspot.com), Andrea Giammarchi (http://webreflection.blogspot.com), Arpad Ray (mailto:arpad@php.net), gorthaur, Paul Smith, Tim de Koning (http://www.kingsquare.nl), Joris, Oleg Eremeev, Steve Hilder, majak, gettimeofday, KELAN, Josh Fraser (http://onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/), Marc Palau, Martin (http://www.erlenwiese.de/), Breaking Par Consulting Inc (http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256CFB006C45F7), Chris, Mirek Slugen, saulius, Alfonso Jimenez (http://www.alfonsojimenez.com), Diplom@t (http://difane.com/), felix, Mailfaker (http://www.weedem.fr/), Tyler Akins (http://rumkin.com), Caio Ariede (http://caioariede.com), Robin, Kankrelune (http://www.webfaktory.info/), Karol Kowalski, Imgen Tata (http://www.myipdf.com/), mdsjack (http://www.mdsjack.bo.it), Dreamer, Felix Geisendoerfer (http://www.debuggable.com/felix), Lars Fischer, AJ, David, Aman Gupta, Michael White, Public Domain (http://www.json.org/json2.js), Steven Levithan (http://blog.stevenlevithan.com), Sakimori, Pellentesque Malesuada, Thunder.m, Dj (http://phpjs.org/functions/htmlentities:425#comment_134018), Steve Clay, David James, Francois, class_exists, nobbler, T. Wild, Itsacon (http://www.itsacon.net/), date, Ole Vrijenhoek (http://www.nervous.nl/), Fox, Raphael (Ao RUDLER), Marco, noname, Mateusz "loonquawl" Zalega, Frank Forte, Arno, ger, mktime, john (http://www.jd-tech.net), Nick Kolosov (http://sammy.ru), marc andreu, Scott Cariss, Douglas Crockford (http://javascript.crockford.com), madipta, Slawomir Kaniecki, ReverseSyntax, Nathan, Alex Wilson, kenneth, Bayron Guevara, Adam Wallner (http://web2.bitbaro.hu/), paulo kuong, jmweb, Lincoln Ramsay, djmix, Pyerre, Jon Hohle, Thiago Mata (http://thiagomata.blog.com), lmeyrick (https://sourceforge.net/projects/bcmath-js/this.), Linuxworld, duncan, Gilbert, Sanjoy Roy, Shingo, sankai, Oskar Larsson H?gfeldt (http://oskar-lh.name/), Denny Wardhana, 0m3r, Everlasto, Subhasis Deb, josh, jd, Pier Paolo Ramon (http://www.mastersoup.com/), P, merabi, Soren Hansen, Eugene Bulkin (http://doubleaw.com/), Der Simon (http://innerdom.sourceforge.net/), echo is bad, Ozh, XoraX (http://www.xorax.info), EdorFaus, JB, J A R, Marc Jansen, Francesco, LH, Stoyan Kyosev (http://www.svest.org/), nord_ua, omid (http://phpjs.org/functions/380:380#comment_137122), Brad Touesnard, MeEtc (http://yass.meetcweb.com), Peter-Paul Koch (http://www.quirksmode.org/js/beat.html), Olivier Louvignes (http://mg-crea.com/), T0bsn, Tim Wiel, Bryan Elliott, Jalal Berrami, Martin, JT, David Randall, Thomas Beaucourt (http://www.webapp.fr), taith, vlado houba, Pierre-Luc Paour, Kristof Coomans (SCK-CEN Belgian Nucleair Research Centre), Martin Pool, Kirk Strobeck, Rick Waldron, Brant Messenger (http://www.brantmessenger.com/), Devan Penner-Woelk, Saulo Vallory, Wagner B. Soares, Artur Tchernychev, Valentina De Rosa, Jason Wong (http://carrot.org/), Christoph, Daniel Esteban, strftime, Mick@el, rezna, Simon Willison (http://simonwillison.net), Anton Ongson, Gabriel Paderni, Marco van Oort, penutbutterjelly, Philipp Lenssen, Bjorn Roesbeke (http://www.bjornroesbeke.be/), Bug?, Eric Nagel, Tomasz Wesolowski, Evertjan Garretsen, Bobby Drake, Blues (http://tech.bluesmoon.info/), Luke Godfrey, Pul, uestla, Alan C, Ulrich, Rafal Kukawski, Yves Sucaet, sowberry, Norman "zEh" Fuchs, hitwork, Zahlii, johnrembo, Nick Callen, Steven Levithan (stevenlevithan.com), ejsanders, Scott Baker, Brian Tafoya (http://www.premasolutions.com/), Philippe Jausions (http://pear.php.net/user/jausions), Aidan Lister (http://aidanlister.com/), Rob, e-mike, HKM, ChaosNo1, metjay, strcasecmp, strcmp, Taras Bogach, jpfle, Alexander Ermolaev (http://snippets.dzone.com/user/AlexanderErmolaev), DxGx, kilops, Orlando, dptr1988, Le Torbi, James (http://www.james-bell.co.uk/), Pedro Tainha (http://www.pedrotainha.com), James, Arnout Kazemier (http://www.3rd-Eden.com), Chris McMacken, gabriel paderni, Yannoo, FGFEmperor, baris ozdil, Tod Gentille, Greg Frazier, jakes, 3D-GRAF, Allan Jensen (http://www.winternet.no), Howard Yeend, Benjamin Lupton, davook, daniel airton wermann (http://wermann.com.br), Atli T¨®r, Maximusya, Ryan W Tenney (http://ryan.10e.us), Alexander M Beedie, fearphage (http://http/my.opera.com/fearphage/), Nathan Sepulveda, Victor, Matteo, Billy, stensi, Cord, Manish, T.J. Leahy, Riddler (http://www.frontierwebdev.com/), Rafa? Kukawski, FremyCompany, Matt Bradley, Tim de Koning, Luis Salazar (http://www.freaky-media.com/), Diogo Resende, Rival, Andrej Pavlovic, Garagoth, Le Torbi (http://www.letorbi.de/), Dino, Josep Sanz (http://www.ws3.es/), rem, Russell Walker (http://www.nbill.co.uk/), Jamie Beck (http://www.terabit.ca/), setcookie, Michael, YUI Library: http://developer.yahoo.com/yui/docs/YAHOO.util.DateLocale.html, Blues at http://hacks.bluesmoon.info/strftime/strftime.js, Ben (http://benblume.co.uk/), DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html), Andreas, William, meo, incidence, Cagri Ekin, Amirouche, Amir Habibi (http://www.residence-mixte.com/), Luke Smith (http://lucassmith.name), Kheang Hok Chin (http://www.distantia.ca/), Jay Klehr, Lorenzo Pisani, Tony, Yen-Wei Liu, Greenseed, mk.keck, Leslie Hoare, dude, booeyOH, Ben Bryan Licensed under the MIT (MIT-LICENSE.txt) license. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL KEVIN VAN ZONNEVELD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ function sprintf () { // http://kevin.vanzonneveld.net // + original by: Ash Searle (http://hexmen.com/blog/) // + namespaced by: Michael White (http://getsprink.com) // + tweaked by: Jack // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: Paulo Freitas // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: Brett Zamir (http://brett-zamir.me) // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + improved by: Dj // + improved by: Allidylls // * example 1: sprintf("%01.2f", 123.1); // * returns 1: 123.10 // * example 2: sprintf("[%10s]", 'monkey'); // * returns 2: '[ monkey]' // * example 3: sprintf("[%'#10s]", 'monkey'); // * returns 3: '[####monkey]' // * example 4: sprintf("%d", 123456789012345); // * returns 4: '123456789012345' var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g; var a = arguments, i = 0, format = a[i++]; // pad() var pad = function (str, len, chr, leftJustify) { if (!chr) { chr = ' '; } var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); return leftJustify ? str + padding : padding + str; }; // justify() var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) { var diff = minWidth - value.length; if (diff > 0) { if (leftJustify || !zeroPad) { value = pad(value, minWidth, customPadChar, leftJustify); } else { value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); } } return value; }; // formatBaseX() var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) { // Note: casts negative numbers to positive ones var number = value >>> 0; prefix = prefix && number && { '2': '0b', '8': '0', '16': '0x' }[base] || ''; value = prefix + pad(number.toString(base), precision || 0, '0', false); return justify(value, prefix, leftJustify, minWidth, zeroPad); }; // formatString() var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) { if (precision != null) { value = value.slice(0, precision); } return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar); }; // doFormat() var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) { var number; var prefix; var method; var textTransform; var value; if (substring == '%%') { return '%'; } // parse flags var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, customPadChar = ' '; var flagsl = flags.length; for (var j = 0; flags && j < flagsl; j++) { switch (flags.charAt(j)) { case ' ': positivePrefix = ' '; break; case '+': positivePrefix = '+'; break; case '-': leftJustify = true; break; case "'": customPadChar = flags.charAt(j + 1); break; case '0': zeroPad = true; break; case '#': prefixBaseX = true; break; } } // parameters may be null, undefined, empty-string or real valued // we want to ignore null, undefined and empty-string values if (!minWidth) { minWidth = 0; } else if (minWidth == '*') { minWidth = +a[i++]; } else if (minWidth.charAt(0) == '*') { minWidth = +a[minWidth.slice(1, -1)]; } else { minWidth = +minWidth; } // Note: undocumented perl feature: if (minWidth < 0) { minWidth = -minWidth; leftJustify = true; } if (!isFinite(minWidth)) { throw new Error('sprintf: (minimum-)width must be finite'); } if (!precision) { precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined; } else if (precision == '*') { precision = +a[i++]; } else if (precision.charAt(0) == '*') { precision = +a[precision.slice(1, -1)]; } else { precision = +precision; } // grab value using valueIndex if required? value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; switch (type) { case 's': return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar); case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad); case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad); case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad); case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad); case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase(); case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad); case 'i': case 'd': number = +value || 0; number = Math.round(number - number % 1); // Plain Math.round doesn't just truncate prefix = number < 0 ? '-' : positivePrefix; value = prefix + pad(String(Math.abs(number)), precision, '0', false); return justify(value, prefix, leftJustify, minWidth, zeroPad); case 'e': case 'E': case 'f': // Should handle locales (as per setlocale) case 'F': case 'g': case 'G': number = +value; prefix = number < 0 ? '-' : positivePrefix; method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; value = prefix + Math.abs(number)[method](precision); return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform](); default: return substring; } }; return format.replace(regex, doFormat); } /** * Represents a Gregorian date in a more precise format than the JavaScript Date object. * In addition to submillisecond precision, this object can also represent leap seconds. * @alias GregorianDate * @constructor * * @param {Number} [year] The year as a whole number. * @param {Number} [month] The month as a whole number with range [1, 12]. * @param {Number} [day] The day of the month as a whole number starting at 1. * @param {Number} [hour] The hour as a whole number with range [0, 23]. * @param {Number} [minute] The minute of the hour as a whole number with range [0, 59]. * @param {Number} [second] The second of the minute as a whole number with range [0, 60], with 60 representing a leap second. * @param {Number} [millisecond] The millisecond of the second as a floating point number with range [0.0, 1000.0). * @param {Boolean} [isLeapSecond] Whether this time is during a leap second. * * @see JulianDate#toGregorianDate */ function GregorianDate( year, month, day, hour, minute, second, millisecond, isLeapSecond ) { /** * Gets or sets the year as a whole number. * @type {Number} */ this.year = year; /** * Gets or sets the month as a whole number with range [1, 12]. * @type {Number} */ this.month = month; /** * Gets or sets the day of the month as a whole number starting at 1. * @type {Number} */ this.day = day; /** * Gets or sets the hour as a whole number with range [0, 23]. * @type {Number} */ this.hour = hour; /** * Gets or sets the minute of the hour as a whole number with range [0, 59]. * @type {Number} */ this.minute = minute; /** * Gets or sets the second of the minute as a whole number with range [0, 60], with 60 representing a leap second. * @type {Number} */ this.second = second; /** * Gets or sets the millisecond of the second as a floating point number with range [0.0, 1000.0). * @type {Number} */ this.millisecond = millisecond; /** * Gets or sets whether this time is during a leap second. * @type {Boolean} */ this.isLeapSecond = isLeapSecond; } /** * Determines if a given date is a leap year. * * @function isLeapYear * * @param {Number} year The year to be tested. * @returns {Boolean} True if year is a leap year. * * @example * var leapYear = Cesium.isLeapYear(2000); // true */ function isLeapYear(year) { //>>includeStart('debug', pragmas.debug); if (year === null || isNaN(year)) { throw new DeveloperError("year is required and must be a number."); } //>>includeEnd('debug'); return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } /** * Describes a single leap second, which is constructed from a {@link JulianDate} and a * numerical offset representing the number of seconds TAI is ahead of the UTC time standard. * @alias LeapSecond * @constructor * * @param {JulianDate} [date] A Julian date representing the time of the leap second. * @param {Number} [offset] The cumulative number of seconds that TAI is ahead of UTC at the provided date. */ function LeapSecond(date, offset) { /** * Gets or sets the date at which this leap second occurs. * @type {JulianDate} */ this.julianDate = date; /** * Gets or sets the cumulative number of seconds between the UTC and TAI time standards at the time * of this leap second. * @type {Number} */ this.offset = offset; } /** * Constants for time conversions like those done by {@link JulianDate}. * * @namespace TimeConstants * * @see JulianDate * * @private */ var TimeConstants = { /** * The number of seconds in one millisecond: 0.001 * @type {Number} * @constant */ SECONDS_PER_MILLISECOND: 0.001, /** * The number of seconds in one minute: 60. * @type {Number} * @constant */ SECONDS_PER_MINUTE: 60.0, /** * The number of minutes in one hour: 60. * @type {Number} * @constant */ MINUTES_PER_HOUR: 60.0, /** * The number of hours in one day: 24. * @type {Number} * @constant */ HOURS_PER_DAY: 24.0, /** * The number of seconds in one hour: 3600. * @type {Number} * @constant */ SECONDS_PER_HOUR: 3600.0, /** * The number of minutes in one day: 1440. * @type {Number} * @constant */ MINUTES_PER_DAY: 1440.0, /** * The number of seconds in one day, ignoring leap seconds: 86400. * @type {Number} * @constant */ SECONDS_PER_DAY: 86400.0, /** * The number of days in one Julian century: 36525. * @type {Number} * @constant */ DAYS_PER_JULIAN_CENTURY: 36525.0, /** * One trillionth of a second. * @type {Number} * @constant */ PICOSECOND: 0.000000001, /** * The number of days to subtract from a Julian date to determine the * modified Julian date, which gives the number of days since midnight * on November 17, 1858. * @type {Number} * @constant */ MODIFIED_JULIAN_DATE_DIFFERENCE: 2400000.5, }; var TimeConstants$1 = Object.freeze(TimeConstants); /** * Provides the type of time standards which JulianDate can take as input. * * @enum {Number} * * @see JulianDate */ var TimeStandard = { /** * Represents the coordinated Universal Time (UTC) time standard. * * UTC is related to TAI according to the relationship * UTC = TAI - deltaT where deltaT is the number of leap * seconds which have been introduced as of the time in TAI. * * @type {Number} * @constant */ UTC: 0, /** * Represents the International Atomic Time (TAI) time standard. * TAI is the principal time standard to which the other time standards are related. * * @type {Number} * @constant */ TAI: 1, }; var TimeStandard$1 = Object.freeze(TimeStandard); var gregorianDateScratch = new GregorianDate(); var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; var daysInLeapFeburary = 29; function compareLeapSecondDates$1(leapSecond, dateToFind) { return JulianDate.compare(leapSecond.julianDate, dateToFind.julianDate); } // we don't really need a leap second instance, anything with a julianDate property will do var binarySearchScratchLeapSecond = new LeapSecond(); function convertUtcToTai(julianDate) { //Even though julianDate is in UTC, we'll treat it as TAI and //search the leap second table for it. binarySearchScratchLeapSecond.julianDate = julianDate; var leapSeconds = JulianDate.leapSeconds; var index = binarySearch( leapSeconds, binarySearchScratchLeapSecond, compareLeapSecondDates$1 ); if (index < 0) { index = ~index; } if (index >= leapSeconds.length) { index = leapSeconds.length - 1; } var offset = leapSeconds[index].offset; if (index > 0) { //Now we have the index of the closest leap second that comes on or after our UTC time. //However, if the difference between the UTC date being converted and the TAI //defined leap second is greater than the offset, we are off by one and need to use //the previous leap second. var difference = JulianDate.secondsDifference( leapSeconds[index].julianDate, julianDate ); if (difference > offset) { index--; offset = leapSeconds[index].offset; } } JulianDate.addSeconds(julianDate, offset, julianDate); } function convertTaiToUtc(julianDate, result) { binarySearchScratchLeapSecond.julianDate = julianDate; var leapSeconds = JulianDate.leapSeconds; var index = binarySearch( leapSeconds, binarySearchScratchLeapSecond, compareLeapSecondDates$1 ); if (index < 0) { index = ~index; } //All times before our first leap second get the first offset. if (index === 0) { return JulianDate.addSeconds(julianDate, -leapSeconds[0].offset, result); } //All times after our leap second get the last offset. if (index >= leapSeconds.length) { return JulianDate.addSeconds( julianDate, -leapSeconds[index - 1].offset, result ); } //Compute the difference between the found leap second and the time we are converting. var difference = JulianDate.secondsDifference( leapSeconds[index].julianDate, julianDate ); if (difference === 0) { //The date is in our leap second table. return JulianDate.addSeconds( julianDate, -leapSeconds[index].offset, result ); } if (difference <= 1.0) { //The requested date is during the moment of a leap second, then we cannot convert to UTC return undefined; } //The time is in between two leap seconds, index is the leap second after the date //we're converting, so we subtract one to get the correct LeapSecond instance. return JulianDate.addSeconds( julianDate, -leapSeconds[--index].offset, result ); } function setComponents(wholeDays, secondsOfDay, julianDate) { var extraDays = (secondsOfDay / TimeConstants$1.SECONDS_PER_DAY) | 0; wholeDays += extraDays; secondsOfDay -= TimeConstants$1.SECONDS_PER_DAY * extraDays; if (secondsOfDay < 0) { wholeDays--; secondsOfDay += TimeConstants$1.SECONDS_PER_DAY; } julianDate.dayNumber = wholeDays; julianDate.secondsOfDay = secondsOfDay; return julianDate; } function computeJulianDateComponents( year, month, day, hour, minute, second, millisecond ) { // Algorithm from page 604 of the Explanatory Supplement to the // Astronomical Almanac (Seidelmann 1992). var a = ((month - 14) / 12) | 0; var b = year + 4800 + a; var dayNumber = (((1461 * b) / 4) | 0) + (((367 * (month - 2 - 12 * a)) / 12) | 0) - (((3 * (((b + 100) / 100) | 0)) / 4) | 0) + day - 32075; // JulianDates are noon-based hour = hour - 12; if (hour < 0) { hour += 24; } var secondsOfDay = second + (hour * TimeConstants$1.SECONDS_PER_HOUR + minute * TimeConstants$1.SECONDS_PER_MINUTE + millisecond * TimeConstants$1.SECONDS_PER_MILLISECOND); if (secondsOfDay >= 43200.0) { dayNumber -= 1; } return [dayNumber, secondsOfDay]; } //Regular expressions used for ISO8601 date parsing. //YYYY var matchCalendarYear = /^(\d{4})$/; //YYYY-MM (YYYYMM is invalid) var matchCalendarMonth = /^(\d{4})-(\d{2})$/; //YYYY-DDD or YYYYDDD var matchOrdinalDate = /^(\d{4})-?(\d{3})$/; //YYYY-Www or YYYYWww or YYYY-Www-D or YYYYWwwD var matchWeekDate = /^(\d{4})-?W(\d{2})-?(\d{1})?$/; //YYYY-MM-DD or YYYYMMDD var matchCalendarDate = /^(\d{4})-?(\d{2})-?(\d{2})$/; // Match utc offset var utcOffset = /([Z+\-])?(\d{2})?:?(\d{2})?$/; // Match hours HH or HH.xxxxx var matchHours = /^(\d{2})(\.\d+)?/.source + utcOffset.source; // Match hours/minutes HH:MM HHMM.xxxxx var matchHoursMinutes = /^(\d{2}):?(\d{2})(\.\d+)?/.source + utcOffset.source; // Match hours/minutes HH:MM:SS HHMMSS.xxxxx var matchHoursMinutesSeconds = /^(\d{2}):?(\d{2}):?(\d{2})(\.\d+)?/.source + utcOffset.source; var iso8601ErrorMessage = "Invalid ISO 8601 date."; /** * Represents an astronomical Julian date, which is the number of days since noon on January 1, -4712 (4713 BC). * For increased precision, this class stores the whole number part of the date and the seconds * part of the date in separate components. In order to be safe for arithmetic and represent * leap seconds, the date is always stored in the International Atomic Time standard * {@link TimeStandard.TAI}. * @alias JulianDate * @constructor * * @param {Number} [julianDayNumber=0.0] The Julian Day Number representing the number of whole days. Fractional days will also be handled correctly. * @param {Number} [secondsOfDay=0.0] The number of seconds into the current Julian Day Number. Fractional seconds, negative seconds and seconds greater than a day will be handled correctly. * @param {TimeStandard} [timeStandard=TimeStandard.UTC] The time standard in which the first two parameters are defined. */ function JulianDate(julianDayNumber, secondsOfDay, timeStandard) { /** * Gets or sets the number of whole days. * @type {Number} */ this.dayNumber = undefined; /** * Gets or sets the number of seconds into the current day. * @type {Number} */ this.secondsOfDay = undefined; julianDayNumber = defaultValue(julianDayNumber, 0.0); secondsOfDay = defaultValue(secondsOfDay, 0.0); timeStandard = defaultValue(timeStandard, TimeStandard$1.UTC); //If julianDayNumber is fractional, make it an integer and add the number of seconds the fraction represented. var wholeDays = julianDayNumber | 0; secondsOfDay = secondsOfDay + (julianDayNumber - wholeDays) * TimeConstants$1.SECONDS_PER_DAY; setComponents(wholeDays, secondsOfDay, this); if (timeStandard === TimeStandard$1.UTC) { convertUtcToTai(this); } } /** * Creates a new instance from a GregorianDate. * * @param {GregorianDate} date A GregorianDate. * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. * * @exception {DeveloperError} date must be a valid GregorianDate. */ JulianDate.fromGregorianDate = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!(date instanceof GregorianDate)) { throw new DeveloperError("date must be a valid GregorianDate."); } //>>includeEnd('debug'); var components = computeJulianDateComponents( date.year, date.month, date.day, date.hour, date.minute, date.second, date.millisecond ); if (!defined(result)) { return new JulianDate(components[0], components[1], TimeStandard$1.UTC); } setComponents(components[0], components[1], result); convertUtcToTai(result); return result; }; /** * Creates a new instance from a JavaScript Date. * * @param {Date} date A JavaScript Date. * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. * * @exception {DeveloperError} date must be a valid JavaScript Date. */ JulianDate.fromDate = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!(date instanceof Date) || isNaN(date.getTime())) { throw new DeveloperError("date must be a valid JavaScript Date."); } //>>includeEnd('debug'); var components = computeJulianDateComponents( date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds() ); if (!defined(result)) { return new JulianDate(components[0], components[1], TimeStandard$1.UTC); } setComponents(components[0], components[1], result); convertUtcToTai(result); return result; }; /** * Creates a new instance from a from an {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} date. * This method is superior to Date.parse because it will handle all valid formats defined by the ISO 8601 * specification, including leap seconds and sub-millisecond times, which discarded by most JavaScript implementations. * * @param {String} iso8601String An ISO 8601 date. * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. * * @exception {DeveloperError} Invalid ISO 8601 date. */ JulianDate.fromIso8601 = function (iso8601String, result) { //>>includeStart('debug', pragmas.debug); if (typeof iso8601String !== "string") { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug'); //Comma and decimal point both indicate a fractional number according to ISO 8601, //start out by blanket replacing , with . which is the only valid such symbol in JS. iso8601String = iso8601String.replace(",", "."); //Split the string into its date and time components, denoted by a mandatory T var tokens = iso8601String.split("T"); var year; var month = 1; var day = 1; var hour = 0; var minute = 0; var second = 0; var millisecond = 0; //Lacking a time is okay, but a missing date is illegal. var date = tokens[0]; var time = tokens[1]; var tmp; var inLeapYear; //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError(iso8601ErrorMessage); } var dashCount; //>>includeEnd('debug'); //First match the date against possible regular expressions. tokens = date.match(matchCalendarDate); if (tokens !== null) { //>>includeStart('debug', pragmas.debug); dashCount = date.split("-").length - 1; if (dashCount > 0 && dashCount !== 2) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug'); year = +tokens[1]; month = +tokens[2]; day = +tokens[3]; } else { tokens = date.match(matchCalendarMonth); if (tokens !== null) { year = +tokens[1]; month = +tokens[2]; } else { tokens = date.match(matchCalendarYear); if (tokens !== null) { year = +tokens[1]; } else { //Not a year/month/day so it must be an ordinal date. var dayOfYear; tokens = date.match(matchOrdinalDate); if (tokens !== null) { year = +tokens[1]; dayOfYear = +tokens[2]; inLeapYear = isLeapYear(year); //This validation is only applicable for this format. //>>includeStart('debug', pragmas.debug); if ( dayOfYear < 1 || (inLeapYear && dayOfYear > 366) || (!inLeapYear && dayOfYear > 365) ) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug') } else { tokens = date.match(matchWeekDate); if (tokens !== null) { //ISO week date to ordinal date from //http://en.wikipedia.org/w/index.php?title=ISO_week_date&oldid=474176775 year = +tokens[1]; var weekNumber = +tokens[2]; var dayOfWeek = +tokens[3] || 0; //>>includeStart('debug', pragmas.debug); dashCount = date.split("-").length - 1; if ( dashCount > 0 && ((!defined(tokens[3]) && dashCount !== 1) || (defined(tokens[3]) && dashCount !== 2)) ) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug') var january4 = new Date(Date.UTC(year, 0, 4)); dayOfYear = weekNumber * 7 + dayOfWeek - january4.getUTCDay() - 3; } else { //None of our regular expressions succeeded in parsing the date properly. //>>includeStart('debug', pragmas.debug); throw new DeveloperError(iso8601ErrorMessage); //>>includeEnd('debug') } } //Split an ordinal date into month/day. tmp = new Date(Date.UTC(year, 0, 1)); tmp.setUTCDate(dayOfYear); month = tmp.getUTCMonth() + 1; day = tmp.getUTCDate(); } } } //Now that we have all of the date components, validate them to make sure nothing is out of range. inLeapYear = isLeapYear(year); //>>includeStart('debug', pragmas.debug); if ( month < 1 || month > 12 || day < 1 || ((month !== 2 || !inLeapYear) && day > daysInMonth[month - 1]) || (inLeapYear && month === 2 && day > daysInLeapFeburary) ) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug') //Now move onto the time string, which is much simpler. //If no time is specified, it is considered the beginning of the day, UTC to match Javascript's implementation. var offsetIndex; if (defined(time)) { tokens = time.match(matchHoursMinutesSeconds); if (tokens !== null) { //>>includeStart('debug', pragmas.debug); dashCount = time.split(":").length - 1; if (dashCount > 0 && dashCount !== 2 && dashCount !== 3) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug') hour = +tokens[1]; minute = +tokens[2]; second = +tokens[3]; millisecond = +(tokens[4] || 0) * 1000.0; offsetIndex = 5; } else { tokens = time.match(matchHoursMinutes); if (tokens !== null) { //>>includeStart('debug', pragmas.debug); dashCount = time.split(":").length - 1; if (dashCount > 2) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug') hour = +tokens[1]; minute = +tokens[2]; second = +(tokens[3] || 0) * 60.0; offsetIndex = 4; } else { tokens = time.match(matchHours); if (tokens !== null) { hour = +tokens[1]; minute = +(tokens[2] || 0) * 60.0; offsetIndex = 3; } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError(iso8601ErrorMessage); //>>includeEnd('debug') } } } //Validate that all values are in proper range. Minutes and hours have special cases at 60 and 24. //>>includeStart('debug', pragmas.debug); if ( minute >= 60 || second >= 61 || hour > 24 || (hour === 24 && (minute > 0 || second > 0 || millisecond > 0)) ) { throw new DeveloperError(iso8601ErrorMessage); } //>>includeEnd('debug'); //Check the UTC offset value, if no value exists, use local time //a Z indicates UTC, + or - are offsets. var offset = tokens[offsetIndex]; var offsetHours = +tokens[offsetIndex + 1]; var offsetMinutes = +(tokens[offsetIndex + 2] || 0); switch (offset) { case "+": hour = hour - offsetHours; minute = minute - offsetMinutes; break; case "-": hour = hour + offsetHours; minute = minute + offsetMinutes; break; case "Z": break; default: minute = minute + new Date( Date.UTC(year, month - 1, day, hour, minute) ).getTimezoneOffset(); break; } } //ISO8601 denotes a leap second by any time having a seconds component of 60 seconds. //If that's the case, we need to temporarily subtract a second in order to build a UTC date. //Then we add it back in after converting to TAI. var isLeapSecond = second === 60; if (isLeapSecond) { second--; } //Even if we successfully parsed the string into its components, after applying UTC offset or //special cases like 24:00:00 denoting midnight, we need to normalize the data appropriately. //milliseconds can never be greater than 1000, and seconds can't be above 60, so we start with minutes while (minute >= 60) { minute -= 60; hour++; } while (hour >= 24) { hour -= 24; day++; } tmp = inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1]; while (day > tmp) { day -= tmp; month++; if (month > 12) { month -= 12; year++; } tmp = inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1]; } //If UTC offset is at the beginning/end of the day, minutes can be negative. while (minute < 0) { minute += 60; hour--; } while (hour < 0) { hour += 24; day--; } while (day < 1) { month--; if (month < 1) { month += 12; year--; } tmp = inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1]; day += tmp; } //Now create the JulianDate components from the Gregorian date and actually create our instance. var components = computeJulianDateComponents( year, month, day, hour, minute, second, millisecond ); if (!defined(result)) { result = new JulianDate(components[0], components[1], TimeStandard$1.UTC); } else { setComponents(components[0], components[1], result); convertUtcToTai(result); } //If we were on a leap second, add it back. if (isLeapSecond) { JulianDate.addSeconds(result, 1, result); } return result; }; /** * Creates a new instance that represents the current system time. * This is equivalent to calling JulianDate.fromDate(new Date());. * * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. */ JulianDate.now = function (result) { return JulianDate.fromDate(new Date(), result); }; var toGregorianDateScratch = new JulianDate(0, 0, TimeStandard$1.TAI); /** * Creates a {@link GregorianDate} from the provided instance. * * @param {JulianDate} julianDate The date to be converted. * @param {GregorianDate} [result] An existing instance to use for the result. * @returns {GregorianDate} The modified result parameter or a new instance if none was provided. */ JulianDate.toGregorianDate = function (julianDate, result) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } //>>includeEnd('debug'); var isLeapSecond = false; var thisUtc = convertTaiToUtc(julianDate, toGregorianDateScratch); if (!defined(thisUtc)) { //Conversion to UTC will fail if we are during a leap second. //If that's the case, subtract a second and convert again. //JavaScript doesn't support leap seconds, so this results in second 59 being repeated twice. JulianDate.addSeconds(julianDate, -1, toGregorianDateScratch); thisUtc = convertTaiToUtc(toGregorianDateScratch, toGregorianDateScratch); isLeapSecond = true; } var julianDayNumber = thisUtc.dayNumber; var secondsOfDay = thisUtc.secondsOfDay; if (secondsOfDay >= 43200.0) { julianDayNumber += 1; } // Algorithm from page 604 of the Explanatory Supplement to the // Astronomical Almanac (Seidelmann 1992). var L = (julianDayNumber + 68569) | 0; var N = ((4 * L) / 146097) | 0; L = (L - (((146097 * N + 3) / 4) | 0)) | 0; var I = ((4000 * (L + 1)) / 1461001) | 0; L = (L - (((1461 * I) / 4) | 0) + 31) | 0; var J = ((80 * L) / 2447) | 0; var day = (L - (((2447 * J) / 80) | 0)) | 0; L = (J / 11) | 0; var month = (J + 2 - 12 * L) | 0; var year = (100 * (N - 49) + I + L) | 0; var hour = (secondsOfDay / TimeConstants$1.SECONDS_PER_HOUR) | 0; var remainingSeconds = secondsOfDay - hour * TimeConstants$1.SECONDS_PER_HOUR; var minute = (remainingSeconds / TimeConstants$1.SECONDS_PER_MINUTE) | 0; remainingSeconds = remainingSeconds - minute * TimeConstants$1.SECONDS_PER_MINUTE; var second = remainingSeconds | 0; var millisecond = (remainingSeconds - second) / TimeConstants$1.SECONDS_PER_MILLISECOND; // JulianDates are noon-based hour += 12; if (hour > 23) { hour -= 24; } //If we were on a leap second, add it back. if (isLeapSecond) { second += 1; } if (!defined(result)) { return new GregorianDate( year, month, day, hour, minute, second, millisecond, isLeapSecond ); } result.year = year; result.month = month; result.day = day; result.hour = hour; result.minute = minute; result.second = second; result.millisecond = millisecond; result.isLeapSecond = isLeapSecond; return result; }; /** * Creates a JavaScript Date from the provided instance. * Since JavaScript dates are only accurate to the nearest millisecond and * cannot represent a leap second, consider using {@link JulianDate.toGregorianDate} instead. * If the provided JulianDate is during a leap second, the previous second is used. * * @param {JulianDate} julianDate The date to be converted. * @returns {Date} A new instance representing the provided date. */ JulianDate.toDate = function (julianDate) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } //>>includeEnd('debug'); var gDate = JulianDate.toGregorianDate(julianDate, gregorianDateScratch); var second = gDate.second; if (gDate.isLeapSecond) { second -= 1; } return new Date( Date.UTC( gDate.year, gDate.month - 1, gDate.day, gDate.hour, gDate.minute, second, gDate.millisecond ) ); }; /** * Creates an ISO8601 representation of the provided date. * * @param {JulianDate} julianDate The date to be converted. * @param {Number} [precision] The number of fractional digits used to represent the seconds component. By default, the most precise representation is used. * @returns {String} The ISO8601 representation of the provided date. */ JulianDate.toIso8601 = function (julianDate, precision) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } //>>includeEnd('debug'); var gDate = JulianDate.toGregorianDate(julianDate, gregorianDateScratch); var year = gDate.year; var month = gDate.month; var day = gDate.day; var hour = gDate.hour; var minute = gDate.minute; var second = gDate.second; var millisecond = gDate.millisecond; // special case - Iso8601.MAXIMUM_VALUE produces a string which we can't parse unless we adjust. // 10000-01-01T00:00:00 is the same instant as 9999-12-31T24:00:00 if ( year === 10000 && month === 1 && day === 1 && hour === 0 && minute === 0 && second === 0 && millisecond === 0 ) { year = 9999; month = 12; day = 31; hour = 24; } var millisecondStr; if (!defined(precision) && millisecond !== 0) { //Forces milliseconds into a number with at least 3 digits to whatever the default toString() precision is. millisecondStr = (millisecond * 0.01).toString().replace(".", ""); return sprintf( "%04d-%02d-%02dT%02d:%02d:%02d.%sZ", year, month, day, hour, minute, second, millisecondStr ); } //Precision is either 0 or milliseconds is 0 with undefined precision, in either case, leave off milliseconds entirely if (!defined(precision) || precision === 0) { return sprintf( "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second ); } //Forces milliseconds into a number with at least 3 digits to whatever the specified precision is. millisecondStr = (millisecond * 0.01) .toFixed(precision) .replace(".", "") .slice(0, precision); return sprintf( "%04d-%02d-%02dT%02d:%02d:%02d.%sZ", year, month, day, hour, minute, second, millisecondStr ); }; /** * Duplicates a JulianDate instance. * * @param {JulianDate} julianDate The date to duplicate. * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. Returns undefined if julianDate is undefined. */ JulianDate.clone = function (julianDate, result) { if (!defined(julianDate)) { return undefined; } if (!defined(result)) { return new JulianDate( julianDate.dayNumber, julianDate.secondsOfDay, TimeStandard$1.TAI ); } result.dayNumber = julianDate.dayNumber; result.secondsOfDay = julianDate.secondsOfDay; return result; }; /** * Compares two instances. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Number} A negative value if left is less than right, a positive value if left is greater than right, or zero if left and right are equal. */ JulianDate.compare = function (left, right) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("left is required."); } if (!defined(right)) { throw new DeveloperError("right is required."); } //>>includeEnd('debug'); var julianDayNumberDifference = left.dayNumber - right.dayNumber; if (julianDayNumberDifference !== 0) { return julianDayNumberDifference; } return left.secondsOfDay - right.secondsOfDay; }; /** * Compares two instances and returns true if they are equal, false otherwise. * * @param {JulianDate} [left] The first instance. * @param {JulianDate} [right] The second instance. * @returns {Boolean} true if the dates are equal; otherwise, false. */ JulianDate.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.dayNumber === right.dayNumber && left.secondsOfDay === right.secondsOfDay) ); }; /** * Compares two instances and returns true if they are within epsilon seconds of * each other. That is, in order for the dates to be considered equal (and for * this function to return true), the absolute value of the difference between them, in * seconds, must be less than epsilon. * * @param {JulianDate} [left] The first instance. * @param {JulianDate} [right] The second instance. * @param {Number} [epsilon=0] The maximum number of seconds that should separate the two instances. * @returns {Boolean} true if the two dates are within epsilon seconds of each other; otherwise false. */ JulianDate.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0); return ( left === right || (defined(left) && defined(right) && Math.abs(JulianDate.secondsDifference(left, right)) <= epsilon) ); }; /** * Computes the total number of whole and fractional days represented by the provided instance. * * @param {JulianDate} julianDate The date. * @returns {Number} The Julian date as single floating point number. */ JulianDate.totalDays = function (julianDate) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } //>>includeEnd('debug'); return ( julianDate.dayNumber + julianDate.secondsOfDay / TimeConstants$1.SECONDS_PER_DAY ); }; /** * Computes the difference in seconds between the provided instance. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Number} The difference, in seconds, when subtracting right from left. */ JulianDate.secondsDifference = function (left, right) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("left is required."); } if (!defined(right)) { throw new DeveloperError("right is required."); } //>>includeEnd('debug'); var dayDifference = (left.dayNumber - right.dayNumber) * TimeConstants$1.SECONDS_PER_DAY; return dayDifference + (left.secondsOfDay - right.secondsOfDay); }; /** * Computes the difference in days between the provided instance. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Number} The difference, in days, when subtracting right from left. */ JulianDate.daysDifference = function (left, right) { //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError("left is required."); } if (!defined(right)) { throw new DeveloperError("right is required."); } //>>includeEnd('debug'); var dayDifference = left.dayNumber - right.dayNumber; var secondDifference = (left.secondsOfDay - right.secondsOfDay) / TimeConstants$1.SECONDS_PER_DAY; return dayDifference + secondDifference; }; /** * Computes the number of seconds the provided instance is ahead of UTC. * * @param {JulianDate} julianDate The date. * @returns {Number} The number of seconds the provided instance is ahead of UTC */ JulianDate.computeTaiMinusUtc = function (julianDate) { binarySearchScratchLeapSecond.julianDate = julianDate; var leapSeconds = JulianDate.leapSeconds; var index = binarySearch( leapSeconds, binarySearchScratchLeapSecond, compareLeapSecondDates$1 ); if (index < 0) { index = ~index; --index; if (index < 0) { index = 0; } } return leapSeconds[index].offset; }; /** * Adds the provided number of seconds to the provided date instance. * * @param {JulianDate} julianDate The date. * @param {Number} seconds The number of seconds to add or subtract. * @param {JulianDate} result An existing instance to use for the result. * @returns {JulianDate} The modified result parameter. */ JulianDate.addSeconds = function (julianDate, seconds, result) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } if (!defined(seconds)) { throw new DeveloperError("seconds is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); return setComponents( julianDate.dayNumber, julianDate.secondsOfDay + seconds, result ); }; /** * Adds the provided number of minutes to the provided date instance. * * @param {JulianDate} julianDate The date. * @param {Number} minutes The number of minutes to add or subtract. * @param {JulianDate} result An existing instance to use for the result. * @returns {JulianDate} The modified result parameter. */ JulianDate.addMinutes = function (julianDate, minutes, result) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } if (!defined(minutes)) { throw new DeveloperError("minutes is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var newSecondsOfDay = julianDate.secondsOfDay + minutes * TimeConstants$1.SECONDS_PER_MINUTE; return setComponents(julianDate.dayNumber, newSecondsOfDay, result); }; /** * Adds the provided number of hours to the provided date instance. * * @param {JulianDate} julianDate The date. * @param {Number} hours The number of hours to add or subtract. * @param {JulianDate} result An existing instance to use for the result. * @returns {JulianDate} The modified result parameter. */ JulianDate.addHours = function (julianDate, hours, result) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } if (!defined(hours)) { throw new DeveloperError("hours is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var newSecondsOfDay = julianDate.secondsOfDay + hours * TimeConstants$1.SECONDS_PER_HOUR; return setComponents(julianDate.dayNumber, newSecondsOfDay, result); }; /** * Adds the provided number of days to the provided date instance. * * @param {JulianDate} julianDate The date. * @param {Number} days The number of days to add or subtract. * @param {JulianDate} result An existing instance to use for the result. * @returns {JulianDate} The modified result parameter. */ JulianDate.addDays = function (julianDate, days, result) { //>>includeStart('debug', pragmas.debug); if (!defined(julianDate)) { throw new DeveloperError("julianDate is required."); } if (!defined(days)) { throw new DeveloperError("days is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var newJulianDayNumber = julianDate.dayNumber + days; return setComponents(newJulianDayNumber, julianDate.secondsOfDay, result); }; /** * Compares the provided instances and returns true if left is earlier than right, false otherwise. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Boolean} true if left is earlier than right, false otherwise. */ JulianDate.lessThan = function (left, right) { return JulianDate.compare(left, right) < 0; }; /** * Compares the provided instances and returns true if left is earlier than or equal to right, false otherwise. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Boolean} true if left is earlier than or equal to right, false otherwise. */ JulianDate.lessThanOrEquals = function (left, right) { return JulianDate.compare(left, right) <= 0; }; /** * Compares the provided instances and returns true if left is later than right, false otherwise. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Boolean} true if left is later than right, false otherwise. */ JulianDate.greaterThan = function (left, right) { return JulianDate.compare(left, right) > 0; }; /** * Compares the provided instances and returns true if left is later than or equal to right, false otherwise. * * @param {JulianDate} left The first instance. * @param {JulianDate} right The second instance. * @returns {Boolean} true if left is later than or equal to right, false otherwise. */ JulianDate.greaterThanOrEquals = function (left, right) { return JulianDate.compare(left, right) >= 0; }; /** * Duplicates this instance. * * @param {JulianDate} [result] An existing instance to use for the result. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. */ JulianDate.prototype.clone = function (result) { return JulianDate.clone(this, result); }; /** * Compares this and the provided instance and returns true if they are equal, false otherwise. * * @param {JulianDate} [right] The second instance. * @returns {Boolean} true if the dates are equal; otherwise, false. */ JulianDate.prototype.equals = function (right) { return JulianDate.equals(this, right); }; /** * Compares this and the provided instance and returns true if they are within epsilon seconds of * each other. That is, in order for the dates to be considered equal (and for * this function to return true), the absolute value of the difference between them, in * seconds, must be less than epsilon. * * @param {JulianDate} [right] The second instance. * @param {Number} [epsilon=0] The maximum number of seconds that should separate the two instances. * @returns {Boolean} true if the two dates are within epsilon seconds of each other; otherwise false. */ JulianDate.prototype.equalsEpsilon = function (right, epsilon) { return JulianDate.equalsEpsilon(this, right, epsilon); }; /** * Creates a string representing this date in ISO8601 format. * * @returns {String} A string representing this date in ISO8601 format. */ JulianDate.prototype.toString = function () { return JulianDate.toIso8601(this); }; /** * Gets or sets the list of leap seconds used throughout Cesium. * @memberof JulianDate * @type {LeapSecond[]} */ JulianDate.leapSeconds = [ new LeapSecond(new JulianDate(2441317, 43210.0, TimeStandard$1.TAI), 10), // January 1, 1972 00:00:00 UTC new LeapSecond(new JulianDate(2441499, 43211.0, TimeStandard$1.TAI), 11), // July 1, 1972 00:00:00 UTC new LeapSecond(new JulianDate(2441683, 43212.0, TimeStandard$1.TAI), 12), // January 1, 1973 00:00:00 UTC new LeapSecond(new JulianDate(2442048, 43213.0, TimeStandard$1.TAI), 13), // January 1, 1974 00:00:00 UTC new LeapSecond(new JulianDate(2442413, 43214.0, TimeStandard$1.TAI), 14), // January 1, 1975 00:00:00 UTC new LeapSecond(new JulianDate(2442778, 43215.0, TimeStandard$1.TAI), 15), // January 1, 1976 00:00:00 UTC new LeapSecond(new JulianDate(2443144, 43216.0, TimeStandard$1.TAI), 16), // January 1, 1977 00:00:00 UTC new LeapSecond(new JulianDate(2443509, 43217.0, TimeStandard$1.TAI), 17), // January 1, 1978 00:00:00 UTC new LeapSecond(new JulianDate(2443874, 43218.0, TimeStandard$1.TAI), 18), // January 1, 1979 00:00:00 UTC new LeapSecond(new JulianDate(2444239, 43219.0, TimeStandard$1.TAI), 19), // January 1, 1980 00:00:00 UTC new LeapSecond(new JulianDate(2444786, 43220.0, TimeStandard$1.TAI), 20), // July 1, 1981 00:00:00 UTC new LeapSecond(new JulianDate(2445151, 43221.0, TimeStandard$1.TAI), 21), // July 1, 1982 00:00:00 UTC new LeapSecond(new JulianDate(2445516, 43222.0, TimeStandard$1.TAI), 22), // July 1, 1983 00:00:00 UTC new LeapSecond(new JulianDate(2446247, 43223.0, TimeStandard$1.TAI), 23), // July 1, 1985 00:00:00 UTC new LeapSecond(new JulianDate(2447161, 43224.0, TimeStandard$1.TAI), 24), // January 1, 1988 00:00:00 UTC new LeapSecond(new JulianDate(2447892, 43225.0, TimeStandard$1.TAI), 25), // January 1, 1990 00:00:00 UTC new LeapSecond(new JulianDate(2448257, 43226.0, TimeStandard$1.TAI), 26), // January 1, 1991 00:00:00 UTC new LeapSecond(new JulianDate(2448804, 43227.0, TimeStandard$1.TAI), 27), // July 1, 1992 00:00:00 UTC new LeapSecond(new JulianDate(2449169, 43228.0, TimeStandard$1.TAI), 28), // July 1, 1993 00:00:00 UTC new LeapSecond(new JulianDate(2449534, 43229.0, TimeStandard$1.TAI), 29), // July 1, 1994 00:00:00 UTC new LeapSecond(new JulianDate(2450083, 43230.0, TimeStandard$1.TAI), 30), // January 1, 1996 00:00:00 UTC new LeapSecond(new JulianDate(2450630, 43231.0, TimeStandard$1.TAI), 31), // July 1, 1997 00:00:00 UTC new LeapSecond(new JulianDate(2451179, 43232.0, TimeStandard$1.TAI), 32), // January 1, 1999 00:00:00 UTC new LeapSecond(new JulianDate(2453736, 43233.0, TimeStandard$1.TAI), 33), // January 1, 2006 00:00:00 UTC new LeapSecond(new JulianDate(2454832, 43234.0, TimeStandard$1.TAI), 34), // January 1, 2009 00:00:00 UTC new LeapSecond(new JulianDate(2456109, 43235.0, TimeStandard$1.TAI), 35), // July 1, 2012 00:00:00 UTC new LeapSecond(new JulianDate(2457204, 43236.0, TimeStandard$1.TAI), 36), // July 1, 2015 00:00:00 UTC new LeapSecond(new JulianDate(2457754, 43237.0, TimeStandard$1.TAI), 37), // January 1, 2017 00:00:00 UTC ]; /** * @license * * Grauw URI utilities * * See: http://hg.grauw.nl/grauw-lib/file/tip/src/uri.js * * @author Laurens Holst (http://www.grauw.nl/) * * Copyright 2012 Laurens Holst * * 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. * */ /** * Constructs a URI object. * @constructor * @class Implementation of URI parsing and base URI resolving algorithm in RFC 3986. * @param {string|URI} uri A string or URI object to create the object from. */ function URI(uri) { if (uri instanceof URI) { // copy constructor this.scheme = uri.scheme; this.authority = uri.authority; this.path = uri.path; this.query = uri.query; this.fragment = uri.fragment; } else if (uri) { // uri is URI string or cast to string var c = parseRegex$1.exec(uri); this.scheme = c[1]; this.authority = c[2]; this.path = c[3]; this.query = c[4]; this.fragment = c[5]; } } // Initial values on the prototype URI.prototype.scheme = null; URI.prototype.authority = null; URI.prototype.path = ''; URI.prototype.query = null; URI.prototype.fragment = null; // Regular expression from RFC 3986 appendix B var parseRegex$1 = new RegExp('^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?$'); /** * Returns the scheme part of the URI. * In "http://example.com:80/a/b?x#y" this is "http". */ URI.prototype.getScheme = function() { return this.scheme; }; /** * Returns the authority part of the URI. * In "http://example.com:80/a/b?x#y" this is "example.com:80". */ URI.prototype.getAuthority = function() { return this.authority; }; /** * Returns the path part of the URI. * In "http://example.com:80/a/b?x#y" this is "/a/b". * In "mailto:mike@example.com" this is "mike@example.com". */ URI.prototype.getPath = function() { return this.path; }; /** * Returns the query part of the URI. * In "http://example.com:80/a/b?x#y" this is "x". */ URI.prototype.getQuery = function() { return this.query; }; /** * Returns the fragment part of the URI. * In "http://example.com:80/a/b?x#y" this is "y". */ URI.prototype.getFragment = function() { return this.fragment; }; /** * Tests whether the URI is an absolute URI. * See RFC 3986 section 4.3. */ URI.prototype.isAbsolute = function() { return !!this.scheme && !this.fragment; }; ///** //* Extensive validation of the URI against the ABNF in RFC 3986 //*/ //URI.prototype.validate /** * Tests whether the URI is a same-document reference. * See RFC 3986 section 4.4. * * To perform more thorough comparison, you can normalise the URI objects. */ URI.prototype.isSameDocumentAs = function(uri) { return uri.scheme == this.scheme && uri.authority == this.authority && uri.path == this.path && uri.query == this.query; }; /** * Simple String Comparison of two URIs. * See RFC 3986 section 6.2.1. * * To perform more thorough comparison, you can normalise the URI objects. */ URI.prototype.equals = function(uri) { return this.isSameDocumentAs(uri) && uri.fragment == this.fragment; }; /** * Normalizes the URI using syntax-based normalization. * This includes case normalization, percent-encoding normalization and path segment normalization. * XXX: Percent-encoding normalization does not escape characters that need to be escaped. * (Although that would not be a valid URI in the first place. See validate().) * See RFC 3986 section 6.2.2. */ URI.prototype.normalize = function() { this.removeDotSegments(); if (this.scheme) this.scheme = this.scheme.toLowerCase(); if (this.authority) this.authority = this.authority.replace(authorityRegex, replaceAuthority). replace(caseRegex, replaceCase); if (this.path) this.path = this.path.replace(caseRegex, replaceCase); if (this.query) this.query = this.query.replace(caseRegex, replaceCase); if (this.fragment) this.fragment = this.fragment.replace(caseRegex, replaceCase); }; var caseRegex = /%[0-9a-z]{2}/gi; var percentRegex = /[a-zA-Z0-9\-\._~]/; var authorityRegex = /(.*@)?([^@:]*)(:.*)?/; function replaceCase(str) { var dec = unescape(str); return percentRegex.test(dec) ? dec : str.toUpperCase(); } function replaceAuthority(str, p1, p2, p3) { return (p1 || '') + p2.toLowerCase() + (p3 || ''); } /** * Resolve a relative URI (this) against a base URI. * The base URI must be an absolute URI. * See RFC 3986 section 5.2 */ URI.prototype.resolve = function(baseURI) { var uri = new URI(); if (this.scheme) { uri.scheme = this.scheme; uri.authority = this.authority; uri.path = this.path; uri.query = this.query; } else { uri.scheme = baseURI.scheme; if (this.authority) { uri.authority = this.authority; uri.path = this.path; uri.query = this.query; } else { uri.authority = baseURI.authority; if (this.path == '') { uri.path = baseURI.path; uri.query = this.query || baseURI.query; } else { if (this.path.charAt(0) == '/') { uri.path = this.path; uri.removeDotSegments(); } else { if (baseURI.authority && baseURI.path == '') { uri.path = '/' + this.path; } else { uri.path = baseURI.path.substring(0, baseURI.path.lastIndexOf('/') + 1) + this.path; } uri.removeDotSegments(); } uri.query = this.query; } } } uri.fragment = this.fragment; return uri; }; /** * Remove dot segments from path. * See RFC 3986 section 5.2.4 * @private */ URI.prototype.removeDotSegments = function() { var input = this.path.split('/'), output = [], segment, absPath = input[0] == ''; if (absPath) input.shift(); input[0] == '' ? input.shift() : null; while (input.length) { segment = input.shift(); if (segment == '..') { output.pop(); } else if (segment != '.') { output.push(segment); } } if (segment == '.' || segment == '..') output.push(''); if (absPath) output.unshift(''); this.path = output.join('/'); }; // We don't like this function because it builds up a cache that is never cleared. // /** // * Resolves a relative URI against an absolute base URI. // * Convenience method. // * @param {String} uri the relative URI to resolve // * @param {String} baseURI the base URI (must be absolute) to resolve against // */ // URI.resolve = function(sURI, sBaseURI) { // var uri = cache[sURI] || (cache[sURI] = new URI(sURI)); // var baseURI = cache[sBaseURI] || (cache[sBaseURI] = new URI(sBaseURI)); // return uri.resolve(baseURI).toString(); // }; // var cache = {}; /** * Serialises the URI to a string. */ URI.prototype.toString = function() { var result = ''; if (this.scheme) result += this.scheme + ':'; if (this.authority) result += '//' + this.authority; result += this.path; if (this.query) result += '?' + this.query; if (this.fragment) result += '#' + this.fragment; return result; }; /** * @private */ function appendForwardSlash(url) { if (url.length === 0 || url[url.length - 1] !== "/") { url = url + "/"; } return url; } /** * Clones an object, returning a new object containing the same properties. * * @function * * @param {Object} object The object to clone. * @param {Boolean} [deep=false] If true, all properties will be deep cloned recursively. * @returns {Object} The cloned object. */ function clone$1(object, deep) { if (object === null || typeof object !== "object") { return object; } deep = defaultValue(deep, false); var result = new object.constructor(); for (var propertyName in object) { if (object.hasOwnProperty(propertyName)) { var value = object[propertyName]; if (deep) { value = clone$1(value, deep); } result[propertyName] = value; } } return result; } /** * Merges two objects, copying their properties onto a new combined object. When two objects have the same * property, the value of the property on the first object is used. If either object is undefined, * it will be treated as an empty object. * * @example * var object1 = { * propOne : 1, * propTwo : { * value1 : 10 * } * } * var object2 = { * propTwo : 2 * } * var final = Cesium.combine(object1, object2); * * // final === { * // propOne : 1, * // propTwo : { * // value1 : 10 * // } * // } * * @param {Object} [object1] The first object to merge. * @param {Object} [object2] The second object to merge. * @param {Boolean} [deep=false] Perform a recursive merge. * @returns {Object} The combined object containing all properties from both objects. * * @function */ function combine$2(object1, object2, deep) { deep = defaultValue(deep, false); var result = {}; var object1Defined = defined(object1); var object2Defined = defined(object2); var property; var object1Value; var object2Value; if (object1Defined) { for (property in object1) { if (object1.hasOwnProperty(property)) { object1Value = object1[property]; if ( object2Defined && deep && typeof object1Value === "object" && object2.hasOwnProperty(property) ) { object2Value = object2[property]; if (typeof object2Value === "object") { result[property] = combine$2(object1Value, object2Value, deep); } else { result[property] = object1Value; } } else { result[property] = object1Value; } } } } if (object2Defined) { for (property in object2) { if ( object2.hasOwnProperty(property) && !result.hasOwnProperty(property) ) { object2Value = object2[property]; result[property] = object2Value; } } } return result; } /** * Given a relative Uri and a base Uri, returns the absolute Uri of the relative Uri. * @function * * @param {String} relative The relative Uri. * @param {String} [base] The base Uri. * @returns {String} The absolute Uri of the given relative Uri. * * @example * //absolute Uri will be "https://test.com/awesome.png"; * var absoluteUri = Cesium.getAbsoluteUri('awesome.png', 'https://test.com'); */ function getAbsoluteUri(relative, base) { var documentObject; if (typeof document !== "undefined") { documentObject = document; } return getAbsoluteUri._implementation(relative, base, documentObject); } getAbsoluteUri._implementation = function (relative, base, documentObject) { //>>includeStart('debug', pragmas.debug); if (!defined(relative)) { throw new DeveloperError("relative uri is required."); } //>>includeEnd('debug'); if (!defined(base)) { if (typeof documentObject === "undefined") { return relative; } base = defaultValue(documentObject.baseURI, documentObject.location.href); } var baseUri = new URI(base); var relativeUri = new URI(relative); return relativeUri.resolve(baseUri).toString(); }; /** * Given a URI, returns the base path of the URI. * @function * * @param {String} uri The Uri. * @param {Boolean} [includeQuery = false] Whether or not to include the query string and fragment form the uri * @returns {String} The base path of the Uri. * * @example * // basePath will be "/Gallery/"; * var basePath = Cesium.getBaseUri('/Gallery/simple.czml?value=true&example=false'); * * // basePath will be "/Gallery/?value=true&example=false"; * var basePath = Cesium.getBaseUri('/Gallery/simple.czml?value=true&example=false', true); */ function getBaseUri(uri, includeQuery) { //>>includeStart('debug', pragmas.debug); if (!defined(uri)) { throw new DeveloperError("uri is required."); } //>>includeEnd('debug'); var basePath = ""; var i = uri.lastIndexOf("/"); if (i !== -1) { basePath = uri.substring(0, i + 1); } if (!includeQuery) { return basePath; } uri = new URI(uri); if (defined(uri.query)) { basePath += "?" + uri.query; } if (defined(uri.fragment)) { basePath += "#" + uri.fragment; } return basePath; } /** * Given a URI, returns the extension of the URI. * @function getExtensionFromUri * * @param {String} uri The Uri. * @returns {String} The extension of the Uri. * * @example * //extension will be "czml"; * var extension = Cesium.getExtensionFromUri('/Gallery/simple.czml?value=true&example=false'); */ function getExtensionFromUri(uri) { //>>includeStart('debug', pragmas.debug); if (!defined(uri)) { throw new DeveloperError("uri is required."); } //>>includeEnd('debug'); var uriObject = new URI(uri); uriObject.normalize(); var path = uriObject.path; var index = path.lastIndexOf("/"); if (index !== -1) { path = path.substr(index + 1); } index = path.lastIndexOf("."); if (index === -1) { path = ""; } else { path = path.substr(index + 1); } return path; } var blobUriRegex = /^blob:/i; /** * Determines if the specified uri is a blob uri. * * @function isBlobUri * * @param {String} uri The uri to test. * @returns {Boolean} true when the uri is a blob uri; otherwise, false. * * @private */ function isBlobUri(uri) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("uri", uri); //>>includeEnd('debug'); return blobUriRegex.test(uri); } var a$1; /** * Given a URL, determine whether that URL is considered cross-origin to the current page. * * @private */ function isCrossOriginUrl(url) { if (!defined(a$1)) { a$1 = document.createElement("a"); } // copy window location into the anchor to get consistent results // when the port is default for the protocol (e.g. 80 for HTTP) a$1.href = window.location.href; // host includes both hostname and port if the port is not standard var host = a$1.host; var protocol = a$1.protocol; a$1.href = url; // IE only absolutizes href on get, not set // eslint-disable-next-line no-self-assign a$1.href = a$1.href; return protocol !== a$1.protocol || host !== a$1.host; } var dataUriRegex$2 = /^data:/i; /** * Determines if the specified uri is a data uri. * * @function isDataUri * * @param {String} uri The uri to test. * @returns {Boolean} true when the uri is a data uri; otherwise, false. * * @private */ function isDataUri(uri) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("uri", uri); //>>includeEnd('debug'); return dataUriRegex$2.test(uri); } /** * @private */ function loadAndExecuteScript(url) { var deferred = when.defer(); var script = document.createElement("script"); script.async = true; script.src = url; var head = document.getElementsByTagName("head")[0]; script.onload = function () { script.onload = undefined; head.removeChild(script); deferred.resolve(); }; script.onerror = function (e) { deferred.reject(e); }; head.appendChild(script); return deferred.promise; } /** * Converts an object representing a set of name/value pairs into a query string, * with names and values encoded properly for use in a URL. Values that are arrays * will produce multiple values with the same name. * @function objectToQuery * * @param {Object} obj The object containing data to encode. * @returns {String} An encoded query string. * * * @example * var str = Cesium.objectToQuery({ * key1 : 'some value', * key2 : 'a/b', * key3 : ['x', 'y'] * }); * * @see queryToObject * // str will be: * // 'key1=some%20value&key2=a%2Fb&key3=x&key3=y' */ function objectToQuery(obj) { //>>includeStart('debug', pragmas.debug); if (!defined(obj)) { throw new DeveloperError("obj is required."); } //>>includeEnd('debug'); var result = ""; for (var propName in obj) { if (obj.hasOwnProperty(propName)) { var value = obj[propName]; var part = encodeURIComponent(propName) + "="; if (Array.isArray(value)) { for (var i = 0, len = value.length; i < len; ++i) { result += part + encodeURIComponent(value[i]) + "&"; } } else { result += part + encodeURIComponent(value) + "&"; } } } // trim last & result = result.slice(0, -1); // This function used to replace %20 with + which is more compact and readable. // However, some servers didn't properly handle + as a space. // https://github.com/CesiumGS/cesium/issues/2192 return result; } /** * Parses a query string into an object, where the keys and values of the object are the * name/value pairs from the query string, decoded. If a name appears multiple times, * the value in the object will be an array of values. * @function queryToObject * * @param {String} queryString The query string. * @returns {Object} An object containing the parameters parsed from the query string. * * * @example * var obj = Cesium.queryToObject('key1=some%20value&key2=a%2Fb&key3=x&key3=y'); * // obj will be: * // { * // key1 : 'some value', * // key2 : 'a/b', * // key3 : ['x', 'y'] * // } * * @see objectToQuery */ function queryToObject(queryString) { //>>includeStart('debug', pragmas.debug); if (!defined(queryString)) { throw new DeveloperError("queryString is required."); } //>>includeEnd('debug'); var result = {}; if (queryString === "") { return result; } var parts = queryString.replace(/\+/g, "%20").split(/[&;]/); for (var i = 0, len = parts.length; i < len; ++i) { var subparts = parts[i].split("="); var name = decodeURIComponent(subparts[0]); var value = subparts[1]; if (defined(value)) { value = decodeURIComponent(value); } else { value = ""; } var resultValue = result[name]; if (typeof resultValue === "string") { // expand the single value to an array result[name] = [resultValue, value]; } else if (Array.isArray(resultValue)) { resultValue.push(value); } else { result[name] = value; } } return result; } /** * State of the request. * * @enum {Number} */ var RequestState = { /** * Initial unissued state. * * @type Number * @constant */ UNISSUED: 0, /** * Issued but not yet active. Will become active when open slots are available. * * @type Number * @constant */ ISSUED: 1, /** * Actual http request has been sent. * * @type Number * @constant */ ACTIVE: 2, /** * Request completed successfully. * * @type Number * @constant */ RECEIVED: 3, /** * Request was cancelled, either explicitly or automatically because of low priority. * * @type Number * @constant */ CANCELLED: 4, /** * Request failed. * * @type Number * @constant */ FAILED: 5, }; var RequestState$1 = Object.freeze(RequestState); /** * An enum identifying the type of request. Used for finer grained logging and priority sorting. * * @enum {Number} */ var RequestType = { /** * Terrain request. * * @type Number * @constant */ TERRAIN: 0, /** * Imagery request. * * @type Number * @constant */ IMAGERY: 1, /** * 3D Tiles request. * * @type Number * @constant */ TILES3D: 2, /** * Other request. * * @type Number * @constant */ OTHER: 3, }; var RequestType$1 = Object.freeze(RequestType); /** * Stores information for making a request. In general this does not need to be constructed directly. * * @alias Request * @constructor * @param {Object} [options] An object with the following properties: * @param {String} [options.url] The url to request. * @param {Request.RequestCallback} [options.requestFunction] The function that makes the actual data request. * @param {Request.CancelCallback} [options.cancelFunction] The function that is called when the request is cancelled. * @param {Request.PriorityCallback} [options.priorityFunction] The function that is called to update the request's priority, which occurs once per frame. * @param {Number} [options.priority=0.0] The initial priority of the request. * @param {Boolean} [options.throttle=false] Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the request will be throttled and sent based on priority. * @param {Boolean} [options.throttleByServer=false] Whether to throttle the request by server. * @param {RequestType} [options.type=RequestType.OTHER] The type of request. */ function Request(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var throttleByServer = defaultValue(options.throttleByServer, false); var throttle = defaultValue(options.throttle, false); /** * The URL to request. * * @type {String} */ this.url = options.url; /** * The function that makes the actual data request. * * @type {Request.RequestCallback} */ this.requestFunction = options.requestFunction; /** * The function that is called when the request is cancelled. * * @type {Request.CancelCallback} */ this.cancelFunction = options.cancelFunction; /** * The function that is called to update the request's priority, which occurs once per frame. * * @type {Request.PriorityCallback} */ this.priorityFunction = options.priorityFunction; /** * Priority is a unit-less value where lower values represent higher priority. * For world-based objects, this is usually the distance from the camera. * A request that does not have a priority function defaults to a priority of 0. * * If priorityFunction is defined, this value is updated every frame with the result of that call. * * @type {Number} * @default 0.0 */ this.priority = defaultValue(options.priority, 0.0); /** * Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the * request will be throttled and sent based on priority. * * @type {Boolean} * @readonly * * @default false */ this.throttle = throttle; /** * Whether to throttle the request by server. Browsers typically support about 6-8 parallel connections * for HTTP/1 servers, and an unlimited amount of connections for HTTP/2 servers. Setting this value * to true is preferable for requests going through HTTP/1 servers. * * @type {Boolean} * @readonly * * @default false */ this.throttleByServer = throttleByServer; /** * Type of request. * * @type {RequestType} * @readonly * * @default RequestType.OTHER */ this.type = defaultValue(options.type, RequestType$1.OTHER); /** * A key used to identify the server that a request is going to. It is derived from the url's authority and scheme. * * @type {String} * * @private */ this.serverKey = undefined; /** * The current state of the request. * * @type {RequestState} * @readonly */ this.state = RequestState$1.UNISSUED; /** * The requests's deferred promise. * * @type {Object} * * @private */ this.deferred = undefined; /** * Whether the request was explicitly cancelled. * * @type {Boolean} * * @private */ this.cancelled = false; } /** * Mark the request as cancelled. * * @private */ Request.prototype.cancel = function () { this.cancelled = true; }; /** * Duplicates a Request instance. * * @param {Request} [result] The object onto which to store the result. * * @returns {Request} The modified result parameter or a new Resource instance if one was not provided. */ Request.prototype.clone = function (result) { if (!defined(result)) { return new Request(this); } result.url = this.url; result.requestFunction = this.requestFunction; result.cancelFunction = this.cancelFunction; result.priorityFunction = this.priorityFunction; result.priority = this.priority; result.throttle = this.throttle; result.throttleByServer = this.throttleByServer; result.type = this.type; result.serverKey = this.serverKey; // These get defaulted because the cloned request hasn't been issued result.state = this.RequestState.UNISSUED; result.deferred = undefined; result.cancelled = false; return result; }; /** * Parses the result of XMLHttpRequest's getAllResponseHeaders() method into * a dictionary. * * @function parseResponseHeaders * * @param {String} headerString The header string returned by getAllResponseHeaders(). The format is * described here: http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method * @returns {Object} A dictionary of key/value pairs, where each key is the name of a header and the corresponding value * is that header's value. * * @private */ function parseResponseHeaders(headerString) { var headers = {}; if (!headerString) { return headers; } var headerPairs = headerString.split("\u000d\u000a"); for (var i = 0; i < headerPairs.length; ++i) { var headerPair = headerPairs[i]; // Can't use split() here because it does the wrong thing // if the header value has the string ": " in it. var index = headerPair.indexOf("\u003a\u0020"); if (index > 0) { var key = headerPair.substring(0, index); var val = headerPair.substring(index + 2); headers[key] = val; } } return headers; } /** * An event that is raised when a request encounters an error. * * @constructor * @alias RequestErrorEvent * * @param {Number} [statusCode] The HTTP error status code, such as 404. * @param {Object} [response] The response included along with the error. * @param {String|Object} [responseHeaders] The response headers, represented either as an object literal or as a * string in the format returned by XMLHttpRequest's getAllResponseHeaders() function. */ function RequestErrorEvent(statusCode, response, responseHeaders) { /** * The HTTP error status code, such as 404. If the error does not have a particular * HTTP code, this property will be undefined. * * @type {Number} */ this.statusCode = statusCode; /** * The response included along with the error. If the error does not include a response, * this property will be undefined. * * @type {Object} */ this.response = response; /** * The headers included in the response, represented as an object literal of key/value pairs. * If the error does not include any headers, this property will be undefined. * * @type {Object} */ this.responseHeaders = responseHeaders; if (typeof this.responseHeaders === "string") { this.responseHeaders = parseResponseHeaders(this.responseHeaders); } } /** * Creates a string representing this RequestErrorEvent. * @memberof RequestErrorEvent * * @returns {String} A string representing the provided RequestErrorEvent. */ RequestErrorEvent.prototype.toString = function () { var str = "Request has failed."; if (defined(this.statusCode)) { str += " Status Code: " + this.statusCode; } return str; }; /** * A generic utility class for managing subscribers for a particular event. * This class is usually instantiated inside of a container class and * exposed as a property for others to subscribe to. * * @alias Event * @constructor * @example * MyObject.prototype.myListener = function(arg1, arg2) { * this.myArg1Copy = arg1; * this.myArg2Copy = arg2; * } * * var myObjectInstance = new MyObject(); * var evt = new Cesium.Event(); * evt.addEventListener(MyObject.prototype.myListener, myObjectInstance); * evt.raiseEvent('1', '2'); * evt.removeEventListener(MyObject.prototype.myListener); */ function Event() { this._listeners = []; this._scopes = []; this._toRemove = []; this._insideRaiseEvent = false; } Object.defineProperties(Event.prototype, { /** * The number of listeners currently subscribed to the event. * @memberof Event.prototype * @type {Number} * @readonly */ numberOfListeners: { get: function () { return this._listeners.length - this._toRemove.length; }, }, }); /** * Registers a callback function to be executed whenever the event is raised. * An optional scope can be provided to serve as the this pointer * in which the function will execute. * * @param {Function} listener The function to be executed when the event is raised. * @param {Object} [scope] An optional object scope to serve as the this * pointer in which the listener function will execute. * @returns {Event.RemoveCallback} A function that will remove this event listener when invoked. * * @see Event#raiseEvent * @see Event#removeEventListener */ Event.prototype.addEventListener = function (listener, scope) { //>>includeStart('debug', pragmas.debug); Check.typeOf.func("listener", listener); //>>includeEnd('debug'); this._listeners.push(listener); this._scopes.push(scope); var event = this; return function () { event.removeEventListener(listener, scope); }; }; /** * Unregisters a previously registered callback. * * @param {Function} listener The function to be unregistered. * @param {Object} [scope] The scope that was originally passed to addEventListener. * @returns {Boolean} true if the listener was removed; false if the listener and scope are not registered with the event. * * @see Event#addEventListener * @see Event#raiseEvent */ Event.prototype.removeEventListener = function (listener, scope) { //>>includeStart('debug', pragmas.debug); Check.typeOf.func("listener", listener); //>>includeEnd('debug'); var listeners = this._listeners; var scopes = this._scopes; var index = -1; for (var i = 0; i < listeners.length; i++) { if (listeners[i] === listener && scopes[i] === scope) { index = i; break; } } if (index !== -1) { if (this._insideRaiseEvent) { //In order to allow removing an event subscription from within //a callback, we don't actually remove the items here. Instead //remember the index they are at and undefined their value. this._toRemove.push(index); listeners[index] = undefined; scopes[index] = undefined; } else { listeners.splice(index, 1); scopes.splice(index, 1); } return true; } return false; }; function compareNumber(a, b) { return b - a; } /** * Raises the event by calling each registered listener with all supplied arguments. * * @param {...Object} arguments This method takes any number of parameters and passes them through to the listener functions. * * @see Event#addEventListener * @see Event#removeEventListener */ Event.prototype.raiseEvent = function () { this._insideRaiseEvent = true; var i; var listeners = this._listeners; var scopes = this._scopes; var length = listeners.length; for (i = 0; i < length; i++) { var listener = listeners[i]; if (defined(listener)) { listeners[i].apply(scopes[i], arguments); } } //Actually remove items removed in removeEventListener. var toRemove = this._toRemove; length = toRemove.length; if (length > 0) { toRemove.sort(compareNumber); for (i = 0; i < length; i++) { var index = toRemove[i]; listeners.splice(index, 1); scopes.splice(index, 1); } toRemove.length = 0; } this._insideRaiseEvent = false; }; /** * Array implementation of a heap. * * @alias Heap * @constructor * @private * * @param {Object} options Object with the following properties: * @param {Heap.ComparatorCallback} options.comparator The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. */ function Heap(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.defined("options.comparator", options.comparator); //>>includeEnd('debug'); this._comparator = options.comparator; this._array = []; this._length = 0; this._maximumLength = undefined; } Object.defineProperties(Heap.prototype, { /** * Gets the length of the heap. * * @memberof Heap.prototype * * @type {Number} * @readonly */ length: { get: function () { return this._length; }, }, /** * Gets the internal array. * * @memberof Heap.prototype * * @type {Array} * @readonly */ internalArray: { get: function () { return this._array; }, }, /** * Gets and sets the maximum length of the heap. * * @memberof Heap.prototype * * @type {Number} */ maximumLength: { get: function () { return this._maximumLength; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("maximumLength", value, 0); //>>includeEnd('debug'); var originalLength = this._length; if (value < originalLength) { var array = this._array; // Remove trailing references for (var i = value; i < originalLength; ++i) { array[i] = undefined; } this._length = value; array.length = value; } this._maximumLength = value; }, }, /** * The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. * * @memberof Heap.prototype * * @type {Heap.ComparatorCallback} */ comparator: { get: function () { return this._comparator; }, }, }); function swap$3(array, a, b) { var temp = array[a]; array[a] = array[b]; array[b] = temp; } /** * Resizes the internal array of the heap. * * @param {Number} [length] The length to resize internal array to. Defaults to the current length of the heap. */ Heap.prototype.reserve = function (length) { length = defaultValue(length, this._length); this._array.length = length; }; /** * Update the heap so that index and all descendants satisfy the heap property. * * @param {Number} [index=0] The starting index to heapify from. */ Heap.prototype.heapify = function (index) { index = defaultValue(index, 0); var length = this._length; var comparator = this._comparator; var array = this._array; var candidate = -1; var inserting = true; while (inserting) { var right = 2 * (index + 1); var left = right - 1; if (left < length && comparator(array[left], array[index]) < 0) { candidate = left; } else { candidate = index; } if (right < length && comparator(array[right], array[candidate]) < 0) { candidate = right; } if (candidate !== index) { swap$3(array, candidate, index); index = candidate; } else { inserting = false; } } }; /** * Resort the heap. */ Heap.prototype.resort = function () { var length = this._length; for (var i = Math.ceil(length / 2); i >= 0; --i) { this.heapify(i); } }; /** * Insert an element into the heap. If the length would grow greater than maximumLength * of the heap, extra elements are removed. * * @param {*} element The element to insert * * @return {*} The element that was removed from the heap if the heap is at full capacity. */ Heap.prototype.insert = function (element) { //>>includeStart('debug', pragmas.debug); Check.defined("element", element); //>>includeEnd('debug'); var array = this._array; var comparator = this._comparator; var maximumLength = this._maximumLength; var index = this._length++; if (index < array.length) { array[index] = element; } else { array.push(element); } while (index !== 0) { var parent = Math.floor((index - 1) / 2); if (comparator(array[index], array[parent]) < 0) { swap$3(array, index, parent); index = parent; } else { break; } } var removedElement; if (defined(maximumLength) && this._length > maximumLength) { removedElement = array[maximumLength]; this._length = maximumLength; } return removedElement; }; /** * Remove the element specified by index from the heap and return it. * * @param {Number} [index=0] The index to remove. * @returns {*} The specified element of the heap. */ Heap.prototype.pop = function (index) { index = defaultValue(index, 0); if (this._length === 0) { return undefined; } //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThan("index", index, this._length); //>>includeEnd('debug'); var array = this._array; var root = array[index]; swap$3(array, index, --this._length); this.heapify(index); array[this._length] = undefined; // Remove trailing reference return root; }; function sortRequests(a, b) { return a.priority - b.priority; } var statistics = { numberOfAttemptedRequests: 0, numberOfActiveRequests: 0, numberOfCancelledRequests: 0, numberOfCancelledActiveRequests: 0, numberOfFailedRequests: 0, numberOfActiveRequestsEver: 0, lastNumberOfActiveRequests: 0, }; var priorityHeapLength = 20; var requestHeap = new Heap({ comparator: sortRequests, }); requestHeap.maximumLength = priorityHeapLength; requestHeap.reserve(priorityHeapLength); var activeRequests = []; var numberOfActiveRequestsByServer = {}; var pageUri = typeof document !== "undefined" ? new URI(document.location.href) : new URI(); var requestCompletedEvent = new Event(); /** * The request scheduler is used to track and constrain the number of active requests in order to prioritize incoming requests. The ability * to retain control over the number of requests in CesiumJS is important because due to events such as changes in the camera position, * a lot of new requests may be generated and a lot of in-flight requests may become redundant. The request scheduler manually constrains the * number of requests so that newer requests wait in a shorter queue and don't have to compete for bandwidth with requests that have expired. * * @namespace RequestScheduler * */ function RequestScheduler() {} /** * The maximum number of simultaneous active requests. Un-throttled requests do not observe this limit. * @type {Number} * @default 50 */ RequestScheduler.maximumRequests = 50; /** * The maximum number of simultaneous active requests per server. Un-throttled requests or servers specifically * listed in {@link requestsByServer} do not observe this limit. * @type {Number} * @default 6 */ RequestScheduler.maximumRequestsPerServer = 6; /** * A per server key list of overrides to use for throttling instead of maximumRequestsPerServer * @type {Object} * * @example * RequestScheduler.requestsByServer = { * 'api.cesium.com:443': 18, * 'assets.cesium.com:443': 18 * }; */ RequestScheduler.requestsByServer = { "api.cesium.com:443": 18, "assets.cesium.com:443": 18, }; /** * Specifies if the request scheduler should throttle incoming requests, or let the browser queue requests under its control. * @type {Boolean} * @default true */ RequestScheduler.throttleRequests = true; /** * When true, log statistics to the console every frame * @type {Boolean} * @default false * @private */ RequestScheduler.debugShowStatistics = false; /** * An event that's raised when a request is completed. Event handlers are passed * the error object if the request fails. * * @type {Event} * @default Event() * @private */ RequestScheduler.requestCompletedEvent = requestCompletedEvent; Object.defineProperties(RequestScheduler, { /** * Returns the statistics used by the request scheduler. * * @memberof RequestScheduler * * @type Object * @readonly * @private */ statistics: { get: function () { return statistics; }, }, /** * The maximum size of the priority heap. This limits the number of requests that are sorted by priority. Only applies to requests that are not yet active. * * @memberof RequestScheduler * * @type {Number} * @default 20 * @private */ priorityHeapLength: { get: function () { return priorityHeapLength; }, set: function (value) { // If the new length shrinks the heap, need to cancel some of the requests. // Since this value is not intended to be tweaked regularly it is fine to just cancel the high priority requests. if (value < priorityHeapLength) { while (requestHeap.length > value) { var request = requestHeap.pop(); cancelRequest(request); } } priorityHeapLength = value; requestHeap.maximumLength = value; requestHeap.reserve(value); }, }, }); function updatePriority(request) { if (defined(request.priorityFunction)) { request.priority = request.priorityFunction(); } } function serverHasOpenSlots(serverKey) { var maxRequests = defaultValue( RequestScheduler.requestsByServer[serverKey], RequestScheduler.maximumRequestsPerServer ); return numberOfActiveRequestsByServer[serverKey] < maxRequests; } function issueRequest(request) { if (request.state === RequestState$1.UNISSUED) { request.state = RequestState$1.ISSUED; request.deferred = when.defer(); } return request.deferred.promise; } function getRequestReceivedFunction(request) { return function (results) { if (request.state === RequestState$1.CANCELLED) { // If the data request comes back but the request is cancelled, ignore it. return; } // explicitly set to undefined to ensure GC of request response data. See #8843 var deferred = request.deferred; --statistics.numberOfActiveRequests; --numberOfActiveRequestsByServer[request.serverKey]; requestCompletedEvent.raiseEvent(); request.state = RequestState$1.RECEIVED; request.deferred = undefined; deferred.resolve(results); }; } function getRequestFailedFunction(request) { return function (error) { if (request.state === RequestState$1.CANCELLED) { // If the data request comes back but the request is cancelled, ignore it. return; } ++statistics.numberOfFailedRequests; --statistics.numberOfActiveRequests; --numberOfActiveRequestsByServer[request.serverKey]; requestCompletedEvent.raiseEvent(error); request.state = RequestState$1.FAILED; request.deferred.reject(error); }; } function startRequest(request) { var promise = issueRequest(request); request.state = RequestState$1.ACTIVE; activeRequests.push(request); ++statistics.numberOfActiveRequests; ++statistics.numberOfActiveRequestsEver; ++numberOfActiveRequestsByServer[request.serverKey]; request .requestFunction() .then(getRequestReceivedFunction(request)) .otherwise(getRequestFailedFunction(request)); return promise; } function cancelRequest(request) { var active = request.state === RequestState$1.ACTIVE; request.state = RequestState$1.CANCELLED; ++statistics.numberOfCancelledRequests; // check that deferred has not been cleared since cancelRequest can be called // on a finished request, e.g. by clearForSpecs during tests if (defined(request.deferred)) { var deferred = request.deferred; request.deferred = undefined; deferred.reject(); } if (active) { --statistics.numberOfActiveRequests; --numberOfActiveRequestsByServer[request.serverKey]; ++statistics.numberOfCancelledActiveRequests; } if (defined(request.cancelFunction)) { request.cancelFunction(); } } /** * Sort requests by priority and start requests. * @private */ RequestScheduler.update = function () { var i; var request; // Loop over all active requests. Cancelled, failed, or received requests are removed from the array to make room for new requests. var removeCount = 0; var activeLength = activeRequests.length; for (i = 0; i < activeLength; ++i) { request = activeRequests[i]; if (request.cancelled) { // Request was explicitly cancelled cancelRequest(request); } if (request.state !== RequestState$1.ACTIVE) { // Request is no longer active, remove from array ++removeCount; continue; } if (removeCount > 0) { // Shift back to fill in vacated slots from completed requests activeRequests[i - removeCount] = request; } } activeRequests.length -= removeCount; // Update priority of issued requests and resort the heap var issuedRequests = requestHeap.internalArray; var issuedLength = requestHeap.length; for (i = 0; i < issuedLength; ++i) { updatePriority(issuedRequests[i]); } requestHeap.resort(); // Get the number of open slots and fill with the highest priority requests. // Un-throttled requests are automatically added to activeRequests, so activeRequests.length may exceed maximumRequests var openSlots = Math.max( RequestScheduler.maximumRequests - activeRequests.length, 0 ); var filledSlots = 0; while (filledSlots < openSlots && requestHeap.length > 0) { // Loop until all open slots are filled or the heap becomes empty request = requestHeap.pop(); if (request.cancelled) { // Request was explicitly cancelled cancelRequest(request); continue; } if (request.throttleByServer && !serverHasOpenSlots(request.serverKey)) { // Open slots are available, but the request is throttled by its server. Cancel and try again later. cancelRequest(request); continue; } startRequest(request); ++filledSlots; } updateStatistics(); }; /** * Get the server key from a given url. * * @param {String} url The url. * @returns {String} The server key. * @private */ RequestScheduler.getServerKey = function (url) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("url", url); //>>includeEnd('debug'); var uri = new URI(url).resolve(pageUri); uri.normalize(); var serverKey = uri.authority; if (!/:/.test(serverKey)) { // If the authority does not contain a port number, add port 443 for https or port 80 for http serverKey = serverKey + ":" + (uri.scheme === "https" ? "443" : "80"); } var length = numberOfActiveRequestsByServer[serverKey]; if (!defined(length)) { numberOfActiveRequestsByServer[serverKey] = 0; } return serverKey; }; /** * Issue a request. If request.throttle is false, the request is sent immediately. Otherwise the request will be * queued and sorted by priority before being sent. * * @param {Request} request The request object. * * @returns {Promise|undefined} A Promise for the requested data, or undefined if this request does not have high enough priority to be issued. * * @private */ RequestScheduler.request = function (request) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("request", request); Check.typeOf.string("request.url", request.url); Check.typeOf.func("request.requestFunction", request.requestFunction); //>>includeEnd('debug'); if (isDataUri(request.url) || isBlobUri(request.url)) { requestCompletedEvent.raiseEvent(); request.state = RequestState$1.RECEIVED; return request.requestFunction(); } ++statistics.numberOfAttemptedRequests; if (!defined(request.serverKey)) { request.serverKey = RequestScheduler.getServerKey(request.url); } if ( RequestScheduler.throttleRequests && request.throttleByServer && !serverHasOpenSlots(request.serverKey) ) { // Server is saturated. Try again later. return undefined; } if (!RequestScheduler.throttleRequests || !request.throttle) { return startRequest(request); } if (activeRequests.length >= RequestScheduler.maximumRequests) { // Active requests are saturated. Try again later. return undefined; } // Insert into the priority heap and see if a request was bumped off. If this request is the lowest // priority it will be returned. updatePriority(request); var removedRequest = requestHeap.insert(request); if (defined(removedRequest)) { if (removedRequest === request) { // Request does not have high enough priority to be issued return undefined; } // A previously issued request has been bumped off the priority heap, so cancel it cancelRequest(removedRequest); } return issueRequest(request); }; function updateStatistics() { if (!RequestScheduler.debugShowStatistics) { return; } if ( statistics.numberOfActiveRequests === 0 && statistics.lastNumberOfActiveRequests > 0 ) { if (statistics.numberOfAttemptedRequests > 0) { console.log( "Number of attempted requests: " + statistics.numberOfAttemptedRequests ); statistics.numberOfAttemptedRequests = 0; } if (statistics.numberOfCancelledRequests > 0) { console.log( "Number of cancelled requests: " + statistics.numberOfCancelledRequests ); statistics.numberOfCancelledRequests = 0; } if (statistics.numberOfCancelledActiveRequests > 0) { console.log( "Number of cancelled active requests: " + statistics.numberOfCancelledActiveRequests ); statistics.numberOfCancelledActiveRequests = 0; } if (statistics.numberOfFailedRequests > 0) { console.log( "Number of failed requests: " + statistics.numberOfFailedRequests ); statistics.numberOfFailedRequests = 0; } } statistics.lastNumberOfActiveRequests = statistics.numberOfActiveRequests; } /** * For testing only. Clears any requests that may not have completed from previous tests. * * @private */ RequestScheduler.clearForSpecs = function () { while (requestHeap.length > 0) { var request = requestHeap.pop(); cancelRequest(request); } var length = activeRequests.length; for (var i = 0; i < length; ++i) { cancelRequest(activeRequests[i]); } activeRequests.length = 0; numberOfActiveRequestsByServer = {}; // Clear stats statistics.numberOfAttemptedRequests = 0; statistics.numberOfActiveRequests = 0; statistics.numberOfCancelledRequests = 0; statistics.numberOfCancelledActiveRequests = 0; statistics.numberOfFailedRequests = 0; statistics.numberOfActiveRequestsEver = 0; statistics.lastNumberOfActiveRequests = 0; }; /** * For testing only. * * @private */ RequestScheduler.numberOfActiveRequestsByServer = function (serverKey) { return numberOfActiveRequestsByServer[serverKey]; }; /** * For testing only. * * @private */ RequestScheduler.requestHeap = requestHeap; /** * 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. *

* 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 * @constructor * @extends Error * * @param {String} [message] The error message for this exception. * * @see DeveloperError */ function RuntimeError(message) { /** * 'RuntimeError' indicating that this exception was thrown due to a runtime error. * @type {String} * @readonly */ this.name = "RuntimeError"; /** * The explanation for why this exception was thrown. * @type {String} * @readonly */ this.message = message; //Browsers such as IE don't have a stack property until you actually throw the error. var stack; try { throw new Error(); } catch (e) { stack = e.stack; } /** * The stack trace of this exception, if available. * @type {String} * @readonly */ this.stack = stack; } if (defined(Object.create)) { RuntimeError.prototype = Object.create(Error.prototype); RuntimeError.prototype.constructor = RuntimeError; } RuntimeError.prototype.toString = function () { var str = this.name + ": " + this.message; if (defined(this.stack)) { str += "\n" + this.stack.toString(); } return str; }; /** * A singleton that contains all of the servers that are trusted. Credentials will be sent with * any requests to these servers. * * @namespace TrustedServers * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} */ var TrustedServers = {}; var _servers = {}; /** * Adds a trusted server to the registry * * @param {String} host The host to be added. * @param {Number} port The port used to access the host. * * @example * // Add a trusted server * TrustedServers.add('my.server.com', 80); */ TrustedServers.add = function (host, port) { //>>includeStart('debug', pragmas.debug); if (!defined(host)) { throw new DeveloperError("host is required."); } if (!defined(port) || port <= 0) { throw new DeveloperError("port is required to be greater than 0."); } //>>includeEnd('debug'); var authority = host.toLowerCase() + ":" + port; if (!defined(_servers[authority])) { _servers[authority] = true; } }; /** * Removes a trusted server from the registry * * @param {String} host The host to be removed. * @param {Number} port The port used to access the host. * * @example * // Remove a trusted server * TrustedServers.remove('my.server.com', 80); */ TrustedServers.remove = function (host, port) { //>>includeStart('debug', pragmas.debug); if (!defined(host)) { throw new DeveloperError("host is required."); } if (!defined(port) || port <= 0) { throw new DeveloperError("port is required to be greater than 0."); } //>>includeEnd('debug'); var authority = host.toLowerCase() + ":" + port; if (defined(_servers[authority])) { delete _servers[authority]; } }; function getAuthority(url) { var uri = new URI(url); uri.normalize(); // Removes username:password@ so we just have host[:port] var authority = uri.getAuthority(); if (!defined(authority)) { return undefined; // Relative URL } if (authority.indexOf("@") !== -1) { var parts = authority.split("@"); authority = parts[1]; } // If the port is missing add one based on the scheme if (authority.indexOf(":") === -1) { var scheme = uri.getScheme(); if (!defined(scheme)) { scheme = window.location.protocol; scheme = scheme.substring(0, scheme.length - 1); } if (scheme === "http") { authority += ":80"; } else if (scheme === "https") { authority += ":443"; } else { return undefined; } } return authority; } /** * Tests whether a server is trusted or not. The server must have been added with the port if it is included in the url. * * @param {String} url The url to be tested against the trusted list * * @returns {boolean} Returns true if url is trusted, false otherwise. * * @example * // Add server * TrustedServers.add('my.server.com', 81); * * // Check if server is trusted * if (TrustedServers.contains('https://my.server.com:81/path/to/file.png')) { * // my.server.com:81 is trusted * } * if (TrustedServers.contains('https://my.server.com/path/to/file.png')) { * // my.server.com isn't trusted * } */ TrustedServers.contains = function (url) { //>>includeStart('debug', pragmas.debug); if (!defined(url)) { throw new DeveloperError("url is required."); } //>>includeEnd('debug'); var authority = getAuthority(url); if (defined(authority) && defined(_servers[authority])) { return true; } return false; }; /** * Clears the registry * * @example * // Remove a trusted server * TrustedServers.clear(); */ TrustedServers.clear = function () { _servers = {}; }; var xhrBlobSupported = (function () { try { var xhr = new XMLHttpRequest(); xhr.open("GET", "#", true); xhr.responseType = "blob"; return xhr.responseType === "blob"; } catch (e) { return false; } })(); /** * Parses a query string and returns the object equivalent. * * @param {Uri} uri The Uri with a query object. * @param {Resource} resource The Resource that will be assigned queryParameters. * @param {Boolean} merge If true, we'll merge with the resource's existing queryParameters. Otherwise they will be replaced. * @param {Boolean} preserveQueryParameters If true duplicate parameters will be concatenated into an array. If false, keys in uri will take precedence. * * @private */ function parseQuery(uri, resource, merge, preserveQueryParameters) { var queryString = uri.query; if (!defined(queryString) || queryString.length === 0) { return {}; } var query; // Special case we run into where the querystring is just a string, not key/value pairs if (queryString.indexOf("=") === -1) { var result = {}; result[queryString] = undefined; query = result; } else { query = queryToObject(queryString); } if (merge) { resource._queryParameters = combineQueryParameters( query, resource._queryParameters, preserveQueryParameters ); } else { resource._queryParameters = query; } uri.query = undefined; } /** * Converts a query object into a string. * * @param {Uri} uri The Uri object that will have the query object set. * @param {Resource} resource The resource that has queryParameters * * @private */ function stringifyQuery(uri, resource) { var queryObject = resource._queryParameters; var keys = Object.keys(queryObject); // We have 1 key with an undefined value, so this is just a string, not key/value pairs if (keys.length === 1 && !defined(queryObject[keys[0]])) { uri.query = keys[0]; } else { uri.query = objectToQuery(queryObject); } } /** * Clones a value if it is defined, otherwise returns the default value * * @param {*} [val] The value to clone. * @param {*} [defaultVal] The default value. * * @returns {*} A clone of val or the defaultVal. * * @private */ function defaultClone(val, defaultVal) { if (!defined(val)) { return defaultVal; } return defined(val.clone) ? val.clone() : clone$1(val); } /** * Checks to make sure the Resource isn't already being requested. * * @param {Request} request The request to check. * * @private */ function checkAndResetRequest(request) { if ( request.state === RequestState$1.ISSUED || request.state === RequestState$1.ACTIVE ) { throw new RuntimeError("The Resource is already being fetched."); } request.state = RequestState$1.UNISSUED; request.deferred = undefined; } /** * This combines a map of query parameters. * * @param {Object} q1 The first map of query parameters. Values in this map will take precedence if preserveQueryParameters is false. * @param {Object} q2 The second map of query parameters. * @param {Boolean} preserveQueryParameters If true duplicate parameters will be concatenated into an array. If false, keys in q1 will take precedence. * * @returns {Object} The combined map of query parameters. * * @example * var q1 = { * a: 1, * b: 2 * }; * var q2 = { * a: 3, * c: 4 * }; * var q3 = { * b: [5, 6], * d: 7 * } * * // Returns * // { * // a: [1, 3], * // b: 2, * // c: 4 * // }; * combineQueryParameters(q1, q2, true); * * // Returns * // { * // a: 1, * // b: 2, * // c: 4 * // }; * combineQueryParameters(q1, q2, false); * * // Returns * // { * // a: 1, * // b: [2, 5, 6], * // d: 7 * // }; * combineQueryParameters(q1, q3, true); * * // Returns * // { * // a: 1, * // b: 2, * // d: 7 * // }; * combineQueryParameters(q1, q3, false); * * @private */ function combineQueryParameters(q1, q2, preserveQueryParameters) { if (!preserveQueryParameters) { return combine$2(q1, q2); } var result = clone$1(q1, true); for (var param in q2) { if (q2.hasOwnProperty(param)) { var value = result[param]; var q2Value = q2[param]; if (defined(value)) { if (!Array.isArray(value)) { value = result[param] = [value]; } result[param] = value.concat(q2Value); } else { result[param] = Array.isArray(q2Value) ? q2Value.slice() : q2Value; } } } return result; } /** * A resource that includes the location and any other parameters we need to retrieve it or create derived resources. It also provides the ability to retry requests. * * @alias Resource * @constructor * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * * @example * function refreshTokenRetryCallback(resource, error) { * if (error.statusCode === 403) { * // 403 status code means a new token should be generated * return getNewAccessToken() * .then(function(token) { * resource.queryParameters.access_token = token; * return true; * }) * .otherwise(function() { * return false; * }); * } * * return false; * } * * var resource = new Resource({ * url: 'http://server.com/path/to/resource.json', * proxy: new DefaultProxy('/proxy/'), * headers: { * 'X-My-Header': 'valueOfHeader' * }, * queryParameters: { * 'access_token': '123-435-456-000' * }, * retryCallback: refreshTokenRetryCallback, * retryAttempts: 1 * }); */ function Resource(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); if (typeof options === "string") { options = { url: options, }; } //>>includeStart('debug', pragmas.debug); Check.typeOf.string("options.url", options.url); //>>includeEnd('debug'); this._url = undefined; this._templateValues = defaultClone(options.templateValues, {}); this._queryParameters = defaultClone(options.queryParameters, {}); /** * Additional HTTP headers that will be sent with the request. * * @type {Object} */ this.headers = defaultClone(options.headers, {}); /** * A Request object that will be used. Intended for internal use only. * * @type {Request} */ this.request = defaultValue(options.request, new Request()); /** * A proxy to be used when loading the resource. * * @type {Proxy} */ this.proxy = options.proxy; /** * Function to call when a request for this resource fails. If it returns true or a Promise that resolves to true, the request will be retried. * * @type {Function} */ this.retryCallback = options.retryCallback; /** * The number of times the retryCallback should be called before giving up. * * @type {Number} */ this.retryAttempts = defaultValue(options.retryAttempts, 0); this._retryCount = 0; var uri = new URI(options.url); parseQuery(uri, this, true, true); // Remove the fragment as it's not sent with a request uri.fragment = undefined; this._url = uri.toString(); } /** * A helper function to create a resource depending on whether we have a String or a Resource * * @param {Resource|String} resource A Resource or a String to use when creating a new Resource. * * @returns {Resource} If resource is a String, a Resource constructed with the url and options. Otherwise the resource parameter is returned. * * @private */ Resource.createIfNeeded = function (resource) { if (resource instanceof Resource) { // Keep existing request object. This function is used internally to duplicate a Resource, so that it can't // be modified outside of a class that holds it (eg. an imagery or terrain provider). Since the Request objects // are managed outside of the providers, by the tile loading code, we want to keep the request property the same so if it is changed // in the underlying tiling code the requests for this resource will use it. return resource.getDerivedResource({ request: resource.request, }); } if (typeof resource !== "string") { return resource; } return new Resource({ url: resource, }); }; var supportsImageBitmapOptionsPromise; /** * A helper function to check whether createImageBitmap supports passing ImageBitmapOptions. * * @returns {Promise} A promise that resolves to true if this browser supports creating an ImageBitmap with options. * * @private */ Resource.supportsImageBitmapOptions = function () { // Until the HTML folks figure out what to do about this, we need to actually try loading an image to // know if this browser supports passing options to the createImageBitmap function. // https://github.com/whatwg/html/pull/4248 if (defined(supportsImageBitmapOptionsPromise)) { return supportsImageBitmapOptionsPromise; } if (typeof createImageBitmap !== "function") { supportsImageBitmapOptionsPromise = when.resolve(false); return supportsImageBitmapOptionsPromise; } var imageDataUri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg=="; supportsImageBitmapOptionsPromise = Resource.fetchBlob({ url: imageDataUri, }) .then(function (blob) { return createImageBitmap(blob, { imageOrientation: "flipY", premultiplyAlpha: "none", }); }) .then(function (imageBitmap) { return true; }) .otherwise(function () { return false; }); return supportsImageBitmapOptionsPromise; }; Object.defineProperties(Resource, { /** * Returns true if blobs are supported. * * @memberof Resource * @type {Boolean} * * @readonly */ isBlobSupported: { get: function () { return xhrBlobSupported; }, }, }); Object.defineProperties(Resource.prototype, { /** * Query parameters appended to the url. * * @memberof Resource.prototype * @type {Object} * * @readonly */ queryParameters: { get: function () { return this._queryParameters; }, }, /** * The key/value pairs used to replace template parameters in the url. * * @memberof Resource.prototype * @type {Object} * * @readonly */ templateValues: { get: function () { return this._templateValues; }, }, /** * The url to the resource with template values replaced, query string appended and encoded by proxy if one was set. * * @memberof Resource.prototype * @type {String} */ url: { get: function () { return this.getUrlComponent(true, true); }, set: function (value) { var uri = new URI(value); parseQuery(uri, this, false); // Remove the fragment as it's not sent with a request uri.fragment = undefined; this._url = uri.toString(); }, }, /** * The file extension of the resource. * * @memberof Resource.prototype * @type {String} * * @readonly */ extension: { get: function () { return getExtensionFromUri(this._url); }, }, /** * True if the Resource refers to a data URI. * * @memberof Resource.prototype * @type {Boolean} */ isDataUri: { get: function () { return isDataUri(this._url); }, }, /** * True if the Resource refers to a blob URI. * * @memberof Resource.prototype * @type {Boolean} */ isBlobUri: { get: function () { return isBlobUri(this._url); }, }, /** * True if the Resource refers to a cross origin URL. * * @memberof Resource.prototype * @type {Boolean} */ isCrossOriginUrl: { get: function () { return isCrossOriginUrl(this._url); }, }, /** * True if the Resource has request headers. This is equivalent to checking if the headers property has any keys. * * @memberof Resource.prototype * @type {Boolean} */ hasHeaders: { get: function () { return Object.keys(this.headers).length > 0; }, }, }); /** * Override Object#toString so that implicit string conversion gives the * complete URL represented by this Resource. * * @returns {String} The URL represented by this Resource */ Resource.prototype.toString = function () { return this.getUrlComponent(true, true); }; /** * Returns the url, optional with the query string and processed by a proxy. * * @param {Boolean} [query=false] If true, the query string is included. * @param {Boolean} [proxy=false] If true, the url is processed by the proxy object, if defined. * * @returns {String} The url with all the requested components. */ Resource.prototype.getUrlComponent = function (query, proxy) { if (this.isDataUri) { return this._url; } var uri = new URI(this._url); if (query) { stringifyQuery(uri, this); } // objectToQuery escapes the placeholders. Undo that. var url = uri.toString().replace(/%7B/g, "{").replace(/%7D/g, "}"); var templateValues = this._templateValues; url = url.replace(/{(.*?)}/g, function (match, key) { var replacement = templateValues[key]; if (defined(replacement)) { // use the replacement value from templateValues if there is one... return encodeURIComponent(replacement); } // otherwise leave it unchanged return match; }); if (proxy && defined(this.proxy)) { url = this.proxy.getURL(url); } return url; }; /** * Combines the specified object and the existing query parameters. This allows you to add many parameters at once, * as opposed to adding them one at a time to the queryParameters property. If a value is already set, it will be replaced with the new value. * * @param {Object} params The query parameters * @param {Boolean} [useAsDefault=false] If true the params will be used as the default values, so they will only be set if they are undefined. */ Resource.prototype.setQueryParameters = function (params, useAsDefault) { if (useAsDefault) { this._queryParameters = combineQueryParameters( this._queryParameters, params, false ); } else { this._queryParameters = combineQueryParameters( params, this._queryParameters, false ); } }; /** * Combines the specified object and the existing query parameters. This allows you to add many parameters at once, * as opposed to adding them one at a time to the queryParameters property. * * @param {Object} params The query parameters */ Resource.prototype.appendQueryParameters = function (params) { this._queryParameters = combineQueryParameters( params, this._queryParameters, true ); }; /** * Combines the specified object and the existing template values. This allows you to add many values at once, * as opposed to adding them one at a time to the templateValues property. If a value is already set, it will become an array and the new value will be appended. * * @param {Object} template The template values * @param {Boolean} [useAsDefault=false] If true the values will be used as the default values, so they will only be set if they are undefined. */ Resource.prototype.setTemplateValues = function (template, useAsDefault) { if (useAsDefault) { this._templateValues = combine$2(this._templateValues, template); } else { this._templateValues = combine$2(template, this._templateValues); } }; /** * Returns a resource relative to the current instance. All properties remain the same as the current instance unless overridden in options. * * @param {Object} options An object with the following properties * @param {String} [options.url] The url that will be resolved relative to the url of the current instance. * @param {Object} [options.queryParameters] An object containing query parameters that will be combined with those of the current instance. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). These will be combined with those of the current instance. * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The function to call when loading the resource fails. * @param {Number} [options.retryAttempts] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {Boolean} [options.preserveQueryParameters=false] If true, this will keep all query parameters from the current resource and derived resource. If false, derived parameters will replace those of the current resource. * * @returns {Resource} The resource derived from the current one. */ Resource.prototype.getDerivedResource = function (options) { var resource = this.clone(); resource._retryCount = 0; if (defined(options.url)) { var uri = new URI(options.url); var preserveQueryParameters = defaultValue( options.preserveQueryParameters, false ); parseQuery(uri, resource, true, preserveQueryParameters); // Remove the fragment as it's not sent with a request uri.fragment = undefined; resource._url = uri.resolve(new URI(getAbsoluteUri(this._url))).toString(); } if (defined(options.queryParameters)) { resource._queryParameters = combine$2( options.queryParameters, resource._queryParameters ); } if (defined(options.templateValues)) { resource._templateValues = combine$2( options.templateValues, resource.templateValues ); } if (defined(options.headers)) { resource.headers = combine$2(options.headers, resource.headers); } if (defined(options.proxy)) { resource.proxy = options.proxy; } if (defined(options.request)) { resource.request = options.request; } if (defined(options.retryCallback)) { resource.retryCallback = options.retryCallback; } if (defined(options.retryAttempts)) { resource.retryAttempts = options.retryAttempts; } return resource; }; /** * Called when a resource fails to load. This will call the retryCallback function if defined until retryAttempts is reached. * * @param {Error} [error] The error that was encountered. * * @returns {Promise} A promise to a boolean, that if true will cause the resource request to be retried. * * @private */ Resource.prototype.retryOnError = function (error) { var retryCallback = this.retryCallback; if ( typeof retryCallback !== "function" || this._retryCount >= this.retryAttempts ) { return when(false); } var that = this; return when(retryCallback(this, error)).then(function (result) { ++that._retryCount; return result; }); }; /** * Duplicates a Resource instance. * * @param {Resource} [result] The object onto which to store the result. * * @returns {Resource} The modified result parameter or a new Resource instance if one was not provided. */ Resource.prototype.clone = function (result) { if (!defined(result)) { result = new Resource({ url: this._url, }); } result._url = this._url; result._queryParameters = clone$1(this._queryParameters); result._templateValues = clone$1(this._templateValues); result.headers = clone$1(this.headers); result.proxy = this.proxy; result.retryCallback = this.retryCallback; result.retryAttempts = this.retryAttempts; result._retryCount = 0; result.request = this.request.clone(); return result; }; /** * Returns the base path of the Resource. * * @param {Boolean} [includeQuery = false] Whether or not to include the query string and fragment form the uri * * @returns {String} The base URI of the resource */ Resource.prototype.getBaseUri = function (includeQuery) { return getBaseUri(this.getUrlComponent(includeQuery), includeQuery); }; /** * Appends a forward slash to the URL. */ Resource.prototype.appendForwardSlash = function () { this._url = appendForwardSlash(this._url); }; /** * Asynchronously loads the resource as raw binary data. Returns a promise that will resolve to * an ArrayBuffer once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example * // load a single URL asynchronously * resource.fetchArrayBuffer().then(function(arrayBuffer) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchArrayBuffer = function () { return this.fetch({ responseType: "arraybuffer", }); }; /** * Creates a Resource and calls fetchArrayBuffer() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchArrayBuffer = function (options) { var resource = new Resource(options); return resource.fetchArrayBuffer(); }; /** * Asynchronously loads the given resource as a blob. Returns a promise that will resolve to * a Blob once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example * // load a single URL asynchronously * resource.fetchBlob().then(function(blob) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchBlob = function () { return this.fetch({ responseType: "blob", }); }; /** * Creates a Resource and calls fetchBlob() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchBlob = function (options) { var resource = new Resource(options); return resource.fetchBlob(); }; /** * Asynchronously loads the given image resource. Returns a promise that will resolve to * an {@link https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap|ImageBitmap} if preferImageBitmap is true and the browser supports createImageBitmap or otherwise an * {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement|Image} once loaded, or reject if the image failed to load. * * @param {Object} [options] An object with the following properties. * @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob. * @param {Boolean} [options.preferImageBitmap=false] If true, image will be decoded during fetch and an ImageBitmap is returned. * @param {Boolean} [options.flipY=false] If true, image will be vertically flipped during decode. Only applies if the browser supports createImageBitmap. * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * // load a single image asynchronously * resource.fetchImage().then(function(image) { * // use the loaded image * }).otherwise(function(error) { * // an error occurred * }); * * // load several images in parallel * when.all([resource1.fetchImage(), resource2.fetchImage()]).then(function(images) { * // images is an array containing all the loaded images * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchImage = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var preferImageBitmap = defaultValue(options.preferImageBitmap, false); var preferBlob = defaultValue(options.preferBlob, false); var flipY = defaultValue(options.flipY, false); checkAndResetRequest(this.request); // We try to load the image normally if // 1. Blobs aren't supported // 2. It's a data URI // 3. It's a blob URI // 4. It doesn't have request headers and we preferBlob is false if ( !xhrBlobSupported || this.isDataUri || this.isBlobUri || (!this.hasHeaders && !preferBlob) ) { return fetchImage({ resource: this, flipY: flipY, preferImageBitmap: preferImageBitmap, }); } var blobPromise = this.fetchBlob(); if (!defined(blobPromise)) { return; } var supportsImageBitmap; var useImageBitmap; var generatedBlobResource; var generatedBlob; return Resource.supportsImageBitmapOptions() .then(function (result) { supportsImageBitmap = result; useImageBitmap = supportsImageBitmap && preferImageBitmap; return blobPromise; }) .then(function (blob) { if (!defined(blob)) { return; } generatedBlob = blob; if (useImageBitmap) { return Resource.createImageBitmapFromBlob(blob, { flipY: flipY, premultiplyAlpha: false, }); } var blobUrl = window.URL.createObjectURL(blob); generatedBlobResource = new Resource({ url: blobUrl, }); return fetchImage({ resource: generatedBlobResource, flipY: flipY, preferImageBitmap: false, }); }) .then(function (image) { if (!defined(image)) { return; } // The blob object may be needed for use by a TileDiscardPolicy, // so attach it to the image. image.blob = generatedBlob; if (useImageBitmap) { return image; } window.URL.revokeObjectURL(generatedBlobResource.url); return image; }) .otherwise(function (error) { if (defined(generatedBlobResource)) { window.URL.revokeObjectURL(generatedBlobResource.url); } // If the blob load succeeded but the image decode failed, attach the blob // to the error object for use by a TileDiscardPolicy. // In particular, BingMapsImageryProvider uses this to detect the // zero-length response that is returned when a tile is not available. error.blob = generatedBlob; return when.reject(error); }); }; /** * Fetches an image and returns a promise to it. * * @param {Object} [options] An object with the following properties. * @param {Resource} [options.resource] Resource object that points to an image to fetch. * @param {Boolean} [options.preferImageBitmap] If true, image will be decoded during fetch and an ImageBitmap is returned. * @param {Boolean} [options.flipY] If true, image will be vertically flipped during decode. Only applies if the browser supports createImageBitmap. * * @private */ function fetchImage(options) { var resource = options.resource; var flipY = options.flipY; var preferImageBitmap = options.preferImageBitmap; var request = resource.request; request.url = resource.url; request.requestFunction = function () { var crossOrigin = false; // data URIs can't have crossorigin set. if (!resource.isDataUri && !resource.isBlobUri) { crossOrigin = resource.isCrossOriginUrl; } var deferred = when.defer(); Resource._Implementations.createImage( request, crossOrigin, deferred, flipY, preferImageBitmap ); return deferred.promise; }; var promise = RequestScheduler.request(request); if (!defined(promise)) { return; } return promise.otherwise(function (e) { // Don't retry cancelled or otherwise aborted requests if (request.state !== RequestState$1.FAILED) { return when.reject(e); } return resource.retryOnError(e).then(function (retry) { if (retry) { // Reset request so it can try again request.state = RequestState$1.UNISSUED; request.deferred = undefined; return fetchImage({ resource: resource, flipY: flipY, preferImageBitmap: preferImageBitmap, }); } return when.reject(e); }); }); } /** * Creates a Resource and calls fetchImage() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Boolean} [options.flipY=false] Whether to vertically flip the image during fetch and decode. Only applies when requesting an image and the browser supports createImageBitmap. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {Boolean} [options.preferBlob=false] If true, we will load the image via a blob. * @param {Boolean} [options.preferImageBitmap=false] If true, image will be decoded during fetch and an ImageBitmap is returned. * @returns {Promise.|Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchImage = function (options) { var resource = new Resource(options); return resource.fetchImage({ flipY: options.flipY, preferBlob: options.preferBlob, preferImageBitmap: options.preferImageBitmap, }); }; /** * Asynchronously loads the given resource as text. Returns a promise that will resolve to * a String once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example * // load text from a URL, setting a custom header * var resource = new Resource({ * url: 'http://someUrl.com/someJson.txt', * headers: { * 'X-Custom-Header' : 'some value' * } * }); * resource.fetchText().then(function(text) { * // Do something with the text * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchText = function () { return this.fetch({ responseType: "text", }); }; /** * Creates a Resource and calls fetchText() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchText = function (options) { var resource = new Resource(options); return resource.fetchText(); }; // note: */* below is */* but that ends the comment block early /** * Asynchronously loads the given resource as JSON. Returns a promise that will resolve to * a JSON object once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. This function * adds 'Accept: application/json,*/*;q=0.01' to the request headers, if not * already specified. * * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.fetchJson().then(function(jsonData) { * // Do something with the JSON object * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchJson = function () { var promise = this.fetch({ responseType: "text", headers: { Accept: "application/json,*/*;q=0.01", }, }); if (!defined(promise)) { return undefined; } return promise.then(function (value) { if (!defined(value)) { return; } return JSON.parse(value); }); }; /** * Creates a Resource and calls fetchJson() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchJson = function (options) { var resource = new Resource(options); return resource.fetchJson(); }; /** * Asynchronously loads the given resource as XML. Returns a promise that will resolve to * an XML Document once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * // load XML from a URL, setting a custom header * Cesium.loadXML('http://someUrl.com/someXML.xml', { * 'X-Custom-Header' : 'some value' * }).then(function(document) { * // Do something with the document * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest|XMLHttpRequest} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchXML = function () { return this.fetch({ responseType: "document", overrideMimeType: "text/xml", }); }; /** * Creates a Resource and calls fetchXML() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchXML = function (options) { var resource = new Resource(options); return resource.fetchXML(); }; /** * Requests a resource using JSONP. * * @param {String} [callbackParameterName='callback'] The callback parameter name that the server expects. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * // load a data asynchronously * resource.fetchJsonp().then(function(data) { * // use the loaded data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetchJsonp = function (callbackParameterName) { callbackParameterName = defaultValue(callbackParameterName, "callback"); checkAndResetRequest(this.request); //generate a unique function name var functionName; do { functionName = "loadJsonp" + Math.random().toString().substring(2, 8); } while (defined(window[functionName])); return fetchJsonp(this, callbackParameterName, functionName); }; function fetchJsonp(resource, callbackParameterName, functionName) { var callbackQuery = {}; callbackQuery[callbackParameterName] = functionName; resource.setQueryParameters(callbackQuery); var request = resource.request; request.url = resource.url; request.requestFunction = function () { var deferred = when.defer(); //assign a function with that name in the global scope window[functionName] = function (data) { deferred.resolve(data); try { delete window[functionName]; } catch (e) { window[functionName] = undefined; } }; Resource._Implementations.loadAndExecuteScript( resource.url, functionName, deferred ); return deferred.promise; }; var promise = RequestScheduler.request(request); if (!defined(promise)) { return; } return promise.otherwise(function (e) { if (request.state !== RequestState$1.FAILED) { return when.reject(e); } return resource.retryOnError(e).then(function (retry) { if (retry) { // Reset request so it can try again request.state = RequestState$1.UNISSUED; request.deferred = undefined; return fetchJsonp(resource, callbackParameterName, functionName); } return when.reject(e); }); }); } /** * Creates a Resource from a URL and calls fetchJsonp() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.callbackParameterName='callback'] The callback parameter name that the server expects. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetchJsonp = function (options) { var resource = new Resource(options); return resource.fetchJsonp(options.callbackParameterName); }; /** * @private */ Resource.prototype._makeRequest = function (options) { var resource = this; checkAndResetRequest(resource.request); var request = resource.request; request.url = resource.url; request.requestFunction = function () { var responseType = options.responseType; var headers = combine$2(options.headers, resource.headers); var overrideMimeType = options.overrideMimeType; var method = options.method; var data = options.data; var deferred = when.defer(); var xhr = Resource._Implementations.loadWithXhr( resource.url, responseType, method, data, headers, deferred, overrideMimeType ); if (defined(xhr) && defined(xhr.abort)) { request.cancelFunction = function () { xhr.abort(); }; } return deferred.promise; }; var promise = RequestScheduler.request(request); if (!defined(promise)) { return; } return promise .then(function (data) { // explicitly set to undefined to ensure GC of request response data. See #8843 request.cancelFunction = undefined; return data; }) .otherwise(function (e) { request.cancelFunction = undefined; if (request.state !== RequestState$1.FAILED) { return when.reject(e); } return resource.retryOnError(e).then(function (retry) { if (retry) { // Reset request so it can try again request.state = RequestState$1.UNISSUED; request.deferred = undefined; return resource.fetch(options); } return when.reject(e); }); }); }; var dataUriRegex$1 = /^data:(.*?)(;base64)?,(.*)$/; function decodeDataUriText(isBase64, data) { var result = decodeURIComponent(data); if (isBase64) { return atob(result); } return result; } function decodeDataUriArrayBuffer(isBase64, data) { var byteString = decodeDataUriText(isBase64, data); var buffer = new ArrayBuffer(byteString.length); var view = new Uint8Array(buffer); for (var i = 0; i < byteString.length; i++) { view[i] = byteString.charCodeAt(i); } return buffer; } function decodeDataUri(dataUriRegexResult, responseType) { responseType = defaultValue(responseType, ""); var mimeType = dataUriRegexResult[1]; var isBase64 = !!dataUriRegexResult[2]; var data = dataUriRegexResult[3]; switch (responseType) { case "": case "text": return decodeDataUriText(isBase64, data); case "arraybuffer": return decodeDataUriArrayBuffer(isBase64, data); case "blob": var buffer = decodeDataUriArrayBuffer(isBase64, data); return new Blob([buffer], { type: mimeType, }); case "document": var parser = new DOMParser(); return parser.parseFromString( decodeDataUriText(isBase64, data), mimeType ); case "json": return JSON.parse(decodeDataUriText(isBase64, data)); default: //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Unhandled responseType: " + responseType); //>>includeEnd('debug'); } } /** * Asynchronously loads the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. It's recommended that you use * the more specific functions eg. fetchJson, fetchBlob, etc. * * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.fetch() * .then(function(body) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.fetch = function (options) { options = defaultClone(options, {}); options.method = "GET"; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls fetch() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.fetch = function (options) { var resource = new Resource(options); return resource.fetch({ // Make copy of just the needed fields because headers can be passed to both the constructor and to fetch responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Asynchronously deletes the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.delete() * .then(function(body) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.delete = function (options) { options = defaultClone(options, {}); options.method = "DELETE"; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls delete() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.data] Data that is posted with the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.delete = function (options) { var resource = new Resource(options); return resource.delete({ // Make copy of just the needed fields because headers can be passed to both the constructor and to fetch responseType: options.responseType, overrideMimeType: options.overrideMimeType, data: options.data, }); }; /** * Asynchronously gets headers the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.head() * .then(function(headers) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.head = function (options) { options = defaultClone(options, {}); options.method = "HEAD"; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls head() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.head = function (options) { var resource = new Resource(options); return resource.head({ // Make copy of just the needed fields because headers can be passed to both the constructor and to fetch responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Asynchronously gets options the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.options() * .then(function(headers) { * // use the data * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.options = function (options) { options = defaultClone(options, {}); options.method = "OPTIONS"; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls options() on it. * * @param {String|Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.options = function (options) { var resource = new Resource(options); return resource.options({ // Make copy of just the needed fields because headers can be passed to both the constructor and to fetch responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Asynchronously posts data to the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} data Data that is posted with the resource. * @param {Object} [options] Object with the following properties: * @param {Object} [options.data] Data that is posted with the resource. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.post(data) * .then(function(result) { * // use the result * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.post = function (data, options) { Check.defined("data", data); options = defaultClone(options, {}); options.method = "POST"; options.data = data; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls post() on it. * * @param {Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} options.data Data that is posted with the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.post = function (options) { var resource = new Resource(options); return resource.post(options.data, { // Make copy of just the needed fields because headers can be passed to both the constructor and to post responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Asynchronously puts data to the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} data Data that is posted with the resource. * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.put(data) * .then(function(result) { * // use the result * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.put = function (data, options) { Check.defined("data", data); options = defaultClone(options, {}); options.method = "PUT"; options.data = data; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls put() on it. * * @param {Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} options.data Data that is posted with the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.put = function (options) { var resource = new Resource(options); return resource.put(options.data, { // Make copy of just the needed fields because headers can be passed to both the constructor and to post responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Asynchronously patches data to the given resource. Returns a promise that will resolve to * the result once loaded, or reject if the resource failed to load. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @param {Object} data Data that is posted with the resource. * @param {Object} [options] Object with the following properties: * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {Object} [options.headers] Additional HTTP headers to send with the request, if any. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * * @example * resource.patch(data) * .then(function(result) { * // use the result * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ Resource.prototype.patch = function (data, options) { Check.defined("data", data); options = defaultClone(options, {}); options.method = "PATCH"; options.data = data; return this._makeRequest(options); }; /** * Creates a Resource from a URL and calls patch() on it. * * @param {Object} options A url or an object with the following properties * @param {String} options.url The url of the resource. * @param {Object} options.data Data that is posted with the resource. * @param {Object} [options.queryParameters] An object containing query parameters that will be sent when retrieving the resource. * @param {Object} [options.templateValues] Key/Value pairs that are used to replace template values (eg. {x}). * @param {Object} [options.headers={}] Additional HTTP headers that will be sent. * @param {Proxy} [options.proxy] A proxy to be used when loading the resource. * @param {Resource.RetryCallback} [options.retryCallback] The Function to call when a request for this resource fails. If it returns true, the request will be retried. * @param {Number} [options.retryAttempts=0] The number of times the retryCallback should be called before giving up. * @param {Request} [options.request] A Request object that will be used. Intended for internal use only. * @param {String} [options.responseType] The type of response. This controls the type of item returned. * @param {String} [options.overrideMimeType] Overrides the MIME type returned by the server. * @returns {Promise.<*>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. */ Resource.patch = function (options) { var resource = new Resource(options); return resource.patch(options.data, { // Make copy of just the needed fields because headers can be passed to both the constructor and to post responseType: options.responseType, overrideMimeType: options.overrideMimeType, }); }; /** * Contains implementations of functions that can be replaced for testing * * @private */ Resource._Implementations = {}; function loadImageElement(url, crossOrigin, deferred) { var image = new Image(); image.onload = function () { deferred.resolve(image); }; image.onerror = function (e) { deferred.reject(e); }; if (crossOrigin) { if (TrustedServers.contains(url)) { image.crossOrigin = "use-credentials"; } else { image.crossOrigin = ""; } } image.src = url; } Resource._Implementations.createImage = function ( request, crossOrigin, deferred, flipY, preferImageBitmap ) { var url = request.url; // Passing an Image to createImageBitmap will force it to run on the main thread // since DOM elements don't exist on workers. We convert it to a blob so it's non-blocking. // See: // https://bugzilla.mozilla.org/show_bug.cgi?id=1044102#c38 // https://bugs.chromium.org/p/chromium/issues/detail?id=580202#c10 Resource.supportsImageBitmapOptions() .then(function (supportsImageBitmap) { // We can only use ImageBitmap if we can flip on decode. // See: https://github.com/CesiumGS/cesium/pull/7579#issuecomment-466146898 if (!(supportsImageBitmap && preferImageBitmap)) { loadImageElement(url, crossOrigin, deferred); return; } var responseType = "blob"; var method = "GET"; var xhrDeferred = when.defer(); var xhr = Resource._Implementations.loadWithXhr( url, responseType, method, undefined, undefined, xhrDeferred, undefined, undefined, undefined ); if (defined(xhr) && defined(xhr.abort)) { request.cancelFunction = function () { xhr.abort(); }; } return xhrDeferred.promise .then(function (blob) { if (!defined(blob)) { deferred.reject( new RuntimeError( "Successfully retrieved " + url + " but it contained no content." ) ); return; } return Resource.createImageBitmapFromBlob(blob, { flipY: flipY, premultiplyAlpha: false, }); }) .then(deferred.resolve); }) .otherwise(deferred.reject); }; /** * Wrapper for createImageBitmap * * @private */ Resource.createImageBitmapFromBlob = function (blob, options) { Check.defined("options", options); Check.typeOf.bool("options.flipY", options.flipY); Check.typeOf.bool("options.premultiplyAlpha", options.premultiplyAlpha); return createImageBitmap(blob, { imageOrientation: options.flipY ? "flipY" : "none", premultiplyAlpha: options.premultiplyAlpha ? "premultiply" : "none", }); }; function decodeResponse(loadWithHttpResponse, responseType) { switch (responseType) { case "text": return loadWithHttpResponse.toString("utf8"); case "json": return JSON.parse(loadWithHttpResponse.toString("utf8")); default: return new Uint8Array(loadWithHttpResponse).buffer; } } function loadWithHttpRequest( url, responseType, method, data, headers, deferred, overrideMimeType ) { // Note: only the 'json' and 'text' responseTypes transforms the loaded buffer /* eslint-disable no-undef */ var URL = require("url").parse(url); var http = URL.protocol === "https:" ? require("https") : require("http"); var zlib = require("zlib"); /* eslint-enable no-undef */ var options = { protocol: URL.protocol, hostname: URL.hostname, port: URL.port, path: URL.path, query: URL.query, method: method, headers: headers, }; http .request(options) .on("response", function (res) { if (res.statusCode < 200 || res.statusCode >= 300) { deferred.reject( new RequestErrorEvent(res.statusCode, res, res.headers) ); return; } var chunkArray = []; res.on("data", function (chunk) { chunkArray.push(chunk); }); res.on("end", function () { // eslint-disable-next-line no-undef var result = Buffer.concat(chunkArray); if (res.headers["content-encoding"] === "gzip") { zlib.gunzip(result, function (error, resultUnzipped) { if (error) { deferred.reject( new RuntimeError("Error decompressing response.") ); } else { deferred.resolve(decodeResponse(resultUnzipped, responseType)); } }); } else { deferred.resolve(decodeResponse(result, responseType)); } }); }) .on("error", function (e) { deferred.reject(new RequestErrorEvent()); }) .end(); } var noXMLHttpRequest = typeof XMLHttpRequest === "undefined"; Resource._Implementations.loadWithXhr = function ( url, responseType, method, data, headers, deferred, overrideMimeType ) { var dataUriRegexResult = dataUriRegex$1.exec(url); if (dataUriRegexResult !== null) { deferred.resolve(decodeDataUri(dataUriRegexResult, responseType)); return; } if (noXMLHttpRequest) { loadWithHttpRequest( url, responseType, method, data, headers, deferred); return; } var xhr = new XMLHttpRequest(); if (TrustedServers.contains(url)) { xhr.withCredentials = true; } xhr.open(method, url, true); if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) { xhr.overrideMimeType(overrideMimeType); } if (defined(headers)) { for (var key in headers) { if (headers.hasOwnProperty(key)) { xhr.setRequestHeader(key, headers[key]); } } } if (defined(responseType)) { xhr.responseType = responseType; } // While non-standard, file protocol always returns a status of 0 on success var localFile = false; if (typeof url === "string") { localFile = url.indexOf("file://") === 0 || (typeof window !== "undefined" && window.location.origin === "file://"); } xhr.onload = function () { if ( (xhr.status < 200 || xhr.status >= 300) && !(localFile && xhr.status === 0) ) { deferred.reject( new RequestErrorEvent( xhr.status, xhr.response, xhr.getAllResponseHeaders() ) ); return; } var response = xhr.response; var browserResponseType = xhr.responseType; if (method === "HEAD" || method === "OPTIONS") { var responseHeaderString = xhr.getAllResponseHeaders(); var splitHeaders = responseHeaderString.trim().split(/[\r\n]+/); var responseHeaders = {}; splitHeaders.forEach(function (line) { var parts = line.split(": "); var header = parts.shift(); responseHeaders[header] = parts.join(": "); }); deferred.resolve(responseHeaders); return; } //All modern browsers will go into either the first or second if block or last else block. //Other code paths support older browsers that either do not support the supplied responseType //or do not support the xhr.response property. if (xhr.status === 204) { // accept no content deferred.resolve(); } else if ( defined(response) && (!defined(responseType) || browserResponseType === responseType) ) { deferred.resolve(response); } else if (responseType === "json" && typeof response === "string") { try { deferred.resolve(JSON.parse(response)); } catch (e) { deferred.reject(e); } } else if ( (browserResponseType === "" || browserResponseType === "document") && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes() ) { deferred.resolve(xhr.responseXML); } else if ( (browserResponseType === "" || browserResponseType === "text") && defined(xhr.responseText) ) { deferred.resolve(xhr.responseText); } else { deferred.reject( new RuntimeError("Invalid XMLHttpRequest response type.") ); } }; xhr.onerror = function (e) { deferred.reject(new RequestErrorEvent()); }; xhr.send(data); return xhr; }; Resource._Implementations.loadAndExecuteScript = function ( url, functionName, deferred ) { return loadAndExecuteScript(url).otherwise(deferred.reject); }; /** * The default implementations * * @private */ Resource._DefaultImplementations = {}; Resource._DefaultImplementations.createImage = Resource._Implementations.createImage; Resource._DefaultImplementations.loadWithXhr = Resource._Implementations.loadWithXhr; Resource._DefaultImplementations.loadAndExecuteScript = Resource._Implementations.loadAndExecuteScript; /** * A resource instance initialized to the current browser location * * @type {Resource} * @constant */ Resource.DEFAULT = Object.freeze( new Resource({ url: typeof document === "undefined" ? "" : document.location.href.split("?")[0], }) ); /** * Specifies Earth polar motion coordinates and the difference between UT1 and UTC. * These Earth Orientation Parameters (EOP) are primarily used in the transformation from * the International Celestial Reference Frame (ICRF) to the International Terrestrial * Reference Frame (ITRF). * * @alias EarthOrientationParameters * @constructor * * @param {Object} [options] Object with the following properties: * @param {Resource|String} [options.url] The URL from which to obtain EOP data. If neither this * parameter nor options.data is specified, all EOP values are assumed * to be 0.0. If options.data is specified, this parameter is * ignored. * @param {Object} [options.data] The actual EOP data. If neither this * parameter nor options.data is specified, all EOP values are assumed * to be 0.0. * @param {Boolean} [options.addNewLeapSeconds=true] True if leap seconds that * are specified in the EOP data but not in {@link JulianDate.leapSeconds} * should be added to {@link JulianDate.leapSeconds}. False if * new leap seconds should be handled correctly in the context * of the EOP data but otherwise ignored. * * @example * // An example EOP data file, EOP.json: * { * "columnNames" : ["dateIso8601","modifiedJulianDateUtc","xPoleWanderRadians","yPoleWanderRadians","ut1MinusUtcSeconds","lengthOfDayCorrectionSeconds","xCelestialPoleOffsetRadians","yCelestialPoleOffsetRadians","taiMinusUtcSeconds"], * "samples" : [ * "2011-07-01T00:00:00Z",55743.0,2.117957047295119e-7,2.111518721609984e-6,-0.2908948,-2.956e-4,3.393695767766752e-11,3.3452143996557983e-10,34.0, * "2011-07-02T00:00:00Z",55744.0,2.193297093339541e-7,2.115460256837405e-6,-0.29065,-1.824e-4,-8.241832578862112e-11,5.623838700870617e-10,34.0, * "2011-07-03T00:00:00Z",55745.0,2.262286080161428e-7,2.1191157519929706e-6,-0.2905572,1.9e-6,-3.490658503988659e-10,6.981317007977318e-10,34.0 * ] * } * * @example * // Loading the EOP data * var eop = new Cesium.EarthOrientationParameters({ url : 'Data/EOP.json' }); * Cesium.Transforms.earthOrientationParameters = eop; * * @private */ function EarthOrientationParameters(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._dates = undefined; this._samples = undefined; this._dateColumn = -1; this._xPoleWanderRadiansColumn = -1; this._yPoleWanderRadiansColumn = -1; this._ut1MinusUtcSecondsColumn = -1; this._xCelestialPoleOffsetRadiansColumn = -1; this._yCelestialPoleOffsetRadiansColumn = -1; this._taiMinusUtcSecondsColumn = -1; this._columnCount = 0; this._lastIndex = -1; this._downloadPromise = undefined; this._dataError = undefined; this._addNewLeapSeconds = defaultValue(options.addNewLeapSeconds, true); if (defined(options.data)) { // Use supplied EOP data. onDataReady(this, options.data); } else if (defined(options.url)) { var resource = Resource.createIfNeeded(options.url); // Download EOP data. var that = this; this._downloadPromise = resource .fetchJson() .then(function (eopData) { onDataReady(that, eopData); }) .otherwise(function () { that._dataError = "An error occurred while retrieving the EOP data from the URL " + resource.url + "."; }); } else { // Use all zeros for EOP data. onDataReady(this, { columnNames: [ "dateIso8601", "modifiedJulianDateUtc", "xPoleWanderRadians", "yPoleWanderRadians", "ut1MinusUtcSeconds", "lengthOfDayCorrectionSeconds", "xCelestialPoleOffsetRadians", "yCelestialPoleOffsetRadians", "taiMinusUtcSeconds", ], samples: [], }); } } /** * A default {@link EarthOrientationParameters} instance that returns zero for all EOP values. */ EarthOrientationParameters.NONE = Object.freeze({ getPromiseToLoad: function () { return when.resolve(); }, compute: function (date, result) { if (!defined(result)) { result = new EarthOrientationParametersSample(0.0, 0.0, 0.0, 0.0, 0.0); } else { result.xPoleWander = 0.0; result.yPoleWander = 0.0; result.xPoleOffset = 0.0; result.yPoleOffset = 0.0; result.ut1MinusUtc = 0.0; } return result; }, }); /** * Gets a promise that, when resolved, indicates that the EOP data has been loaded and is * ready to use. * * @returns {Promise} The promise. */ EarthOrientationParameters.prototype.getPromiseToLoad = function () { return when(this._downloadPromise); }; /** * Computes the Earth Orientation Parameters (EOP) for a given date by interpolating. * If the EOP data has not yet been download, this method returns undefined. * * @param {JulianDate} date The date for each to evaluate the EOP. * @param {EarthOrientationParametersSample} [result] The instance to which to copy the result. * If this parameter is undefined, a new instance is created and returned. * @returns {EarthOrientationParametersSample} The EOP evaluated at the given date, or * undefined if the data necessary to evaluate EOP at the date has not yet been * downloaded. * * @exception {RuntimeError} The loaded EOP data has an error and cannot be used. * * @see EarthOrientationParameters#getPromiseToLoad */ EarthOrientationParameters.prototype.compute = function (date, result) { // We cannot compute until the samples are available. if (!defined(this._samples)) { if (defined(this._dataError)) { throw new RuntimeError(this._dataError); } return undefined; } if (!defined(result)) { result = new EarthOrientationParametersSample(0.0, 0.0, 0.0, 0.0, 0.0); } if (this._samples.length === 0) { result.xPoleWander = 0.0; result.yPoleWander = 0.0; result.xPoleOffset = 0.0; result.yPoleOffset = 0.0; result.ut1MinusUtc = 0.0; return result; } var dates = this._dates; var lastIndex = this._lastIndex; var before = 0; var after = 0; if (defined(lastIndex)) { var previousIndexDate = dates[lastIndex]; var nextIndexDate = dates[lastIndex + 1]; var isAfterPrevious = JulianDate.lessThanOrEquals(previousIndexDate, date); var isAfterLastSample = !defined(nextIndexDate); var isBeforeNext = isAfterLastSample || JulianDate.greaterThanOrEquals(nextIndexDate, date); if (isAfterPrevious && isBeforeNext) { before = lastIndex; if (!isAfterLastSample && nextIndexDate.equals(date)) { ++before; } after = before + 1; interpolate(this, dates, this._samples, date, before, after, result); return result; } } var index = binarySearch(dates, date, JulianDate.compare, this._dateColumn); if (index >= 0) { // If the next entry is the same date, use the later entry. This way, if two entries // describe the same moment, one before a leap second and the other after, then we will use // the post-leap second data. if (index < dates.length - 1 && dates[index + 1].equals(date)) { ++index; } before = index; after = index; } else { after = ~index; before = after - 1; // Use the first entry if the date requested is before the beginning of the data. if (before < 0) { before = 0; } } this._lastIndex = before; interpolate(this, dates, this._samples, date, before, after, result); return result; }; function compareLeapSecondDates(leapSecond, dateToFind) { return JulianDate.compare(leapSecond.julianDate, dateToFind); } function onDataReady(eop, eopData) { if (!defined(eopData.columnNames)) { eop._dataError = "Error in loaded EOP data: The columnNames property is required."; return; } if (!defined(eopData.samples)) { eop._dataError = "Error in loaded EOP data: The samples property is required."; return; } var dateColumn = eopData.columnNames.indexOf("modifiedJulianDateUtc"); var xPoleWanderRadiansColumn = eopData.columnNames.indexOf( "xPoleWanderRadians" ); var yPoleWanderRadiansColumn = eopData.columnNames.indexOf( "yPoleWanderRadians" ); var ut1MinusUtcSecondsColumn = eopData.columnNames.indexOf( "ut1MinusUtcSeconds" ); var xCelestialPoleOffsetRadiansColumn = eopData.columnNames.indexOf( "xCelestialPoleOffsetRadians" ); var yCelestialPoleOffsetRadiansColumn = eopData.columnNames.indexOf( "yCelestialPoleOffsetRadians" ); var taiMinusUtcSecondsColumn = eopData.columnNames.indexOf( "taiMinusUtcSeconds" ); if ( dateColumn < 0 || xPoleWanderRadiansColumn < 0 || yPoleWanderRadiansColumn < 0 || ut1MinusUtcSecondsColumn < 0 || xCelestialPoleOffsetRadiansColumn < 0 || yCelestialPoleOffsetRadiansColumn < 0 || taiMinusUtcSecondsColumn < 0 ) { eop._dataError = "Error in loaded EOP data: The columnNames property must include modifiedJulianDateUtc, xPoleWanderRadians, yPoleWanderRadians, ut1MinusUtcSeconds, xCelestialPoleOffsetRadians, yCelestialPoleOffsetRadians, and taiMinusUtcSeconds columns"; return; } var samples = (eop._samples = eopData.samples); var dates = (eop._dates = []); eop._dateColumn = dateColumn; eop._xPoleWanderRadiansColumn = xPoleWanderRadiansColumn; eop._yPoleWanderRadiansColumn = yPoleWanderRadiansColumn; eop._ut1MinusUtcSecondsColumn = ut1MinusUtcSecondsColumn; eop._xCelestialPoleOffsetRadiansColumn = xCelestialPoleOffsetRadiansColumn; eop._yCelestialPoleOffsetRadiansColumn = yCelestialPoleOffsetRadiansColumn; eop._taiMinusUtcSecondsColumn = taiMinusUtcSecondsColumn; eop._columnCount = eopData.columnNames.length; eop._lastIndex = undefined; var lastTaiMinusUtc; var addNewLeapSeconds = eop._addNewLeapSeconds; // Convert the ISO8601 dates to JulianDates. for (var i = 0, len = samples.length; i < len; i += eop._columnCount) { var mjd = samples[i + dateColumn]; var taiMinusUtc = samples[i + taiMinusUtcSecondsColumn]; var day = mjd + TimeConstants$1.MODIFIED_JULIAN_DATE_DIFFERENCE; var date = new JulianDate(day, taiMinusUtc, TimeStandard$1.TAI); dates.push(date); if (addNewLeapSeconds) { if (taiMinusUtc !== lastTaiMinusUtc && defined(lastTaiMinusUtc)) { // We crossed a leap second boundary, so add the leap second // if it does not already exist. var leapSeconds = JulianDate.leapSeconds; var leapSecondIndex = binarySearch( leapSeconds, date, compareLeapSecondDates ); if (leapSecondIndex < 0) { var leapSecond = new LeapSecond(date, taiMinusUtc); leapSeconds.splice(~leapSecondIndex, 0, leapSecond); } } lastTaiMinusUtc = taiMinusUtc; } } } function fillResultFromIndex(eop, samples, index, columnCount, result) { var start = index * columnCount; result.xPoleWander = samples[start + eop._xPoleWanderRadiansColumn]; result.yPoleWander = samples[start + eop._yPoleWanderRadiansColumn]; result.xPoleOffset = samples[start + eop._xCelestialPoleOffsetRadiansColumn]; result.yPoleOffset = samples[start + eop._yCelestialPoleOffsetRadiansColumn]; result.ut1MinusUtc = samples[start + eop._ut1MinusUtcSecondsColumn]; } function linearInterp(dx, y1, y2) { return y1 + dx * (y2 - y1); } function interpolate(eop, dates, samples, date, before, after, result) { var columnCount = eop._columnCount; // First check the bounds on the EOP data // If we are after the bounds of the data, return zeros. // The 'before' index should never be less than zero. if (after > dates.length - 1) { result.xPoleWander = 0; result.yPoleWander = 0; result.xPoleOffset = 0; result.yPoleOffset = 0; result.ut1MinusUtc = 0; return result; } var beforeDate = dates[before]; var afterDate = dates[after]; if (beforeDate.equals(afterDate) || date.equals(beforeDate)) { fillResultFromIndex(eop, samples, before, columnCount, result); return result; } else if (date.equals(afterDate)) { fillResultFromIndex(eop, samples, after, columnCount, result); return result; } var factor = JulianDate.secondsDifference(date, beforeDate) / JulianDate.secondsDifference(afterDate, beforeDate); var startBefore = before * columnCount; var startAfter = after * columnCount; // Handle UT1 leap second edge case var beforeUt1MinusUtc = samples[startBefore + eop._ut1MinusUtcSecondsColumn]; var afterUt1MinusUtc = samples[startAfter + eop._ut1MinusUtcSecondsColumn]; var offsetDifference = afterUt1MinusUtc - beforeUt1MinusUtc; if (offsetDifference > 0.5 || offsetDifference < -0.5) { // The absolute difference between the values is more than 0.5, so we may have // crossed a leap second. Check if this is the case and, if so, adjust the // afterValue to account for the leap second. This way, our interpolation will // produce reasonable results. var beforeTaiMinusUtc = samples[startBefore + eop._taiMinusUtcSecondsColumn]; var afterTaiMinusUtc = samples[startAfter + eop._taiMinusUtcSecondsColumn]; if (beforeTaiMinusUtc !== afterTaiMinusUtc) { if (afterDate.equals(date)) { // If we are at the end of the leap second interval, take the second value // Otherwise, the interpolation below will yield the wrong side of the // discontinuity // At the end of the leap second, we need to start accounting for the jump beforeUt1MinusUtc = afterUt1MinusUtc; } else { // Otherwise, remove the leap second so that the interpolation is correct afterUt1MinusUtc -= afterTaiMinusUtc - beforeTaiMinusUtc; } } } result.xPoleWander = linearInterp( factor, samples[startBefore + eop._xPoleWanderRadiansColumn], samples[startAfter + eop._xPoleWanderRadiansColumn] ); result.yPoleWander = linearInterp( factor, samples[startBefore + eop._yPoleWanderRadiansColumn], samples[startAfter + eop._yPoleWanderRadiansColumn] ); result.xPoleOffset = linearInterp( factor, samples[startBefore + eop._xCelestialPoleOffsetRadiansColumn], samples[startAfter + eop._xCelestialPoleOffsetRadiansColumn] ); result.yPoleOffset = linearInterp( factor, samples[startBefore + eop._yCelestialPoleOffsetRadiansColumn], samples[startAfter + eop._yCelestialPoleOffsetRadiansColumn] ); result.ut1MinusUtc = linearInterp( factor, beforeUt1MinusUtc, afterUt1MinusUtc ); return result; } function initialize$9(ellipsoid, x, y, z) { x = defaultValue(x, 0.0); y = defaultValue(y, 0.0); z = defaultValue(z, 0.0); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("x", x, 0.0); Check.typeOf.number.greaterThanOrEquals("y", y, 0.0); Check.typeOf.number.greaterThanOrEquals("z", z, 0.0); //>>includeEnd('debug'); ellipsoid._radii = new Cartesian3(x, y, z); ellipsoid._radiiSquared = new Cartesian3(x * x, y * y, z * z); ellipsoid._radiiToTheFourth = new Cartesian3( x * x * x * x, y * y * y * y, z * z * z * z ); ellipsoid._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 ); ellipsoid._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) ); ellipsoid._minimumRadius = Math.min(x, y, z); ellipsoid._maximumRadius = Math.max(x, y, z); ellipsoid._centerToleranceSquared = CesiumMath.EPSILON1; if (ellipsoid._radiiSquared.z !== 0) { ellipsoid._squaredXOverSquaredZ = ellipsoid._radiiSquared.x / ellipsoid._radiiSquared.z; } } /** * A quadratic surface defined in Cartesian coordinates by the equation * (x / a)^2 + (y / b)^2 + (z / c)^2 = 1. 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 * * @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 */ function Ellipsoid(x, y, z) { this._radii = undefined; this._radiiSquared = undefined; this._radiiToTheFourth = undefined; this._oneOverRadii = undefined; this._oneOverRadiiSquared = undefined; this._minimumRadius = undefined; this._maximumRadius = undefined; this._centerToleranceSquared = undefined; this._squaredXOverSquaredZ = undefined; initialize$9(this, x, y, z); } Object.defineProperties(Ellipsoid.prototype, { /** * Gets the radii of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Cartesian3} * @readonly */ radii: { get: function () { return this._radii; }, }, /** * Gets the squared radii of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Cartesian3} * @readonly */ radiiSquared: { get: function () { return this._radiiSquared; }, }, /** * Gets the radii of the ellipsoid raise to the fourth power. * @memberof Ellipsoid.prototype * @type {Cartesian3} * @readonly */ radiiToTheFourth: { get: function () { return this._radiiToTheFourth; }, }, /** * Gets one over the radii of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Cartesian3} * @readonly */ oneOverRadii: { get: function () { return this._oneOverRadii; }, }, /** * Gets one over the squared radii of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Cartesian3} * @readonly */ oneOverRadiiSquared: { get: function () { return this._oneOverRadiiSquared; }, }, /** * Gets the minimum radius of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Number} * @readonly */ minimumRadius: { get: function () { return this._minimumRadius; }, }, /** * Gets the maximum radius of the ellipsoid. * @memberof Ellipsoid.prototype * @type {Number} * @readonly */ maximumRadius: { get: function () { return this._maximumRadius; }, }, }); /** * Duplicates an Ellipsoid instance. * * @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} [cartesian=Cartesian3.ZERO] The ellipsoid's radius in the x, y, and z directions. * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new * instance should be created. * @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, result) { if (!defined(result)) { result = new Ellipsoid(); } if (!defined(cartesian)) { return result; } initialize$9(result, cartesian.x, cartesian.y, cartesian.z); return result; }; /** * An Ellipsoid instance initialized to the WGS84 standard. * * @type {Ellipsoid} * @constant */ Ellipsoid.WGS84 = Object.freeze( new Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793) ); /** * An Ellipsoid instance initialized to radii of (1.0, 1.0, 1.0). * * @type {Ellipsoid} * @constant */ Ellipsoid.UNIT_SPHERE = Object.freeze(new Ellipsoid(1.0, 1.0, 1.0)); /** * An Ellipsoid instance initialized to a sphere with the lunar radius. * * @type {Ellipsoid} * @constant */ Ellipsoid.MOON = Object.freeze( new Ellipsoid( CesiumMath.LUNAR_RADIUS, CesiumMath.LUNAR_RADIUS, CesiumMath.LUNAR_RADIUS ) ); /** * Duplicates an Ellipsoid instance. * * @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); }; /** * The number of elements used to pack the object into an array. * @type {Number} */ Ellipsoid.packedLength = Cartesian3.packedLength; /** * Stores the provided instance into the provided array. * * @param {Ellipsoid} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Ellipsoid.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._radii, array, startingIndex); return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Ellipsoid} [result] The object into which to store the result. * @returns {Ellipsoid} The modified result parameter or a new Ellipsoid instance if one was not provided. */ Ellipsoid.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var radii = Cartesian3.unpack(array, startingIndex); return Ellipsoid.fromCartesian3(radii, result); }; /** * Computes the unit vector directed from the center of this ellipsoid toward the provided Cartesian position. * @function * * @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. */ Ellipsoid.prototype.geocentricSurfaceNormal = Cartesian3.normalize; /** * Computes the normal of the plane tangent to the surface of the ellipsoid at the provided position. * * @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. */ Ellipsoid.prototype.geodeticSurfaceNormalCartographic = function ( cartographic, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartographic", cartographic); //>>includeEnd('debug'); 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. * * @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, or undefined if a normal cannot be found. */ Ellipsoid.prototype.geodeticSurfaceNormal = function (cartesian, result) { if ( Cartesian3.equalsEpsilon(cartesian, Cartesian3.ZERO, CesiumMath.EPSILON14) ) { return undefined; } if (!defined(result)) { result = new Cartesian3(); } 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. * * @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. * * @example * //Create a Cartographic and determine it's Cartesian representation on a WGS84 ellipsoid. * var position = new Cesium.Cartographic(Cesium.Math.toRadians(21), Cesium.Math.toRadians(78), 5000); * var cartesianPosition = Cesium.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); if (!defined(result)) { result = new Cartesian3(); } return Cartesian3.add(k, n, result); }; /** * Converts the provided array of cartographics to an array of Cartesians. * * @param {Cartographic[]} cartographics An array of cartographic positions. * @param {Cartesian3[]} [result] The object onto which to store the result. * @returns {Cartesian3[]} The modified result parameter or a new Array instance if none was provided. * * @example * //Convert an array of Cartographics and determine their Cartesian representation on a WGS84 ellipsoid. * var positions = [new Cesium.Cartographic(Cesium.Math.toRadians(21), Cesium.Math.toRadians(78), 0), * new Cesium.Cartographic(Cesium.Math.toRadians(21.321), Cesium.Math.toRadians(78.123), 100), * new Cesium.Cartographic(Cesium.Math.toRadians(21.645), Cesium.Math.toRadians(78.456), 250)]; * var cartesianPositions = Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(positions); */ Ellipsoid.prototype.cartographicArrayToCartesianArray = function ( cartographics, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartographics", cartographics); //>>includeEnd('debug') 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. * * @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. * * @example * //Create a Cartesian and determine it's Cartographic representation on a WGS84 ellipsoid. * var position = new Cesium.Cartesian3(17832.12, 83234.52, 952313.73); * var cartographicPosition = Cesium.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. * * @param {Cartesian3[]} cartesians An array of Cartesian positions. * @param {Cartographic[]} [result] The object onto which to store the result. * @returns {Cartographic[]} The modified result parameter or a new Array instance if none was provided. * * @example * //Create an array of Cartesians and determine their Cartographic representation on a WGS84 ellipsoid. * var positions = [new Cesium.Cartesian3(17832.12, 83234.52, 952313.73), * new Cesium.Cartesian3(17832.13, 83234.53, 952313.73), * new Cesium.Cartesian3(17832.14, 83234.54, 952313.73)] * var cartographicPositions = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions); */ Ellipsoid.prototype.cartesianArrayToCartographicArray = function ( cartesians, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); 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; }; /** * 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. * * @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. */ Ellipsoid.prototype.scaleToGeodeticSurface = function (cartesian, result) { return scaleToGeodeticSurface( cartesian, this._oneOverRadii, this._oneOverRadiiSquared, this._centerToleranceSquared, result ); }; /** * Scales the provided Cartesian position along the geocentric surface normal * so that it is on the surface of this 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. */ Ellipsoid.prototype.scaleToGeocentricSurface = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } 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#oneOverRadii}. * * @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 ) { if (!defined(result)) { result = new Cartesian3(); } 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#radii}. * * @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 ) { if (!defined(result)) { result = new Cartesian3(); } return Cartesian3.multiplyComponents(position, this._radii, result); }; /** * Compares this Ellipsoid against the provided Ellipsoid componentwise and returns * true if they are equal, false otherwise. * * @param {Ellipsoid} [right] The other Ellipsoid. * @returns {Boolean} true if they are equal, false 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)'. * * @returns {String} A string representing this ellipsoid in the format '(radii.x, radii.y, radii.z)'. */ Ellipsoid.prototype.toString = function () { return this._radii.toString(); }; /** * Computes a point which is the intersection of the surface normal with the z-axis. * * @param {Cartesian3} position the position. must be on the surface of the ellipsoid. * @param {Number} [buffer = 0.0] A buffer to subtract from the ellipsoid size when checking if the point is inside the ellipsoid. * In earth case, with common earth datums, there is no need for this buffer since the intersection point is always (relatively) very close to the center. * In WGS84 datum, intersection point is at max z = +-42841.31151331382 (0.673% of z-axis). * Intersection point could be outside the ellipsoid if the ratio of MajorAxis / AxisOfRotation is bigger than the square root of 2 * @param {Cartesian3} [result] The cartesian to which to copy the result, or undefined to create and * return a new instance. * @returns {Cartesian3 | undefined} the intersection point if it's inside the ellipsoid, undefined otherwise * * @exception {DeveloperError} position is required. * @exception {DeveloperError} Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y). * @exception {DeveloperError} Ellipsoid.radii.z must be greater than 0. */ Ellipsoid.prototype.getSurfaceNormalIntersectionWithZAxis = function ( position, buffer, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("position", position); if ( !CesiumMath.equalsEpsilon( this._radii.x, this._radii.y, CesiumMath.EPSILON15 ) ) { throw new DeveloperError( "Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)" ); } Check.typeOf.number.greaterThan("Ellipsoid.radii.z", this._radii.z, 0); //>>includeEnd('debug'); buffer = defaultValue(buffer, 0.0); var squaredXOverSquaredZ = this._squaredXOverSquaredZ; if (!defined(result)) { result = new Cartesian3(); } result.x = 0.0; result.y = 0.0; result.z = position.z * (1 - squaredXOverSquaredZ); if (Math.abs(result.z) >= this._radii.z - buffer) { return undefined; } return result; }; var abscissas = [ 0.14887433898163, 0.43339539412925, 0.67940956829902, 0.86506336668898, 0.97390652851717, 0.0, ]; var weights = [ 0.29552422471475, 0.26926671930999, 0.21908636251598, 0.14945134915058, 0.066671344308684, 0.0, ]; /** * Compute the 10th order Gauss-Legendre Quadrature of the given definite integral. * * @param {Number} a The lower bound for the integration. * @param {Number} b The upper bound for the integration. * @param {Ellipsoid~RealValuedScalarFunction} func The function to integrate. * @returns {Number} The value of the integral of the given function over the given domain. * * @private */ function gaussLegendreQuadrature(a, b, func) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("a", a); Check.typeOf.number("b", b); Check.typeOf.func("func", func); //>>includeEnd('debug'); // The range is half of the normal range since the five weights add to one (ten weights add to two). // The values of the abscissas are multiplied by two to account for this. var xMean = 0.5 * (b + a); var xRange = 0.5 * (b - a); var sum = 0.0; for (var i = 0; i < 5; i++) { var dx = xRange * abscissas[i]; sum += weights[i] * (func(xMean + dx) + func(xMean - dx)); } // Scale the sum to the range of x. sum *= xRange; return sum; } /** * A real valued scalar function. * @callback Ellipsoid~RealValuedScalarFunction * * @param {Number} x The value used to evaluate the function. * @returns {Number} The value of the function at x. * * @private */ /** * Computes an approximation of the surface area of a rectangle on the surface of an ellipsoid using * Gauss-Legendre 10th order quadrature. * * @param {Rectangle} rectangle The rectangle used for computing the surface area. * @returns {Number} The approximate area of the rectangle on the surface of this ellipsoid. */ Ellipsoid.prototype.surfaceArea = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var minLongitude = rectangle.west; var maxLongitude = rectangle.east; var minLatitude = rectangle.south; var maxLatitude = rectangle.north; while (maxLongitude < minLongitude) { maxLongitude += CesiumMath.TWO_PI; } var radiiSquared = this._radiiSquared; var a2 = radiiSquared.x; var b2 = radiiSquared.y; var c2 = radiiSquared.z; var a2b2 = a2 * b2; return gaussLegendreQuadrature(minLatitude, maxLatitude, function (lat) { // phi represents the angle measured from the north pole // sin(phi) = sin(pi / 2 - lat) = cos(lat), cos(phi) is similar var sinPhi = Math.cos(lat); var cosPhi = Math.sin(lat); return ( Math.cos(lat) * gaussLegendreQuadrature(minLongitude, maxLongitude, function (lon) { var cosTheta = Math.cos(lon); var sinTheta = Math.sin(lon); return Math.sqrt( a2b2 * cosPhi * cosPhi + c2 * (b2 * cosTheta * cosTheta + a2 * sinTheta * sinTheta) * sinPhi * sinPhi ); }) ); }); }; /** * A rotation expressed as a heading, pitch, and roll. Heading is the rotation about the * negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about * the positive x axis. * @alias HeadingPitchRoll * @constructor * * @param {Number} [heading=0.0] The heading component in radians. * @param {Number} [pitch=0.0] The pitch component in radians. * @param {Number} [roll=0.0] The roll component in radians. */ function HeadingPitchRoll(heading, pitch, roll) { /** * Gets or sets the heading. * @type {Number} * @default 0.0 */ this.heading = defaultValue(heading, 0.0); /** * Gets or sets the pitch. * @type {Number} * @default 0.0 */ this.pitch = defaultValue(pitch, 0.0); /** * Gets or sets the roll. * @type {Number} * @default 0.0 */ this.roll = defaultValue(roll, 0.0); } /** * Computes the heading, pitch and roll from a quaternion (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles ) * * @param {Quaternion} quaternion The quaternion from which to retrieve heading, pitch, and roll, all expressed in radians. * @param {HeadingPitchRoll} [result] The object in which to store the result. If not provided, a new instance is created and returned. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. */ HeadingPitchRoll.fromQuaternion = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); if (!defined(quaternion)) { throw new DeveloperError("quaternion is required"); } //>>includeEnd('debug'); if (!defined(result)) { result = new HeadingPitchRoll(); } var test = 2 * (quaternion.w * quaternion.y - quaternion.z * quaternion.x); var denominatorRoll = 1 - 2 * (quaternion.x * quaternion.x + quaternion.y * quaternion.y); var numeratorRoll = 2 * (quaternion.w * quaternion.x + quaternion.y * quaternion.z); var denominatorHeading = 1 - 2 * (quaternion.y * quaternion.y + quaternion.z * quaternion.z); var numeratorHeading = 2 * (quaternion.w * quaternion.z + quaternion.x * quaternion.y); result.heading = -Math.atan2(numeratorHeading, denominatorHeading); result.roll = Math.atan2(numeratorRoll, denominatorRoll); result.pitch = -CesiumMath.asinClamped(test); return result; }; /** * Returns a new HeadingPitchRoll instance from angles given in degrees. * * @param {Number} heading the heading in degrees * @param {Number} pitch the pitch in degrees * @param {Number} roll the heading in degrees * @param {HeadingPitchRoll} [result] The object in which to store the result. If not provided, a new instance is created and returned. * @returns {HeadingPitchRoll} A new HeadingPitchRoll instance */ HeadingPitchRoll.fromDegrees = function (heading, pitch, roll, result) { //>>includeStart('debug', pragmas.debug); if (!defined(heading)) { throw new DeveloperError("heading is required"); } if (!defined(pitch)) { throw new DeveloperError("pitch is required"); } if (!defined(roll)) { throw new DeveloperError("roll is required"); } //>>includeEnd('debug'); if (!defined(result)) { result = new HeadingPitchRoll(); } result.heading = heading * CesiumMath.RADIANS_PER_DEGREE; result.pitch = pitch * CesiumMath.RADIANS_PER_DEGREE; result.roll = roll * CesiumMath.RADIANS_PER_DEGREE; return result; }; /** * Duplicates a HeadingPitchRoll instance. * * @param {HeadingPitchRoll} headingPitchRoll The HeadingPitchRoll to duplicate. * @param {HeadingPitchRoll} [result] The object onto which to store the result. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. (Returns undefined if headingPitchRoll is undefined) */ HeadingPitchRoll.clone = function (headingPitchRoll, result) { if (!defined(headingPitchRoll)) { return undefined; } if (!defined(result)) { return new HeadingPitchRoll( headingPitchRoll.heading, headingPitchRoll.pitch, headingPitchRoll.roll ); } result.heading = headingPitchRoll.heading; result.pitch = headingPitchRoll.pitch; result.roll = headingPitchRoll.roll; return result; }; /** * Compares the provided HeadingPitchRolls componentwise and returns * true if they are equal, false otherwise. * * @param {HeadingPitchRoll} [left] The first HeadingPitchRoll. * @param {HeadingPitchRoll} [right] The second HeadingPitchRoll. * @returns {Boolean} true if left and right are equal, false otherwise. */ HeadingPitchRoll.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.heading === right.heading && left.pitch === right.pitch && left.roll === right.roll) ); }; /** * Compares the provided HeadingPitchRolls componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {HeadingPitchRoll} [left] The first HeadingPitchRoll. * @param {HeadingPitchRoll} [right] The second HeadingPitchRoll. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ HeadingPitchRoll.equalsEpsilon = function ( left, right, relativeEpsilon, absoluteEpsilon ) { return ( left === right || (defined(left) && defined(right) && CesiumMath.equalsEpsilon( left.heading, right.heading, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.pitch, right.pitch, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( left.roll, right.roll, relativeEpsilon, absoluteEpsilon )) ); }; /** * Duplicates this HeadingPitchRoll instance. * * @param {HeadingPitchRoll} [result] The object onto which to store the result. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. */ HeadingPitchRoll.prototype.clone = function (result) { return HeadingPitchRoll.clone(this, result); }; /** * Compares this HeadingPitchRoll against the provided HeadingPitchRoll componentwise and returns * true if they are equal, false otherwise. * * @param {HeadingPitchRoll} [right] The right hand side HeadingPitchRoll. * @returns {Boolean} true if they are equal, false otherwise. */ HeadingPitchRoll.prototype.equals = function (right) { return HeadingPitchRoll.equals(this, right); }; /** * Compares this HeadingPitchRoll against the provided HeadingPitchRoll componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {HeadingPitchRoll} [right] The right hand side HeadingPitchRoll. * @param {Number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ HeadingPitchRoll.prototype.equalsEpsilon = function ( right, relativeEpsilon, absoluteEpsilon ) { return HeadingPitchRoll.equalsEpsilon( this, right, relativeEpsilon, absoluteEpsilon ); }; /** * Creates a string representing this HeadingPitchRoll in the format '(heading, pitch, roll)' in radians. * * @returns {String} A string representing the provided HeadingPitchRoll in the format '(heading, pitch, roll)'. */ HeadingPitchRoll.prototype.toString = function () { return "(" + this.heading + ", " + this.pitch + ", " + this.roll + ")"; }; /*global CESIUM_BASE_URL*/ var cesiumScriptRegex = /((?:.*\/)|^)Cesium\.js(?:\?|\#|$)/; function getBaseUrlFromCesiumScript() { var scripts = document.getElementsByTagName("script"); for (var i = 0, len = scripts.length; i < len; ++i) { var src = scripts[i].getAttribute("src"); var result = cesiumScriptRegex.exec(src); if (result !== null) { return result[1]; } } return undefined; } var a; function tryMakeAbsolute(url) { if (typeof document === "undefined") { //Node.js and Web Workers. In both cases, the URL will already be absolute. return url; } if (!defined(a)) { a = document.createElement("a"); } a.href = url; // IE only absolutizes href on get, not set // eslint-disable-next-line no-self-assign a.href = a.href; return a.href; } var baseResource; function getCesiumBaseUrl() { if (defined(baseResource)) { return baseResource; } var baseUrlString; if (typeof CESIUM_BASE_URL !== "undefined") { baseUrlString = CESIUM_BASE_URL; } else if ( typeof define === "object" && defined(define.amd) && !define.amd.toUrlUndefined && defined(require.toUrl) ) { baseUrlString = getAbsoluteUri( "..", buildModuleUrl("Core/buildModuleUrl.js") ); } else { baseUrlString = getBaseUrlFromCesiumScript(); } //>>includeStart('debug', pragmas.debug); if (!defined(baseUrlString)) { throw new DeveloperError( "Unable to determine Cesium base URL automatically, try defining a global variable called CESIUM_BASE_URL." ); } //>>includeEnd('debug'); baseResource = new Resource({ url: tryMakeAbsolute(baseUrlString), }); baseResource.appendForwardSlash(); return baseResource; } function buildModuleUrlFromRequireToUrl(moduleID) { //moduleID will be non-relative, so require it relative to this module, in Core. return tryMakeAbsolute(require.toUrl("../" + moduleID)); } function buildModuleUrlFromBaseUrl(moduleID) { var resource = getCesiumBaseUrl().getDerivedResource({ url: moduleID, }); return resource.url; } var implementation$2; /** * Given a relative URL under the Cesium base URL, returns an absolute URL. * @function * * @param {String} relativeUrl The relative path. * @returns {String} The absolutely URL representation of the provided path. * * @example * var viewer = new Cesium.Viewer("cesiumContainer", { * imageryProvider: new Cesium.TileMapServiceImageryProvider({ * url: Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"), * }), * baseLayerPicker: false, * }); */ function buildModuleUrl(relativeUrl) { if (!defined(implementation$2)) { //select implementation if ( typeof define === "object" && defined(define.amd) && !define.amd.toUrlUndefined && defined(require.toUrl) ) { implementation$2 = buildModuleUrlFromRequireToUrl; } else { implementation$2 = buildModuleUrlFromBaseUrl; } } var url = implementation$2(relativeUrl); return url; } // exposed for testing buildModuleUrl._cesiumScriptRegex = cesiumScriptRegex; buildModuleUrl._buildModuleUrlFromBaseUrl = buildModuleUrlFromBaseUrl; buildModuleUrl._clearBaseResource = function () { baseResource = undefined; }; /** * Sets the base URL for resolving modules. * @param {String} value The new base URL. */ buildModuleUrl.setBaseUrl = function (value) { baseResource = Resource.DEFAULT.getDerivedResource({ url: value, }); }; /** * Gets the base URL for resolving modules. */ buildModuleUrl.getCesiumBaseUrl = getCesiumBaseUrl; /** * An IAU 2006 XYS value sampled at a particular time. * * @alias Iau2006XysSample * @constructor * * @param {Number} x The X value. * @param {Number} y The Y value. * @param {Number} s The S value. * * @private */ function Iau2006XysSample(x, y, s) { /** * The X value. * @type {Number} */ this.x = x; /** * The Y value. * @type {Number} */ this.y = y; /** * The S value. * @type {Number} */ this.s = s; } /** * A set of IAU2006 XYS data that is used to evaluate the transformation between the International * Celestial Reference Frame (ICRF) and the International Terrestrial Reference Frame (ITRF). * * @alias Iau2006XysData * @constructor * * @param {Object} [options] Object with the following properties: * @param {Resource|String} [options.xysFileUrlTemplate='Assets/IAU2006_XYS/IAU2006_XYS_{0}.json'] A template URL for obtaining the XYS data. In the template, * `{0}` will be replaced with the file index. * @param {Number} [options.interpolationOrder=9] The order of interpolation to perform on the XYS data. * @param {Number} [options.sampleZeroJulianEphemerisDate=2442396.5] The Julian ephemeris date (JED) of the * first XYS sample. * @param {Number} [options.stepSizeDays=1.0] The step size, in days, between successive XYS samples. * @param {Number} [options.samplesPerXysFile=1000] The number of samples in each XYS file. * @param {Number} [options.totalSamples=27426] The total number of samples in all XYS files. * * @private */ function Iau2006XysData(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._xysFileUrlTemplate = Resource.createIfNeeded( options.xysFileUrlTemplate ); this._interpolationOrder = defaultValue(options.interpolationOrder, 9); this._sampleZeroJulianEphemerisDate = defaultValue( options.sampleZeroJulianEphemerisDate, 2442396.5 ); this._sampleZeroDateTT = new JulianDate( this._sampleZeroJulianEphemerisDate, 0.0, TimeStandard$1.TAI ); this._stepSizeDays = defaultValue(options.stepSizeDays, 1.0); this._samplesPerXysFile = defaultValue(options.samplesPerXysFile, 1000); this._totalSamples = defaultValue(options.totalSamples, 27426); this._samples = new Array(this._totalSamples * 3); this._chunkDownloadsInProgress = []; var order = this._interpolationOrder; // Compute denominators and X values for interpolation. var denom = (this._denominators = new Array(order + 1)); var xTable = (this._xTable = new Array(order + 1)); var stepN = Math.pow(this._stepSizeDays, order); for (var i = 0; i <= order; ++i) { denom[i] = stepN; xTable[i] = i * this._stepSizeDays; for (var j = 0; j <= order; ++j) { if (j !== i) { denom[i] *= i - j; } } denom[i] = 1.0 / denom[i]; } // Allocate scratch arrays for interpolation. this._work = new Array(order + 1); this._coef = new Array(order + 1); } var julianDateScratch$1 = new JulianDate(0, 0.0, TimeStandard$1.TAI); function getDaysSinceEpoch(xys, dayTT, secondTT) { var dateTT = julianDateScratch$1; dateTT.dayNumber = dayTT; dateTT.secondsOfDay = secondTT; return JulianDate.daysDifference(dateTT, xys._sampleZeroDateTT); } /** * Preloads XYS data for a specified date range. * * @param {Number} startDayTT The Julian day number of the beginning of the interval to preload, expressed in * the Terrestrial Time (TT) time standard. * @param {Number} startSecondTT The seconds past noon of the beginning of the interval to preload, expressed in * the Terrestrial Time (TT) time standard. * @param {Number} stopDayTT The Julian day number of the end of the interval to preload, expressed in * the Terrestrial Time (TT) time standard. * @param {Number} stopSecondTT The seconds past noon of the end of the interval to preload, expressed in * the Terrestrial Time (TT) time standard. * @returns {Promise} A promise that, when resolved, indicates that the requested interval has been * preloaded. */ Iau2006XysData.prototype.preload = function ( startDayTT, startSecondTT, stopDayTT, stopSecondTT ) { var startDaysSinceEpoch = getDaysSinceEpoch(this, startDayTT, startSecondTT); var stopDaysSinceEpoch = getDaysSinceEpoch(this, stopDayTT, stopSecondTT); var startIndex = (startDaysSinceEpoch / this._stepSizeDays - this._interpolationOrder / 2) | 0; if (startIndex < 0) { startIndex = 0; } var stopIndex = (stopDaysSinceEpoch / this._stepSizeDays - this._interpolationOrder / 2) | (0 + this._interpolationOrder); if (stopIndex >= this._totalSamples) { stopIndex = this._totalSamples - 1; } var startChunk = (startIndex / this._samplesPerXysFile) | 0; var stopChunk = (stopIndex / this._samplesPerXysFile) | 0; var promises = []; for (var i = startChunk; i <= stopChunk; ++i) { promises.push(requestXysChunk(this, i)); } return when.all(promises); }; /** * Computes the XYS values for a given date by interpolating. If the required data is not yet downloaded, * this method will return undefined. * * @param {Number} dayTT The Julian day number for which to compute the XYS value, expressed in * the Terrestrial Time (TT) time standard. * @param {Number} secondTT The seconds past noon of the date for which to compute the XYS value, expressed in * the Terrestrial Time (TT) time standard. * @param {Iau2006XysSample} [result] The instance to which to copy the interpolated result. If this parameter * is undefined, a new instance is allocated and returned. * @returns {Iau2006XysSample} The interpolated XYS values, or undefined if the required data for this * computation has not yet been downloaded. * * @see Iau2006XysData#preload */ Iau2006XysData.prototype.computeXysRadians = function ( dayTT, secondTT, result ) { var daysSinceEpoch = getDaysSinceEpoch(this, dayTT, secondTT); if (daysSinceEpoch < 0.0) { // Can't evaluate prior to the epoch of the data. return undefined; } var centerIndex = (daysSinceEpoch / this._stepSizeDays) | 0; if (centerIndex >= this._totalSamples) { // Can't evaluate after the last sample in the data. return undefined; } var degree = this._interpolationOrder; var firstIndex = centerIndex - ((degree / 2) | 0); if (firstIndex < 0) { firstIndex = 0; } var lastIndex = firstIndex + degree; if (lastIndex >= this._totalSamples) { lastIndex = this._totalSamples - 1; firstIndex = lastIndex - degree; if (firstIndex < 0) { firstIndex = 0; } } // Are all the samples we need present? // We can assume so if the first and last are present var isDataMissing = false; var samples = this._samples; if (!defined(samples[firstIndex * 3])) { requestXysChunk(this, (firstIndex / this._samplesPerXysFile) | 0); isDataMissing = true; } if (!defined(samples[lastIndex * 3])) { requestXysChunk(this, (lastIndex / this._samplesPerXysFile) | 0); isDataMissing = true; } if (isDataMissing) { return undefined; } if (!defined(result)) { result = new Iau2006XysSample(0.0, 0.0, 0.0); } else { result.x = 0.0; result.y = 0.0; result.s = 0.0; } var x = daysSinceEpoch - firstIndex * this._stepSizeDays; var work = this._work; var denom = this._denominators; var coef = this._coef; var xTable = this._xTable; var i, j; for (i = 0; i <= degree; ++i) { work[i] = x - xTable[i]; } for (i = 0; i <= degree; ++i) { coef[i] = 1.0; for (j = 0; j <= degree; ++j) { if (j !== i) { coef[i] *= work[j]; } } coef[i] *= denom[i]; var sampleIndex = (firstIndex + i) * 3; result.x += coef[i] * samples[sampleIndex++]; result.y += coef[i] * samples[sampleIndex++]; result.s += coef[i] * samples[sampleIndex]; } return result; }; function requestXysChunk(xysData, chunkIndex) { if (xysData._chunkDownloadsInProgress[chunkIndex]) { // Chunk has already been requested. return xysData._chunkDownloadsInProgress[chunkIndex]; } var deferred = when.defer(); xysData._chunkDownloadsInProgress[chunkIndex] = deferred; var chunkUrl; var xysFileUrlTemplate = xysData._xysFileUrlTemplate; if (defined(xysFileUrlTemplate)) { chunkUrl = xysFileUrlTemplate.getDerivedResource({ templateValues: { 0: chunkIndex, }, }); } else { chunkUrl = new Resource({ url: buildModuleUrl( "Assets/IAU2006_XYS/IAU2006_XYS_" + chunkIndex + ".json" ), }); } when(chunkUrl.fetchJson(), function (chunk) { xysData._chunkDownloadsInProgress[chunkIndex] = false; var samples = xysData._samples; var newSamples = chunk.samples; var startIndex = chunkIndex * xysData._samplesPerXysFile * 3; for (var i = 0, len = newSamples.length; i < len; ++i) { samples[startIndex + i] = newSamples[i]; } deferred.resolve(); }); return deferred.promise; } /** * A 3x3 matrix, indexable as a column-major order array. * Constructor parameters are in row-major order for code readability. * @alias Matrix3 * @constructor * @implements {ArrayLike} * * @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.fromColumnMajorArray * @see Matrix3.fromRowMajorArray * @see Matrix3.fromQuaternion * @see Matrix3.fromScale * @see Matrix3.fromUniformScale * @see Matrix2 * @see Matrix4 */ function Matrix3( 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); } /** * The number of elements used to pack the object into an array. * @type {Number} */ Matrix3.packedLength = 9; /** * Stores the provided instance into the provided array. * * @param {Matrix3} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Matrix3.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value[0]; array[startingIndex++] = value[1]; array[startingIndex++] = value[2]; array[startingIndex++] = value[3]; array[startingIndex++] = value[4]; array[startingIndex++] = value[5]; array[startingIndex++] = value[6]; array[startingIndex++] = value[7]; array[startingIndex++] = value[8]; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Matrix3} [result] The object into which to store the result. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. */ Matrix3.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Matrix3(); } result[0] = array[startingIndex++]; result[1] = array[startingIndex++]; result[2] = array[startingIndex++]; result[3] = array[startingIndex++]; result[4] = array[startingIndex++]; result[5] = array[startingIndex++]; result[6] = array[startingIndex++]; result[7] = array[startingIndex++]; result[8] = array[startingIndex++]; return result; }; /** * Duplicates a Matrix3 instance. * * @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 (matrix, result) { if (!defined(matrix)) { return undefined; } 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; }; /** * Creates a Matrix3 from 9 consecutive elements in an array. * * @param {Number[]} 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. * * @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 = Cesium.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 = Cesium.Matrix3.fromArray(v2, 2); */ Matrix3.fromArray = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); 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]; return result; }; /** * Creates a Matrix3 instance from a column-major order array. * * @param {Number[]} 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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. */ Matrix3.fromColumnMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); return Matrix3.clone(values, result); }; /** * Creates a Matrix3 instance from a row-major order array. * The resulting matrix will be in column-major order. * * @param {Number[]} 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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. */ Matrix3.fromRowMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); 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. * * @param {Quaternion} quaternion the quaternion to use. * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. * @returns {Matrix3} The 3x3 rotation matrix from this quaternion. */ Matrix3.fromQuaternion = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); //>>includeEnd('debug'); 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 3x3 rotation matrix from the provided headingPitchRoll. (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles ) * * @param {HeadingPitchRoll} headingPitchRoll the headingPitchRoll to use. * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. * @returns {Matrix3} The 3x3 rotation matrix from this headingPitchRoll. */ Matrix3.fromHeadingPitchRoll = function (headingPitchRoll, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("headingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); var cosTheta = Math.cos(-headingPitchRoll.pitch); var cosPsi = Math.cos(-headingPitchRoll.heading); var cosPhi = Math.cos(headingPitchRoll.roll); var sinTheta = Math.sin(-headingPitchRoll.pitch); var sinPsi = Math.sin(-headingPitchRoll.heading); var sinPhi = Math.sin(headingPitchRoll.roll); var m00 = cosTheta * cosPsi; var m01 = -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi; var m02 = sinPhi * sinPsi + cosPhi * sinTheta * cosPsi; var m10 = cosTheta * sinPsi; var m11 = cosPhi * cosPsi + sinPhi * sinTheta * sinPsi; var m12 = -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi; var m20 = -sinTheta; var m21 = sinPhi * cosTheta; var m22 = cosPhi * cosTheta; if (!defined(result)) { return new Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22); } result[0] = m00; result[1] = m10; result[2] = m20; result[3] = m01; result[4] = m11; result[5] = m21; result[6] = m02; result[7] = m12; result[8] = m22; return result; }; /** * Computes a Matrix3 instance representing a non-uniform scale. * * @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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Creates * // [7.0, 0.0, 0.0] * // [0.0, 8.0, 0.0] * // [0.0, 0.0, 9.0] * var m = Cesium.Matrix3.fromScale(new Cesium.Cartesian3(7.0, 8.0, 9.0)); */ Matrix3.fromScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("scale", scale); //>>includeEnd('debug'); 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. * * @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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Creates * // [2.0, 0.0, 0.0] * // [0.0, 2.0, 0.0] * // [0.0, 0.0, 2.0] * var m = Cesium.Matrix3.fromUniformScale(2.0); */ Matrix3.fromUniformScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("scale", scale); //>>includeEnd('debug'); 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; }; /** * Computes a Matrix3 instance representing the cross product equivalent matrix of a Cartesian3 vector. * * @param {Cartesian3} vector the vector on the left hand side of the cross product operation. * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. * @returns {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Creates * // [0.0, -9.0, 8.0] * // [9.0, 0.0, -7.0] * // [-8.0, 7.0, 0.0] * var m = Cesium.Matrix3.fromCrossProduct(new Cesium.Cartesian3(7.0, 8.0, 9.0)); */ Matrix3.fromCrossProduct = function (vector, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("vector", vector); //>>includeEnd('debug'); if (!defined(result)) { return new Matrix3( 0.0, -vector.z, vector.y, vector.z, 0.0, -vector.x, -vector.y, vector.x, 0.0 ); } result[0] = 0.0; result[1] = vector.z; result[2] = -vector.y; result[3] = -vector.z; result[4] = 0.0; result[5] = vector.x; result[6] = vector.y; result[7] = -vector.x; result[8] = 0.0; 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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Rotate a point 45 degrees counterclockwise around the x-axis. * var p = new Cesium.Cartesian3(5, 6, 7); * var m = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(45.0)); * var rotated = Cesium.Matrix3.multiplyByVector(m, p, new Cesium.Cartesian3()); */ Matrix3.fromRotationX = function (angle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("angle", angle); //>>includeEnd('debug'); 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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Rotate a point 45 degrees counterclockwise around the y-axis. * var p = new Cesium.Cartesian3(5, 6, 7); * var m = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(45.0)); * var rotated = Cesium.Matrix3.multiplyByVector(m, p, new Cesium.Cartesian3()); */ Matrix3.fromRotationY = function (angle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("angle", angle); //>>includeEnd('debug'); 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 {Matrix3} The modified result parameter, or a new Matrix3 instance if one was not provided. * * @example * // Rotate a point 45 degrees counterclockwise around the z-axis. * var p = new Cesium.Cartesian3(5, 6, 7); * var m = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(45.0)); * var rotated = Cesium.Matrix3.multiplyByVector(m, p, new Cesium.Cartesian3()); */ Matrix3.fromRotationZ = function (angle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("angle", angle); //>>includeEnd('debug'); 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. * * @param {Matrix3} matrix The matrix to use.. * @param {Number[]} [result] The Array onto which to store the result. * @returns {Number[]} The modified Array parameter or a new Array instance if one was not provided. */ Matrix3.toArray = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); 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. * * @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 must be 0, 1, or 2. * @exception {DeveloperError} column must be 0, 1, or 2. * * @example * var myMatrix = new Cesium.Matrix3(); * var column1Row0Index = Cesium.Matrix3.getElementIndex(1, 0); * var column1Row0 = myMatrix[column1Row0Index] * myMatrix[column1Row0Index] = 10.0; */ Matrix3.getElementIndex = function (column, row) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("row", row, 0); Check.typeOf.number.lessThanOrEquals("row", row, 2); Check.typeOf.number.greaterThanOrEquals("column", column, 0); Check.typeOf.number.lessThanOrEquals("column", column, 2); //>>includeEnd('debug'); return column * 3 + row; }; /** * Retrieves a copy of the matrix column at the provided index as a Cartesian3 instance. * * @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. * * @exception {DeveloperError} index must be 0, 1, or 2. */ Matrix3.getColumn = function (matrix, index, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 2); Check.typeOf.object("result", result); //>>includeEnd('debug'); var startIndex = index * 3; var x = matrix[startIndex]; var y = matrix[startIndex + 1]; var z = matrix[startIndex + 2]; 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. * * @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 {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. * * @exception {DeveloperError} index must be 0, 1, or 2. */ Matrix3.setColumn = function (matrix, index, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 2); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. * * @exception {DeveloperError} index must be 0, 1, or 2. */ Matrix3.getRow = function (matrix, index, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 2); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = matrix[index]; var y = matrix[index + 3]; var z = matrix[index + 6]; 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. * * @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 {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. * * @exception {DeveloperError} index must be 0, 1, or 2. */ Matrix3.setRow = function (matrix, index, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 2); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result = Matrix3.clone(matrix, result); result[index] = cartesian.x; result[index + 3] = cartesian.y; result[index + 6] = cartesian.z; return result; }; var scratchColumn$2 = new Cartesian3(); /** * Extracts the non-uniform scale assuming the matrix is an affine transformation. * * @param {Matrix3} matrix The matrix. * @param {Cartesian3} result The object onto which to store the result. * @returns {Cartesian3} The modified result parameter. */ Matrix3.getScale = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = Cartesian3.magnitude( Cartesian3.fromElements(matrix[0], matrix[1], matrix[2], scratchColumn$2) ); result.y = Cartesian3.magnitude( Cartesian3.fromElements(matrix[3], matrix[4], matrix[5], scratchColumn$2) ); result.z = Cartesian3.magnitude( Cartesian3.fromElements(matrix[6], matrix[7], matrix[8], scratchColumn$2) ); return result; }; var scratchScale$8 = new Cartesian3(); /** * Computes the maximum scale assuming the matrix is an affine transformation. * The maximum scale is the maximum length of the column vectors. * * @param {Matrix3} matrix The matrix. * @returns {Number} The maximum scale. */ Matrix3.getMaximumScale = function (matrix) { Matrix3.getScale(matrix, scratchScale$8); return Cartesian3.maximumComponent(scratchScale$8); }; /** * Computes the product of two matrices. * * @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. */ Matrix3.multiply = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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]; 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 sum of two matrices. * * @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. */ Matrix3.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] + right[0]; result[1] = left[1] + right[1]; result[2] = left[2] + right[2]; result[3] = left[3] + right[3]; result[4] = left[4] + right[4]; result[5] = left[5] + right[5]; result[6] = left[6] + right[6]; result[7] = left[7] + right[7]; result[8] = left[8] + right[8]; return result; }; /** * Computes the difference of two matrices. * * @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. */ Matrix3.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] - right[0]; result[1] = left[1] - right[1]; result[2] = left[2] - right[2]; result[3] = left[3] - right[3]; result[4] = left[4] - right[4]; result[5] = left[5] - right[5]; result[6] = left[6] - right[6]; result[7] = left[7] - right[7]; result[8] = left[8] - right[8]; return result; }; /** * Computes the product of a matrix and a column vector. * * @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. */ Matrix3.multiplyByVector = function (matrix, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; result.x = x; result.y = y; result.z = z; return result; }; /** * Computes the product of a matrix and a scalar. * * @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. */ Matrix3.multiplyByScalar = function (matrix, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; }; /** * Computes the product of a matrix times a (non-uniform) scale, as if the scale were a scale matrix. * * @param {Matrix3} matrix The matrix on the left-hand side. * @param {Cartesian3} scale The non-uniform scale on the right-hand side. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. * * * @example * // Instead of Cesium.Matrix3.multiply(m, Cesium.Matrix3.fromScale(scale), m); * Cesium.Matrix3.multiplyByScale(m, scale, m); * * @see Matrix3.fromScale * @see Matrix3.multiplyByUniformScale */ Matrix3.multiplyByScale = function (matrix, scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("scale", scale); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = matrix[0] * scale.x; result[1] = matrix[1] * scale.x; result[2] = matrix[2] * scale.x; result[3] = matrix[3] * scale.y; result[4] = matrix[4] * scale.y; result[5] = matrix[5] * scale.y; result[6] = matrix[6] * scale.z; result[7] = matrix[7] * scale.z; result[8] = matrix[8] * scale.z; return result; }; /** * Creates a negated copy of the provided matrix. * * @param {Matrix3} matrix The matrix to negate. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. */ Matrix3.negate = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Matrix3} matrix The matrix to transpose. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. */ Matrix3.transpose = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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]; 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; }; var UNIT = new Cartesian3(1, 1, 1); /** * Extracts the rotation assuming the matrix is an affine transformation. * * @param {Matrix3} matrix The matrix. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter */ Matrix3.getRotation = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); var inverseScale = Cartesian3.divideComponents( UNIT, Matrix3.getScale(matrix, scratchScale$8), scratchScale$8 ); result = Matrix3.multiplyByScale(matrix, inverseScale, result); 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. *

* Returns a diagonal matrix and unitary matrix such that: * matrix = unitary matrix * diagonal matrix * transpose(unitary matrix) *

*

* The values along the diagonal of the diagonal matrix are the eigenvalues. The columns * of the unitary matrix are the corresponding eigenvectors. *

* * @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 Cesium.Matrix3(), * diagonal : new Cesium.Matrix3() * }; * Cesium.Matrix3.computeEigenDecomposition(a, result); * * var unitaryTranspose = Cesium.Matrix3.transpose(result.unitary, new Cesium.Matrix3()); * var b = Cesium.Matrix3.multiply(result.unitary, result.diagonal, new Cesium.Matrix3()); * Cesium.Matrix3.multiply(b, unitaryTranspose, b); // b is now equal to a * * var lambda = Cesium.Matrix3.getColumn(result.diagonal, 0, new Cesium.Cartesian3()).x; // first eigenvalue * var v = Cesium.Matrix3.getColumn(result.unitary, 0, new Cesium.Cartesian3()); // first eigenvector * var c = Cesium.Cartesian3.multiplyByScalar(v, lambda, new Cesium.Cartesian3()); // equal to Cesium.Matrix3.multiplyByVector(a, v) */ Matrix3.computeEigenDecomposition = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); // 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. * * @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. */ Matrix3.abs = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Matrix3} matrix The matrix to use. * @returns {Number} The value of the determinant of the matrix. */ Matrix3.determinant = function (matrix) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); 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. * * @param {Matrix3} matrix The matrix to invert. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. * * @exception {DeveloperError} matrix is not invertible. */ Matrix3.inverse = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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); //>>includeStart('debug', pragmas.debug); if (Math.abs(determinant) <= CesiumMath.EPSILON15) { throw new DeveloperError("matrix is not invertible"); } //>>includeEnd('debug'); result[0] = m22 * m33 - m23 * m32; result[1] = m23 * m31 - m21 * m33; result[2] = m21 * m32 - m22 * m31; result[3] = m13 * m32 - m12 * m33; result[4] = m11 * m33 - m13 * m31; result[5] = m12 * m31 - m11 * m32; result[6] = m12 * m23 - m13 * m22; result[7] = m13 * m21 - m11 * m23; result[8] = m11 * m22 - m12 * m21; var scale = 1.0 / determinant; return Matrix3.multiplyByScalar(result, scale, result); }; var scratchTransposeMatrix$1 = new Matrix3(); /** * Computes the inverse transpose of a matrix. * * @param {Matrix3} matrix The matrix to transpose and invert. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. */ Matrix3.inverseTranspose = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); return Matrix3.inverse( Matrix3.transpose(matrix, scratchTransposeMatrix$1), result ); }; /** * Compares the provided matrices componentwise and returns * true if they are equal, false otherwise. * * @param {Matrix3} [left] The first matrix. * @param {Matrix3} [right] The second matrix. * @returns {Boolean} true if left and right are equal, false 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 * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix3} [left] The first matrix. * @param {Matrix3} [right] The second matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Matrix3.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0); 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. * * @type {Matrix3} * @constant */ Matrix3.IDENTITY = Object.freeze( new Matrix3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) ); /** * An immutable Matrix3 instance initialized to the zero matrix. * * @type {Matrix3} * @constant */ Matrix3.ZERO = Object.freeze( new Matrix3(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ); /** * The index into Matrix3 for column 0, row 0. * * @type {Number} * @constant */ Matrix3.COLUMN0ROW0 = 0; /** * The index into Matrix3 for column 0, row 1. * * @type {Number} * @constant */ Matrix3.COLUMN0ROW1 = 1; /** * The index into Matrix3 for column 0, row 2. * * @type {Number} * @constant */ Matrix3.COLUMN0ROW2 = 2; /** * The index into Matrix3 for column 1, row 0. * * @type {Number} * @constant */ Matrix3.COLUMN1ROW0 = 3; /** * The index into Matrix3 for column 1, row 1. * * @type {Number} * @constant */ Matrix3.COLUMN1ROW1 = 4; /** * The index into Matrix3 for column 1, row 2. * * @type {Number} * @constant */ Matrix3.COLUMN1ROW2 = 5; /** * The index into Matrix3 for column 2, row 0. * * @type {Number} * @constant */ Matrix3.COLUMN2ROW0 = 6; /** * The index into Matrix3 for column 2, row 1. * * @type {Number} * @constant */ Matrix3.COLUMN2ROW1 = 7; /** * The index into Matrix3 for column 2, row 2. * * @type {Number} * @constant */ Matrix3.COLUMN2ROW2 = 8; Object.defineProperties(Matrix3.prototype, { /** * Gets the number of items in the collection. * @memberof Matrix3.prototype * * @type {Number} */ length: { get: function () { return Matrix3.packedLength; }, }, }); /** * Duplicates the provided Matrix3 instance. * * @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 * true if they are equal, false otherwise. * * @param {Matrix3} [right] The right hand side matrix. * @returns {Boolean} true if they are equal, false otherwise. */ Matrix3.prototype.equals = function (right) { return Matrix3.equals(this, right); }; /** * @private */ Matrix3.equalsArray = function (matrix, array, offset) { return ( matrix[0] === array[offset] && matrix[1] === array[offset + 1] && matrix[2] === array[offset + 2] && matrix[3] === array[offset + 3] && matrix[4] === array[offset + 4] && matrix[5] === array[offset + 5] && matrix[6] === array[offset + 6] && matrix[7] === array[offset + 7] && matrix[8] === array[offset + 8] ); }; /** * Compares this matrix to the provided matrix componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix3} [right] The right hand side matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ 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)'. * * @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] + ")" ); }; /** * A 4x4 matrix, indexable as a column-major order array. * Constructor parameters are in row-major order for code readability. * @alias Matrix4 * @constructor * @implements {ArrayLike} * * @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.fromTranslationRotationScale * @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 Matrix4.computeView * @see Matrix2 * @see Matrix3 * @see Packable */ function Matrix4( 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); } /** * The number of elements used to pack the object into an array. * @type {Number} */ Matrix4.packedLength = 16; /** * Stores the provided instance into the provided array. * * @param {Matrix4} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Matrix4.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value[0]; array[startingIndex++] = value[1]; array[startingIndex++] = value[2]; array[startingIndex++] = value[3]; array[startingIndex++] = value[4]; array[startingIndex++] = value[5]; array[startingIndex++] = value[6]; array[startingIndex++] = value[7]; array[startingIndex++] = value[8]; array[startingIndex++] = value[9]; array[startingIndex++] = value[10]; array[startingIndex++] = value[11]; array[startingIndex++] = value[12]; array[startingIndex++] = value[13]; array[startingIndex++] = value[14]; array[startingIndex] = value[15]; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Matrix4} [result] The object into which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. */ Matrix4.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Matrix4(); } result[0] = array[startingIndex++]; result[1] = array[startingIndex++]; result[2] = array[startingIndex++]; result[3] = array[startingIndex++]; result[4] = array[startingIndex++]; result[5] = array[startingIndex++]; result[6] = array[startingIndex++]; result[7] = array[startingIndex++]; result[8] = array[startingIndex++]; result[9] = array[startingIndex++]; result[10] = array[startingIndex++]; result[11] = array[startingIndex++]; result[12] = array[startingIndex++]; result[13] = array[startingIndex++]; result[14] = array[startingIndex++]; result[15] = array[startingIndex]; return result; }; /** * Duplicates a Matrix4 instance. * * @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. * @function * * @param {Number[]} 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. * * @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 = Cesium.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 = Cesium.Matrix4.fromArray(v2, 2); */ Matrix4.fromArray = Matrix4.unpack; /** * Computes a Matrix4 instance from a column-major order array. * * @param {Number[]} 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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. */ Matrix4.fromColumnMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); return Matrix4.clone(values, result); }; /** * Computes a Matrix4 instance from a row-major order array. * The resulting matrix will be in column-major order. * * @param {Number[]} 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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. */ Matrix4.fromRowMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); 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. * * @param {Matrix3} rotation The upper left portion of the matrix representing the rotation. * @param {Cartesian3} [translation=Cartesian3.ZERO] 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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. */ Matrix4.fromRotationTranslation = function (rotation, translation, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rotation", rotation); //>>includeEnd('debug'); translation = defaultValue(translation, Cartesian3.ZERO); 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; }; /** * Computes a Matrix4 instance from a translation, rotation, and scale (TRS) * representation with the rotation represented as a quaternion. * * @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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. * * @example * var result = Cesium.Matrix4.fromTranslationQuaternionRotationScale( * new Cesium.Cartesian3(1.0, 2.0, 3.0), // translation * Cesium.Quaternion.IDENTITY, // rotation * new Cesium.Cartesian3(7.0, 8.0, 9.0), // scale * result); */ Matrix4.fromTranslationQuaternionRotationScale = function ( translation, rotation, scale, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("translation", translation); Check.typeOf.object("rotation", rotation); Check.typeOf.object("scale", scale); //>>includeEnd('debug'); 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 {@link TranslationRotationScale} instance. * * @param {TranslationRotationScale} translationRotationScale The instance. * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. * @returns {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. */ Matrix4.fromTranslationRotationScale = function ( translationRotationScale, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("translationRotationScale", translationRotationScale); //>>includeEnd('debug'); return Matrix4.fromTranslationQuaternionRotationScale( translationRotationScale.translation, translationRotationScale.rotation, translationRotationScale.scale, result ); }; /** * Creates a Matrix4 instance from a Cartesian3 representing the translation. * * @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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. * * @see Matrix4.multiplyByTranslation */ Matrix4.fromTranslation = function (translation, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("translation", translation); //>>includeEnd('debug'); return Matrix4.fromRotationTranslation(Matrix3.IDENTITY, translation, result); }; /** * Computes a Matrix4 instance representing a non-uniform scale. * * @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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. * * @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 = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(7.0, 8.0, 9.0)); */ Matrix4.fromScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("scale", scale); //>>includeEnd('debug'); 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. * * @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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. * * @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 = Cesium.Matrix4.fromUniformScale(2.0); */ Matrix4.fromUniformScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("scale", scale); //>>includeEnd('debug'); 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 fromCameraR = new Cartesian3(); var fromCameraU = new Cartesian3(); /** * Computes a Matrix4 instance from a Camera. * * @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 {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided. */ Matrix4.fromCamera = function (camera, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("camera", camera); //>>includeEnd('debug'); var position = camera.position; var direction = camera.direction; var up = camera.up; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("camera.position", position); Check.typeOf.object("camera.direction", direction); Check.typeOf.object("camera.up", up); //>>includeEnd('debug'); Cartesian3.normalize(direction, fromCameraF); Cartesian3.normalize( Cartesian3.cross(fromCameraF, up, fromCameraR), fromCameraR ); Cartesian3.normalize( Cartesian3.cross(fromCameraR, fromCameraF, fromCameraU), fromCameraU ); var sX = fromCameraR.x; var sY = fromCameraR.y; var sZ = fromCameraR.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 positionX = position.x; var positionY = position.y; var positionZ = position.z; var t0 = sX * -positionX + sY * -positionY + sZ * -positionZ; var t1 = uX * -positionX + uY * -positionY + uZ * -positionZ; var t2 = fX * positionX + fY * positionY + fZ * positionZ; // 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, -position.x, // 0.0, 1.0, 0.0, -position.y, // 0.0, 0.0, 1.0, -position.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. * * @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. * @returns {Matrix4} The modified result parameter. * * @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 ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThan("fovY", fovY, 0.0); Check.typeOf.number.lessThan("fovY", fovY, Math.PI); Check.typeOf.number.greaterThan("near", near, 0.0); Check.typeOf.number.greaterThan("far", far, 0.0); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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); 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. * * @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. * @returns {Matrix4} The modified result parameter. */ Matrix4.computeOrthographicOffCenter = function ( left, right, bottom, top, near, far, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("left", left); Check.typeOf.number("right", right); Check.typeOf.number("bottom", bottom); Check.typeOf.number("top", top); Check.typeOf.number("near", near); Check.typeOf.number("far", far); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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. * * @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. * @returns {Matrix4} The modified result parameter. */ Matrix4.computePerspectiveOffCenter = function ( left, right, bottom, top, near, far, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("left", left); Check.typeOf.number("right", right); Check.typeOf.number("bottom", bottom); Check.typeOf.number("top", top); Check.typeOf.number("near", near); Check.typeOf.number("far", far); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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); 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. * * @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 {Matrix4} result The object in which the result will be stored. * @returns {Matrix4} The modified result parameter. */ Matrix4.computeInfinitePerspectiveOffCenter = function ( left, right, bottom, top, near, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("left", left); Check.typeOf.number("right", right); Check.typeOf.number("bottom", bottom); Check.typeOf.number("top", top); Check.typeOf.number("near", near); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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. * * @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. * @returns {Matrix4} The modified result parameter. * * @example * // Create viewport transformation using an explicit viewport and depth range. * var m = Cesium.Matrix4.computeViewportTransformation({ * x : 0.0, * y : 0.0, * width : 1024.0, * height : 768.0 * }, 0.0, 1.0, new Cesium.Matrix4()); */ Matrix4.computeViewportTransformation = function ( viewport, nearDepthRange, farDepthRange, result ) { if (!defined(result)) { result = new Matrix4(); } 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; 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 a Matrix4 instance that transforms from world space to view space. * * @param {Cartesian3} position The position of the camera. * @param {Cartesian3} direction The forward direction. * @param {Cartesian3} up The up direction. * @param {Cartesian3} right The right direction. * @param {Matrix4} result The object in which the result will be stored. * @returns {Matrix4} The modified result parameter. */ Matrix4.computeView = function (position, direction, up, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("position", position); Check.typeOf.object("direction", direction); Check.typeOf.object("up", up); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = right.x; result[1] = up.x; result[2] = -direction.x; result[3] = 0.0; result[4] = right.y; result[5] = up.y; result[6] = -direction.y; result[7] = 0.0; result[8] = right.z; result[9] = up.z; result[10] = -direction.z; result[11] = 0.0; result[12] = -Cartesian3.dot(right, position); result[13] = -Cartesian3.dot(up, position); result[14] = Cartesian3.dot(direction, position); result[15] = 1.0; return result; }; /** * Computes an Array from the provided Matrix4 instance. * The array will be in column-major order. * * @param {Matrix4} matrix The matrix to use.. * @param {Number[]} [result] The Array onto which to store the result. * @returns {Number[]} The modified Array parameter or a new Array instance if one was not provided. * * @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 = Cesium.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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); 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. * * @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 must be 0, 1, 2, or 3. * @exception {DeveloperError} column must be 0, 1, 2, or 3. * * @example * var myMatrix = new Cesium.Matrix4(); * var column1Row0Index = Cesium.Matrix4.getElementIndex(1, 0); * var column1Row0 = myMatrix[column1Row0Index]; * myMatrix[column1Row0Index] = 10.0; */ Matrix4.getElementIndex = function (column, row) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("row", row, 0); Check.typeOf.number.lessThanOrEquals("row", row, 3); Check.typeOf.number.greaterThanOrEquals("column", column, 0); Check.typeOf.number.lessThanOrEquals("column", column, 3); //>>includeEnd('debug'); return column * 4 + row; }; /** * Retrieves a copy of the matrix column at the provided index as a Cartesian4 instance. * * @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. * * @exception {DeveloperError} index must be 0, 1, 2, or 3. * * @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 = Cesium.Matrix4.getColumn(m, 2, new Cesium.Cartesian4()); * * @example * //Example 2: Sets values for Cartesian instance * var a = new Cesium.Cartesian4(); * Cesium.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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 3); Check.typeOf.object("result", result); //>>includeEnd('debug'); var startIndex = index * 4; var x = matrix[startIndex]; var y = matrix[startIndex + 1]; var z = matrix[startIndex + 2]; var w = matrix[startIndex + 3]; 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. * * @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 {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @exception {DeveloperError} index must be 0, 1, 2, or 3. * * @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 = Cesium.Matrix4.setColumn(m, 2, new Cesium.Cartesian4(99.0, 98.0, 97.0, 96.0), new Cesium.Matrix4()); * * // 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 3); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; }; /** * Computes a new matrix that replaces the translation in the rightmost column of the provided * matrix with the provided translation. This assumes the matrix is an affine transformation * * @param {Matrix4} matrix The matrix to use. * @param {Cartesian3} translation The translation that replaces the translation of the provided matrix. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. */ Matrix4.setTranslation = function (matrix, translation, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("translation", translation); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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] = translation.x; result[13] = translation.y; result[14] = translation.z; result[15] = matrix[15]; return result; }; var scaleScratch = new Cartesian3(); /** * Computes a new matrix that replaces the scale with the provided scale. This assumes the matrix is an affine transformation * * @param {Matrix4} matrix The matrix to use. * @param {Cartesian3} scale The scale that replaces the scale of the provided matrix. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. */ Matrix4.setScale = function (matrix, scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("scale", scale); Check.typeOf.object("result", result); //>>includeEnd('debug'); var existingScale = Matrix4.getScale(matrix, scaleScratch); var newScale = Cartesian3.divideComponents( scale, existingScale, scaleScratch ); return Matrix4.multiplyByScale(matrix, newScale, result); }; /** * Retrieves a copy of the matrix row at the provided index as a Cartesian4 instance. * * @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. * * @exception {DeveloperError} index must be 0, 1, 2, or 3. * * @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 = Cesium.Matrix4.getRow(m, 2, new Cesium.Cartesian4()); * * @example * //Example 2: Sets values for a Cartesian instance * var a = new Cesium.Cartesian4(); * Cesium.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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 3); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = matrix[index]; var y = matrix[index + 4]; var z = matrix[index + 8]; var w = matrix[index + 12]; 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. * * @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 {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @exception {DeveloperError} index must be 0, 1, 2, or 3. * * @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 = Cesium.Matrix4.setRow(m, 2, new Cesium.Cartesian4(99.0, 98.0, 97.0, 96.0), new Cesium.Matrix4()); * * // 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 3); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; }; var scratchColumn$1 = new Cartesian3(); /** * Extracts the non-uniform scale assuming the matrix is an affine transformation. * * @param {Matrix4} matrix The matrix. * @param {Cartesian3} result The object onto which to store the result. * @returns {Cartesian3} The modified result parameter */ Matrix4.getScale = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = Cartesian3.magnitude( Cartesian3.fromElements(matrix[0], matrix[1], matrix[2], scratchColumn$1) ); result.y = Cartesian3.magnitude( Cartesian3.fromElements(matrix[4], matrix[5], matrix[6], scratchColumn$1) ); result.z = Cartesian3.magnitude( Cartesian3.fromElements(matrix[8], matrix[9], matrix[10], scratchColumn$1) ); return result; }; var scratchScale$7 = new Cartesian3(); /** * Computes the maximum scale assuming the matrix is an affine transformation. * The maximum scale is the maximum length of the column vectors in the upper-left * 3x3 matrix. * * @param {Matrix4} matrix The matrix. * @returns {Number} The maximum scale. */ Matrix4.getMaximumScale = function (matrix) { Matrix4.getScale(matrix, scratchScale$7); return Cartesian3.maximumComponent(scratchScale$7); }; /** * Computes the product of two matrices. * * @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. */ Matrix4.multiply = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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 sum of two matrices. * * @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. */ Matrix4.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] + right[0]; result[1] = left[1] + right[1]; result[2] = left[2] + right[2]; result[3] = left[3] + right[3]; result[4] = left[4] + right[4]; result[5] = left[5] + right[5]; result[6] = left[6] + right[6]; result[7] = left[7] + right[7]; result[8] = left[8] + right[8]; result[9] = left[9] + right[9]; result[10] = left[10] + right[10]; result[11] = left[11] + right[11]; result[12] = left[12] + right[12]; result[13] = left[13] + right[13]; result[14] = left[14] + right[14]; result[15] = left[15] + right[15]; return result; }; /** * Computes the difference of two matrices. * * @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. */ Matrix4.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] - right[0]; result[1] = left[1] - right[1]; result[2] = left[2] - right[2]; result[3] = left[3] - right[3]; result[4] = left[4] - right[4]; result[5] = left[5] - right[5]; result[6] = left[6] - right[6]; result[7] = left[7] - right[7]; result[8] = left[8] - right[8]; result[9] = left[9] - right[9]; result[10] = left[10] - right[10]; result[11] = left[11] - right[11]; result[12] = left[12] - right[12]; result[13] = left[13] - right[13]; result[14] = left[14] - right[14]; result[15] = left[15] - right[15]; 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 Matrix4.multiply}. * * @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. * * @example * var m1 = new Cesium.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 = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3(1.0, 1.0, 1.0)); * var m3 = Cesium.Matrix4.multiplyTransformation(m1, m2, new Cesium.Matrix4()); */ Matrix4.multiplyTransformation = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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 [0.0, 0.0, 0.0, 1.0]) * by a 3x3 rotation matrix. This is an optimization * for Matrix4.multiply(m, Matrix4.fromRotationTranslation(rotation), m); with less allocations and arithmetic operations. * * @param {Matrix4} matrix The matrix on the left-hand side. * @param {Matrix3} rotation The 3x3 rotation matrix on the right-hand side. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @example * // Instead of Cesium.Matrix4.multiply(m, Cesium.Matrix4.fromRotationTranslation(rotation), m); * Cesium.Matrix4.multiplyByMatrix3(m, rotation, m); */ Matrix4.multiplyByMatrix3 = function (matrix, rotation, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("rotation", rotation); Check.typeOf.object("result", result); //>>includeEnd('debug'); var left0 = matrix[0]; var left1 = matrix[1]; var left2 = matrix[2]; var left4 = matrix[4]; var left5 = matrix[5]; var left6 = matrix[6]; var left8 = matrix[8]; var left9 = matrix[9]; var left10 = matrix[10]; var right0 = rotation[0]; var right1 = rotation[1]; var right2 = rotation[2]; var right4 = rotation[3]; var right5 = rotation[4]; var right6 = rotation[5]; var right8 = rotation[6]; var right9 = rotation[7]; var right10 = rotation[8]; 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; 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] = matrix[12]; result[13] = matrix[13]; result[14] = matrix[14]; result[15] = matrix[15]; return result; }; /** * Multiplies a transformation matrix (with a bottom row of [0.0, 0.0, 0.0, 1.0]) * by an implicit translation matrix defined by a {@link Cartesian3}. This is an optimization * for Matrix4.multiply(m, Matrix4.fromTranslation(position), m); with less allocations and arithmetic operations. * * @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. * * @example * // Instead of Cesium.Matrix4.multiply(m, Cesium.Matrix4.fromTranslation(position), m); * Cesium.Matrix4.multiplyByTranslation(m, position, m); */ Matrix4.multiplyByTranslation = function (matrix, translation, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("translation", translation); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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]; 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 an affine transformation matrix (with a bottom row of [0.0, 0.0, 0.0, 1.0]) * by an implicit uniform scale matrix. This is an optimization * for Matrix4.multiply(m, Matrix4.fromUniformScale(scale), m);, where * m must be an affine matrix. * This function performs fewer allocations and arithmetic operations. * * @param {Matrix4} matrix The affine 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. * * * @example * // Instead of Cesium.Matrix4.multiply(m, Cesium.Matrix4.fromUniformScale(scale), m); * Cesium.Matrix4.multiplyByUniformScale(m, scale, m); * * @see Matrix4.fromUniformScale * @see Matrix4.multiplyByScale */ Matrix4.multiplyByUniformScale = function (matrix, scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number("scale", scale); Check.typeOf.object("result", result); //>>includeEnd('debug'); uniformScaleScratch.x = scale; uniformScaleScratch.y = scale; uniformScaleScratch.z = scale; return Matrix4.multiplyByScale(matrix, uniformScaleScratch, result); }; /** * Multiplies an affine transformation matrix (with a bottom row of [0.0, 0.0, 0.0, 1.0]) * by an implicit non-uniform scale matrix. This is an optimization * for Matrix4.multiply(m, Matrix4.fromUniformScale(scale), m);, where * m must be an affine matrix. * This function performs fewer allocations and arithmetic operations. * * @param {Matrix4} matrix The affine 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. * * * @example * // Instead of Cesium.Matrix4.multiply(m, Cesium.Matrix4.fromScale(scale), m); * Cesium.Matrix4.multiplyByScale(m, scale, m); * * @see Matrix4.fromScale * @see Matrix4.multiplyByUniformScale */ Matrix4.multiplyByScale = function (matrix, scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("scale", scale); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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); } 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. * * @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. */ Matrix4.multiplyByVector = function (matrix, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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 w component of zero. * * @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. * * @example * var p = new Cesium.Cartesian3(1.0, 2.0, 3.0); * var result = Cesium.Matrix4.multiplyByPointAsVector(matrix, p, new Cesium.Cartesian3()); * // A shortcut for * // Cartesian3 p = ... * // Cesium.Matrix4.multiplyByVector(matrix, new Cesium.Cartesian4(p.x, p.y, p.z, 0.0), result); */ Matrix4.multiplyByPointAsVector = function (matrix, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; 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 w component of 1, but returns a {@link Cartesian3} instead of a {@link Cartesian4}. * * @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. * * @example * var p = new Cesium.Cartesian3(1.0, 2.0, 3.0); * var result = Cesium.Matrix4.multiplyByPoint(matrix, p, new Cesium.Cartesian3()); */ Matrix4.multiplyByPoint = function (matrix, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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]; result.x = x; result.y = y; result.z = z; return result; }; /** * Computes the product of a matrix and a scalar. * * @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. * * @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 = Cesium.Matrix4.multiplyByScalar(m, -2, new Cesium.Matrix4()); * * // 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Matrix4} matrix The matrix to negate. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @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 = Cesium.Matrix4.negate(m, new Cesium.Matrix4()); * * // 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Matrix4} matrix The matrix to transpose. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @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 = Cesium.Matrix4.transpose(m, new Cesium.Matrix4()); * * // 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) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. */ Matrix4.abs = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 * true if they are equal, false otherwise. * * @param {Matrix4} [left] The first matrix. * @param {Matrix4} [right] The second matrix. * @returns {Boolean} true if left and right are equal, false 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(Cesium.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) { // Given that most matrices will be transformation matrices, the elements // are tested in order such that the test is likely to fail as early // as possible. I _think_ this is just as friendly to the L1 cache // as testing in index order. It is certainty faster in practice. return ( left === right || (defined(left) && defined(right) && // Translation left[12] === right[12] && left[13] === right[13] && left[14] === right[14] && // Rotation/scale left[0] === right[0] && left[1] === right[1] && left[2] === right[2] && left[4] === right[4] && left[5] === right[5] && left[6] === right[6] && left[8] === right[8] && left[9] === right[9] && left[10] === right[10] && // Bottom row left[3] === right[3] && left[7] === right[7] && left[11] === right[11] && left[15] === right[15]) ); }; /** * Compares the provided matrices componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix4} [left] The first matrix. * @param {Matrix4} [right] The second matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. * * @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(Cesium.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) { epsilon = defaultValue(epsilon, 0); 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. * * @param {Matrix4} matrix The matrix to use. * @param {Cartesian3} result The object onto which to store the result. * @returns {Cartesian3} The modified result parameter. */ Matrix4.getTranslation = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 an affine transformation matrix. * * @param {Matrix4} matrix The matrix to use. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter. * * @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 Cesium.Matrix3(); * Cesium.Matrix4.getMatrix3(m,b); * * // b = [10.0, 14.0, 18.0] * // [11.0, 15.0, 19.0] * // [12.0, 16.0, 20.0] */ Matrix4.getMatrix3 = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; }; var scratchInverseRotation = new Matrix3(); var scratchMatrix3Zero = new Matrix3(); var scratchBottomRow = new Cartesian4(); var scratchExpectedBottomRow = new Cartesian4(0.0, 0.0, 0.0, 1.0); /** * 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 Matrix4.inverseTransformation}. * * @param {Matrix4} matrix The matrix to invert. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. * * @exception {RuntimeError} matrix is not invertible because its determinate is zero. */ Matrix4.inverse = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); // // 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.EPSILON21) { // Special case for a zero scale matrix that can occur, for example, // when a model's node has a [0, 0, 0] scale. if ( Matrix3.equalsEpsilon( Matrix4.getMatrix3(matrix, scratchInverseRotation), scratchMatrix3Zero, CesiumMath.EPSILON7 ) && Cartesian4.equals( Matrix4.getRow(matrix, 3, scratchBottomRow), scratchExpectedBottomRow ) ) { result[0] = 0.0; result[1] = 0.0; result[2] = 0.0; result[3] = 0.0; result[4] = 0.0; result[5] = 0.0; result[6] = 0.0; result[7] = 0.0; result[8] = 0.0; result[9] = 0.0; result[10] = 0.0; result[11] = 0.0; result[12] = -matrix[12]; result[13] = -matrix[13]; result[14] = -matrix[14]; result[15] = 1.0; return result; } throw new RuntimeError( "matrix is not invertible because its determinate is zero." ); } // calculate matrix inverse det = 1.0 / 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 Matrix4.inverse}. * * @param {Matrix4} matrix The matrix to invert. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. */ Matrix4.inverseTransformation = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); //This function is an optimized version of the below 4 lines. //var rT = Matrix3.transpose(Matrix4.getMatrix3(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; 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; }; var scratchTransposeMatrix = new Matrix4(); /** * Computes the inverse transpose of a matrix. * * @param {Matrix4} matrix The matrix to transpose and invert. * @param {Matrix4} result The object onto which to store the result. * @returns {Matrix4} The modified result parameter. */ Matrix4.inverseTranspose = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); return Matrix4.inverse( Matrix4.transpose(matrix, scratchTransposeMatrix), result ); }; /** * An immutable Matrix4 instance initialized to the identity matrix. * * @type {Matrix4} * @constant */ Matrix4.IDENTITY = Object.freeze( 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 ) ); /** * An immutable Matrix4 instance initialized to the zero matrix. * * @type {Matrix4} * @constant */ Matrix4.ZERO = Object.freeze( new Matrix4( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ) ); /** * The index into Matrix4 for column 0, row 0. * * @type {Number} * @constant */ Matrix4.COLUMN0ROW0 = 0; /** * The index into Matrix4 for column 0, row 1. * * @type {Number} * @constant */ Matrix4.COLUMN0ROW1 = 1; /** * The index into Matrix4 for column 0, row 2. * * @type {Number} * @constant */ Matrix4.COLUMN0ROW2 = 2; /** * The index into Matrix4 for column 0, row 3. * * @type {Number} * @constant */ Matrix4.COLUMN0ROW3 = 3; /** * The index into Matrix4 for column 1, row 0. * * @type {Number} * @constant */ Matrix4.COLUMN1ROW0 = 4; /** * The index into Matrix4 for column 1, row 1. * * @type {Number} * @constant */ Matrix4.COLUMN1ROW1 = 5; /** * The index into Matrix4 for column 1, row 2. * * @type {Number} * @constant */ Matrix4.COLUMN1ROW2 = 6; /** * The index into Matrix4 for column 1, row 3. * * @type {Number} * @constant */ Matrix4.COLUMN1ROW3 = 7; /** * The index into Matrix4 for column 2, row 0. * * @type {Number} * @constant */ Matrix4.COLUMN2ROW0 = 8; /** * The index into Matrix4 for column 2, row 1. * * @type {Number} * @constant */ Matrix4.COLUMN2ROW1 = 9; /** * The index into Matrix4 for column 2, row 2. * * @type {Number} * @constant */ Matrix4.COLUMN2ROW2 = 10; /** * The index into Matrix4 for column 2, row 3. * * @type {Number} * @constant */ Matrix4.COLUMN2ROW3 = 11; /** * The index into Matrix4 for column 3, row 0. * * @type {Number} * @constant */ Matrix4.COLUMN3ROW0 = 12; /** * The index into Matrix4 for column 3, row 1. * * @type {Number} * @constant */ Matrix4.COLUMN3ROW1 = 13; /** * The index into Matrix4 for column 3, row 2. * * @type {Number} * @constant */ Matrix4.COLUMN3ROW2 = 14; /** * The index into Matrix4 for column 3, row 3. * * @type {Number} * @constant */ Matrix4.COLUMN3ROW3 = 15; Object.defineProperties(Matrix4.prototype, { /** * Gets the number of items in the collection. * @memberof Matrix4.prototype * * @type {Number} */ length: { get: function () { return Matrix4.packedLength; }, }, }); /** * Duplicates the provided Matrix4 instance. * * @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 * true if they are equal, false otherwise. * * @param {Matrix4} [right] The right hand side matrix. * @returns {Boolean} true if they are equal, false otherwise. */ Matrix4.prototype.equals = function (right) { return Matrix4.equals(this, right); }; /** * @private */ Matrix4.equalsArray = function (matrix, array, offset) { return ( matrix[0] === array[offset] && matrix[1] === array[offset + 1] && matrix[2] === array[offset + 2] && matrix[3] === array[offset + 3] && matrix[4] === array[offset + 4] && matrix[5] === array[offset + 5] && matrix[6] === array[offset + 6] && matrix[7] === array[offset + 7] && matrix[8] === array[offset + 8] && matrix[9] === array[offset + 9] && matrix[10] === array[offset + 10] && matrix[11] === array[offset + 11] && matrix[12] === array[offset + 12] && matrix[13] === array[offset + 13] && matrix[14] === array[offset + 14] && matrix[15] === array[offset + 15] ); }; /** * Compares this matrix to the provided matrix componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix4} [right] The right hand side matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ 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)'. * * @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] + ")" ); }; 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. * * @namespace Fullscreen * * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification} */ var Fullscreen = {}; Object.defineProperties(Fullscreen, { /** * The element that is currently fullscreen, if any. To simply check if the * browser is in fullscreen mode or not, use {@link Fullscreen#fullscreen}. * @memberof Fullscreen * @type {Object} * @readonly */ element: { get: function () { if (!Fullscreen.supportsFullscreen()) { return undefined; } return document[_names.fullscreenElement]; }, }, /** * 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#fullscreen}. * @memberof Fullscreen * @type {String} * @readonly */ changeEventName: { get: function () { if (!Fullscreen.supportsFullscreen()) { return undefined; } return _names.fullscreenchange; }, }, /** * The name of the event that is fired when a fullscreen error * occurs. This event name is intended for use with addEventListener. * @memberof Fullscreen * @type {String} * @readonly */ errorEventName: { get: function () { if (!Fullscreen.supportsFullscreen()) { return undefined; } return _names.fullscreenerror; }, }, /** * 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). * @memberof Fullscreen * @type {Boolean} * @readonly */ enabled: { get: function () { if (!Fullscreen.supportsFullscreen()) { return undefined; } return document[_names.fullscreenEnabled]; }, }, /** * Determines if the browser is currently in fullscreen mode. * @memberof Fullscreen * @type {Boolean} * @readonly */ fullscreen: { get: function () { if (!Fullscreen.supportsFullscreen()) { return undefined; } return Fullscreen.element !== null; }, }, }); /** * Detects whether the browser supports the standard fullscreen API. * * @returns {Boolean} true if the browser supports the standard fullscreen API, * false 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 (document[name] !== undefined) { _names.fullscreenEnabled = name; } else { name = prefix + "FullScreenEnabled"; if (document[name] !== undefined) { _names.fullscreenEnabled = name; } } // casing of Fullscreen differs across browsers name = prefix + "FullscreenElement"; if (document[name] !== undefined) { _names.fullscreenElement = name; } else { name = prefix + "FullScreenElement"; if (document[name] !== undefined) { _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 (document["on" + name] !== undefined) { //except on IE if (prefix === "ms") { name = "MSFullscreenChange"; } _names.fullscreenchange = name; } name = prefix + "fullscreenerror"; if (document["on" + name] !== undefined) { //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. * @param {Object} [vrDevice] The HMDVRDevice device. * * @example * // Put the entire page into fullscreen. * Cesium.Fullscreen.requestFullscreen(document.body) * * // Place only the Cesium canvas into fullscreen. * Cesium.Fullscreen.requestFullscreen(scene.canvas) */ Fullscreen.requestFullscreen = function (element, vrDevice) { if (!Fullscreen.supportsFullscreen()) { return; } element[_names.requestFullscreen]({ vrDisplay: vrDevice }); }; /** * 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](); }; //For unit tests Fullscreen._names = _names; var theNavigator; if (typeof navigator !== "undefined") { theNavigator = navigator; } else { theNavigator = {}; } 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)) { isChromeResult = false; // Edge contains Chrome in the user agent too if (!isEdge()) { var fields = / Chrome\/([\.0-9]+)/.exec(theNavigator.userAgent); if (fields !== null) { isChromeResult = true; chromeVersionResult = extractVersion(fields[1]); } } } return isChromeResult; } function chromeVersion() { return isChrome() && chromeVersionResult; } var isSafariResult; var safariVersionResult; function isSafari() { if (!defined(isSafariResult)) { isSafariResult = false; // Chrome and Edge contain Safari in the user agent too if ( !isChrome() && !isEdge() && / Safari\/[\.0-9]+/.test(theNavigator.userAgent) ) { var fields = / Version\/([\.0-9]+)/.exec(theNavigator.userAgent); if (fields !== null) { isSafariResult = true; safariVersionResult = extractVersion(fields[1]); } } } return isSafariResult; } function safariVersion() { return isSafari() && safariVersionResult; } var isWebkitResult; var webkitVersionResult; function isWebkit() { if (!defined(isWebkitResult)) { isWebkitResult = false; var fields = / AppleWebKit\/([\.0-9]+)(\+?)/.exec(theNavigator.userAgent); if (fields !== null) { 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)) { isInternetExplorerResult = false; var fields; if (theNavigator.appName === "Microsoft Internet Explorer") { fields = /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(theNavigator.userAgent); if (fields !== null) { isInternetExplorerResult = true; internetExplorerVersionResult = extractVersion(fields[1]); } } else if (theNavigator.appName === "Netscape") { fields = /Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec( theNavigator.userAgent ); if (fields !== null) { isInternetExplorerResult = true; internetExplorerVersionResult = extractVersion(fields[1]); } } } return isInternetExplorerResult; } function internetExplorerVersion() { return isInternetExplorer() && internetExplorerVersionResult; } var isEdgeResult; var edgeVersionResult; function isEdge() { if (!defined(isEdgeResult)) { isEdgeResult = false; var fields = / Edge\/([\.0-9]+)/.exec(theNavigator.userAgent); if (fields !== null) { isEdgeResult = true; edgeVersionResult = extractVersion(fields[1]); } } return isEdgeResult; } function edgeVersion() { return isEdge() && edgeVersionResult; } var isFirefoxResult; var firefoxVersionResult; function isFirefox() { if (!defined(isFirefoxResult)) { isFirefoxResult = false; var fields = /Firefox\/([\.0-9]+)/.exec(theNavigator.userAgent); if (fields !== null) { isFirefoxResult = true; firefoxVersionResult = extractVersion(fields[1]); } } return isFirefoxResult; } var isWindowsResult; function isWindows() { if (!defined(isWindowsResult)) { isWindowsResult = /Windows/i.test(theNavigator.appVersion); } return isWindowsResult; } function firefoxVersion() { return isFirefox() && firefoxVersionResult; } var hasPointerEvents; function supportsPointerEvents() { if (!defined(hasPointerEvents)) { //While navigator.pointerEnabled is deprecated in the W3C specification //we still need to use it if it exists in order to support browsers //that rely on it, such as the Windows WebBrowser control which defines //PointerEvent but sets navigator.pointerEnabled to false. //Firefox disabled because of https://github.com/CesiumGS/cesium/issues/6372 hasPointerEvents = !isFirefox() && typeof PointerEvent !== "undefined" && (!defined(theNavigator.pointerEnabled) || theNavigator.pointerEnabled); } return hasPointerEvents; } var imageRenderingValueResult; var supportsImageRenderingPixelatedResult; function supportsImageRenderingPixelated() { if (!defined(supportsImageRenderingPixelatedResult)) { var canvas = document.createElement("canvas"); canvas.setAttribute( "style", "image-rendering: -moz-crisp-edges;" + "image-rendering: pixelated;" ); //canvas.style.imageRendering will be undefined, null or an empty string on unsupported browsers. var tmp = canvas.style.imageRendering; supportsImageRenderingPixelatedResult = defined(tmp) && tmp !== ""; if (supportsImageRenderingPixelatedResult) { imageRenderingValueResult = tmp; } } return supportsImageRenderingPixelatedResult; } function imageRenderingValue() { return supportsImageRenderingPixelated() ? imageRenderingValueResult : undefined; } function supportsWebP() { //>>includeStart('debug', pragmas.debug); if (!supportsWebP.initialized) { throw new DeveloperError( "You must call FeatureDetection.supportsWebP.initialize and wait for the promise to resolve before calling FeatureDetection.supportsWebP" ); } //>>includeEnd('debug'); return supportsWebP._result; } supportsWebP._promise = undefined; supportsWebP._result = undefined; supportsWebP.initialize = function () { // From https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_for_webp if (defined(supportsWebP._promise)) { return supportsWebP._promise; } var supportsWebPDeferred = when.defer(); supportsWebP._promise = supportsWebPDeferred.promise; if (isEdge()) { // Edge's WebP support with WebGL is incomplete. // See bug report: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/19221241/ supportsWebP._result = false; supportsWebPDeferred.resolve(supportsWebP._result); return supportsWebPDeferred.promise; } var image = new Image(); image.onload = function () { supportsWebP._result = image.width > 0 && image.height > 0; supportsWebPDeferred.resolve(supportsWebP._result); }; image.onerror = function () { supportsWebP._result = false; supportsWebPDeferred.resolve(supportsWebP._result); }; image.src = "data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA"; return supportsWebPDeferred.promise; }; Object.defineProperties(supportsWebP, { initialized: { get: function () { return defined(supportsWebP._result); }, }, }); var typedArrayTypes = []; if (typeof ArrayBuffer !== "undefined") { typedArrayTypes.push( Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ); if (typeof Uint8ClampedArray !== "undefined") { typedArrayTypes.push(Uint8ClampedArray); } if (typeof Uint8ClampedArray !== "undefined") { typedArrayTypes.push(Uint8ClampedArray); } } /** * A set of functions to detect whether the current browser supports * various features. * * @namespace FeatureDetection */ var FeatureDetection = { isChrome: isChrome, chromeVersion: chromeVersion, isSafari: isSafari, safariVersion: safariVersion, isWebkit: isWebkit, webkitVersion: webkitVersion, isInternetExplorer: isInternetExplorer, internetExplorerVersion: internetExplorerVersion, isEdge: isEdge, edgeVersion: edgeVersion, isFirefox: isFirefox, firefoxVersion: firefoxVersion, isWindows: isWindows, hardwareConcurrency: defaultValue(theNavigator.hardwareConcurrency, 3), supportsPointerEvents: supportsPointerEvents, supportsImageRenderingPixelated: supportsImageRenderingPixelated, supportsWebP: supportsWebP, imageRenderingValue: imageRenderingValue, typedArrayTypes: typedArrayTypes, }; /** * Detects whether the current browser supports the full screen standard. * * @returns {Boolean} true if the browser supports the full screen standard, false if not. * * @see Fullscreen * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification} */ FeatureDetection.supportsFullscreen = function () { return Fullscreen.supportsFullscreen(); }; /** * Detects whether the current browser supports typed arrays. * * @returns {Boolean} true if the browser supports typed arrays, false if not. * * @see {@link http://www.khronos.org/registry/typedarray/specs/latest/|Typed Array Specification} */ FeatureDetection.supportsTypedArrays = function () { return typeof ArrayBuffer !== "undefined"; }; /** * Detects whether the current browser supports Web Workers. * * @returns {Boolean} true if the browsers supports Web Workers, false if not. * * @see {@link http://www.w3.org/TR/workers/} */ FeatureDetection.supportsWebWorkers = function () { return typeof Worker !== "undefined"; }; /** * Detects whether the current browser supports Web Assembly. * * @returns {Boolean} true if the browsers supports Web Assembly, false if not. * * @see {@link https://developer.mozilla.org/en-US/docs/WebAssembly} */ FeatureDetection.supportsWebAssembly = function () { return typeof WebAssembly !== "undefined" && !FeatureDetection.isEdge(); }; /** * A set of 4-dimensional coordinates used to represent rotation in 3-dimensional space. * @alias Quaternion * @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 PackableForInterpolation */ function Quaternion(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); } var fromAxisAngleScratch = new Cartesian3(); /** * Computes a quaternion representing a rotation around an axis. * * @param {Cartesian3} axis The axis of rotation. * @param {Number} angle The angle in radians to rotate around the axis. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. */ Quaternion.fromAxisAngle = function (axis, angle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("axis", axis); Check.typeOf.number("angle", angle); //>>includeEnd('debug'); var halfAngle = angle / 2.0; var s = Math.sin(halfAngle); fromAxisAngleScratch = Cartesian3.normalize(axis, fromAxisAngleScratch); var x = fromAxisAngleScratch.x * s; var y = fromAxisAngleScratch.y * s; var z = fromAxisAngleScratch.z * s; var w = Math.cos(halfAngle); if (!defined(result)) { return new Quaternion(x, y, z, w); } result.x = x; result.y = y; result.z = z; result.w = w; return result; }; var fromRotationMatrixNext = [1, 2, 0]; var fromRotationMatrixQuat = new Array(3); /** * Computes a Quaternion from the provided Matrix3 instance. * * @param {Matrix3} matrix The rotation matrix. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. * * @see Matrix3.fromQuaternion */ Quaternion.fromRotationMatrix = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); var root; var x; var y; var z; var w; var m00 = matrix[Matrix3.COLUMN0ROW0]; var m11 = matrix[Matrix3.COLUMN1ROW1]; var m22 = matrix[Matrix3.COLUMN2ROW2]; var trace = m00 + m11 + m22; if (trace > 0.0) { // |w| > 1/2, may as well choose w > 1/2 root = Math.sqrt(trace + 1.0); // 2w w = 0.5 * root; root = 0.5 / root; // 1/(4w) x = (matrix[Matrix3.COLUMN1ROW2] - matrix[Matrix3.COLUMN2ROW1]) * root; y = (matrix[Matrix3.COLUMN2ROW0] - matrix[Matrix3.COLUMN0ROW2]) * root; z = (matrix[Matrix3.COLUMN0ROW1] - matrix[Matrix3.COLUMN1ROW0]) * root; } else { // |w| <= 1/2 var next = fromRotationMatrixNext; var i = 0; if (m11 > m00) { i = 1; } if (m22 > m00 && m22 > m11) { i = 2; } var j = next[i]; var k = next[j]; root = Math.sqrt( matrix[Matrix3.getElementIndex(i, i)] - matrix[Matrix3.getElementIndex(j, j)] - matrix[Matrix3.getElementIndex(k, k)] + 1.0 ); var quat = fromRotationMatrixQuat; quat[i] = 0.5 * root; root = 0.5 / root; w = (matrix[Matrix3.getElementIndex(k, j)] - matrix[Matrix3.getElementIndex(j, k)]) * root; quat[j] = (matrix[Matrix3.getElementIndex(j, i)] + matrix[Matrix3.getElementIndex(i, j)]) * root; quat[k] = (matrix[Matrix3.getElementIndex(k, i)] + matrix[Matrix3.getElementIndex(i, k)]) * root; x = -quat[0]; y = -quat[1]; z = -quat[2]; } if (!defined(result)) { return new Quaternion(x, y, z, w); } result.x = x; result.y = y; result.z = z; result.w = w; return result; }; var scratchHPRQuaternion$1 = new Quaternion(); var scratchHeadingQuaternion = new Quaternion(); var scratchPitchQuaternion = new Quaternion(); var scratchRollQuaternion = new Quaternion(); /** * Computes a rotation from the given heading, pitch and roll angles. Heading is the rotation about the * negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about * the positive x axis. * * @param {HeadingPitchRoll} headingPitchRoll The rotation expressed as a heading, pitch and roll. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided. */ Quaternion.fromHeadingPitchRoll = function (headingPitchRoll, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("headingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); scratchRollQuaternion = Quaternion.fromAxisAngle( Cartesian3.UNIT_X, headingPitchRoll.roll, scratchHPRQuaternion$1 ); scratchPitchQuaternion = Quaternion.fromAxisAngle( Cartesian3.UNIT_Y, -headingPitchRoll.pitch, result ); result = Quaternion.multiply( scratchPitchQuaternion, scratchRollQuaternion, scratchPitchQuaternion ); scratchHeadingQuaternion = Quaternion.fromAxisAngle( Cartesian3.UNIT_Z, -headingPitchRoll.heading, scratchHPRQuaternion$1 ); return Quaternion.multiply(scratchHeadingQuaternion, result, result); }; var sampledQuaternionAxis = new Cartesian3(); var sampledQuaternionRotation = new Cartesian3(); var sampledQuaternionTempQuaternion = new Quaternion(); var sampledQuaternionQuaternion0 = new Quaternion(); var sampledQuaternionQuaternion0Conjugate = new Quaternion(); /** * The number of elements used to pack the object into an array. * @type {Number} */ Quaternion.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {Quaternion} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Quaternion.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.x; array[startingIndex++] = value.y; array[startingIndex++] = value.z; array[startingIndex] = value.w; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Quaternion} [result] The object into which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. */ Quaternion.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Quaternion(); } result.x = array[startingIndex]; result.y = array[startingIndex + 1]; result.z = array[startingIndex + 2]; result.w = array[startingIndex + 3]; return result; }; /** * The number of elements used to store the object into an array in its interpolatable form. * @type {Number} */ Quaternion.packedInterpolationLength = 3; /** * Converts a packed array into a form suitable for interpolation. * * @param {Number[]} packedArray The packed array. * @param {Number} [startingIndex=0] The index of the first element to be converted. * @param {Number} [lastIndex=packedArray.length] The index of the last element to be converted. * @param {Number[]} [result] The object into which to store the result. */ Quaternion.convertPackedArrayForInterpolation = function ( packedArray, startingIndex, lastIndex, result ) { Quaternion.unpack( packedArray, lastIndex * 4, sampledQuaternionQuaternion0Conjugate ); Quaternion.conjugate( sampledQuaternionQuaternion0Conjugate, sampledQuaternionQuaternion0Conjugate ); for (var i = 0, len = lastIndex - startingIndex + 1; i < len; i++) { var offset = i * 3; Quaternion.unpack( packedArray, (startingIndex + i) * 4, sampledQuaternionTempQuaternion ); Quaternion.multiply( sampledQuaternionTempQuaternion, sampledQuaternionQuaternion0Conjugate, sampledQuaternionTempQuaternion ); if (sampledQuaternionTempQuaternion.w < 0) { Quaternion.negate( sampledQuaternionTempQuaternion, sampledQuaternionTempQuaternion ); } Quaternion.computeAxis( sampledQuaternionTempQuaternion, sampledQuaternionAxis ); var angle = Quaternion.computeAngle(sampledQuaternionTempQuaternion); if (!defined(result)) { result = []; } result[offset] = sampledQuaternionAxis.x * angle; result[offset + 1] = sampledQuaternionAxis.y * angle; result[offset + 2] = sampledQuaternionAxis.z * angle; } }; /** * Retrieves an instance from a packed array converted with {@link convertPackedArrayForInterpolation}. * * @param {Number[]} array The array previously packed for interpolation. * @param {Number[]} sourceArray The original packed array. * @param {Number} [firstIndex=0] The firstIndex used to convert the array. * @param {Number} [lastIndex=packedArray.length] The lastIndex used to convert the array. * @param {Quaternion} [result] The object into which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. */ Quaternion.unpackInterpolationResult = function ( array, sourceArray, firstIndex, lastIndex, result ) { if (!defined(result)) { result = new Quaternion(); } Cartesian3.fromArray(array, 0, sampledQuaternionRotation); var magnitude = Cartesian3.magnitude(sampledQuaternionRotation); Quaternion.unpack(sourceArray, lastIndex * 4, sampledQuaternionQuaternion0); if (magnitude === 0) { Quaternion.clone(Quaternion.IDENTITY, sampledQuaternionTempQuaternion); } else { Quaternion.fromAxisAngle( sampledQuaternionRotation, magnitude, sampledQuaternionTempQuaternion ); } return Quaternion.multiply( sampledQuaternionTempQuaternion, sampledQuaternionQuaternion0, result ); }; /** * Duplicates a Quaternion instance. * * @param {Quaternion} quaternion The quaternion to duplicate. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. (Returns undefined if quaternion is undefined) */ Quaternion.clone = function (quaternion, result) { if (!defined(quaternion)) { return undefined; } if (!defined(result)) { return new Quaternion( quaternion.x, quaternion.y, quaternion.z, quaternion.w ); } result.x = quaternion.x; result.y = quaternion.y; result.z = quaternion.z; result.w = quaternion.w; return result; }; /** * Computes the conjugate of the provided quaternion. * * @param {Quaternion} quaternion The quaternion to conjugate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.conjugate = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = -quaternion.x; result.y = -quaternion.y; result.z = -quaternion.z; result.w = quaternion.w; return result; }; /** * Computes magnitude squared for the provided quaternion. * * @param {Quaternion} quaternion The quaternion to conjugate. * @returns {Number} The magnitude squared. */ Quaternion.magnitudeSquared = function (quaternion) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); //>>includeEnd('debug'); return ( quaternion.x * quaternion.x + quaternion.y * quaternion.y + quaternion.z * quaternion.z + quaternion.w * quaternion.w ); }; /** * Computes magnitude for the provided quaternion. * * @param {Quaternion} quaternion The quaternion to conjugate. * @returns {Number} The magnitude. */ Quaternion.magnitude = function (quaternion) { return Math.sqrt(Quaternion.magnitudeSquared(quaternion)); }; /** * Computes the normalized form of the provided quaternion. * * @param {Quaternion} quaternion The quaternion to normalize. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.normalize = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("result", result); //>>includeEnd('debug'); var inverseMagnitude = 1.0 / Quaternion.magnitude(quaternion); var x = quaternion.x * inverseMagnitude; var y = quaternion.y * inverseMagnitude; var z = quaternion.z * inverseMagnitude; var w = quaternion.w * inverseMagnitude; result.x = x; result.y = y; result.z = z; result.w = w; return result; }; /** * Computes the inverse of the provided quaternion. * * @param {Quaternion} quaternion The quaternion to normalize. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.inverse = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("result", result); //>>includeEnd('debug'); var magnitudeSquared = Quaternion.magnitudeSquared(quaternion); result = Quaternion.conjugate(quaternion, result); return Quaternion.multiplyByScalar(result, 1.0 / magnitudeSquared, result); }; /** * Computes the componentwise sum of two quaternions. * * @param {Quaternion} left The first quaternion. * @param {Quaternion} right The second quaternion. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 quaternions. * * @param {Quaternion} left The first quaternion. * @param {Quaternion} right The second quaternion. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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; }; /** * Negates the provided quaternion. * * @param {Quaternion} quaternion The quaternion to be negated. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.negate = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = -quaternion.x; result.y = -quaternion.y; result.z = -quaternion.z; result.w = -quaternion.w; return result; }; /** * Computes the dot (scalar) product of two quaternions. * * @param {Quaternion} left The first quaternion. * @param {Quaternion} right The second quaternion. * @returns {Number} The dot product. */ Quaternion.dot = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return ( left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w ); }; /** * Computes the product of two quaternions. * * @param {Quaternion} left The first quaternion. * @param {Quaternion} right The second quaternion. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.multiply = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); var leftX = left.x; var leftY = left.y; var leftZ = left.z; var leftW = left.w; var rightX = right.x; var rightY = right.y; var rightZ = right.z; var rightW = right.w; var x = leftW * rightX + leftX * rightW + leftY * rightZ - leftZ * rightY; var y = leftW * rightY - leftX * rightZ + leftY * rightW + leftZ * rightX; var z = leftW * rightZ + leftX * rightY - leftY * rightX + leftZ * rightW; var w = leftW * rightW - leftX * rightX - leftY * rightY - leftZ * rightZ; result.x = x; result.y = y; result.z = z; result.w = w; return result; }; /** * Multiplies the provided quaternion componentwise by the provided scalar. * * @param {Quaternion} quaternion The quaternion to be scaled. * @param {Number} scalar The scalar to multiply with. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.multiplyByScalar = function (quaternion, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = quaternion.x * scalar; result.y = quaternion.y * scalar; result.z = quaternion.z * scalar; result.w = quaternion.w * scalar; return result; }; /** * Divides the provided quaternion componentwise by the provided scalar. * * @param {Quaternion} quaternion The quaternion to be divided. * @param {Number} scalar The scalar to divide by. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.divideByScalar = function (quaternion, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = quaternion.x / scalar; result.y = quaternion.y / scalar; result.z = quaternion.z / scalar; result.w = quaternion.w / scalar; return result; }; /** * Computes the axis of rotation of the provided quaternion. * * @param {Quaternion} quaternion The quaternion to use. * @param {Cartesian3} result The object onto which to store the result. * @returns {Cartesian3} The modified result parameter. */ Quaternion.computeAxis = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.object("result", result); //>>includeEnd('debug'); var w = quaternion.w; if (Math.abs(w - 1.0) < CesiumMath.EPSILON6) { result.x = result.y = result.z = 0; return result; } var scalar = 1.0 / Math.sqrt(1.0 - w * w); result.x = quaternion.x * scalar; result.y = quaternion.y * scalar; result.z = quaternion.z * scalar; return result; }; /** * Computes the angle of rotation of the provided quaternion. * * @param {Quaternion} quaternion The quaternion to use. * @returns {Number} The angle of rotation. */ Quaternion.computeAngle = function (quaternion) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); //>>includeEnd('debug'); if (Math.abs(quaternion.w - 1.0) < CesiumMath.EPSILON6) { return 0.0; } return 2.0 * Math.acos(quaternion.w); }; var lerpScratch = new Quaternion(); /** * Computes the linear interpolation or extrapolation at t using the provided quaternions. * * @param {Quaternion} start The value corresponding to t at 0.0. * @param {Quaternion} end The value corresponding to t at 1.0. * @param {Number} t The point along t at which to interpolate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.lerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); lerpScratch = Quaternion.multiplyByScalar(end, t, lerpScratch); result = Quaternion.multiplyByScalar(start, 1.0 - t, result); return Quaternion.add(lerpScratch, result, result); }; var slerpEndNegated = new Quaternion(); var slerpScaledP = new Quaternion(); var slerpScaledR = new Quaternion(); /** * Computes the spherical linear interpolation or extrapolation at t using the provided quaternions. * * @param {Quaternion} start The value corresponding to t at 0.0. * @param {Quaternion} end The value corresponding to t at 1.0. * @param {Number} t The point along t at which to interpolate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. * * @see Quaternion#fastSlerp */ Quaternion.slerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); var dot = Quaternion.dot(start, end); // The angle between start must be acute. Since q and -q represent // the same rotation, negate q to get the acute angle. var r = end; if (dot < 0.0) { dot = -dot; r = slerpEndNegated = Quaternion.negate(end, slerpEndNegated); } // dot > 0, as the dot product approaches 1, the angle between the // quaternions vanishes. use linear interpolation. if (1.0 - dot < CesiumMath.EPSILON6) { return Quaternion.lerp(start, r, t, result); } var theta = Math.acos(dot); slerpScaledP = Quaternion.multiplyByScalar( start, Math.sin((1 - t) * theta), slerpScaledP ); slerpScaledR = Quaternion.multiplyByScalar( r, Math.sin(t * theta), slerpScaledR ); result = Quaternion.add(slerpScaledP, slerpScaledR, result); return Quaternion.multiplyByScalar(result, 1.0 / Math.sin(theta), result); }; /** * The logarithmic quaternion function. * * @param {Quaternion} quaternion The unit quaternion. * @param {Cartesian3} result The object onto which to store the result. * @returns {Cartesian3} The modified result parameter. */ Quaternion.log = function (quaternion, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("quaternion", quaternion); Check.typeOf.object("result", result); //>>includeEnd('debug'); var theta = CesiumMath.acosClamped(quaternion.w); var thetaOverSinTheta = 0.0; if (theta !== 0.0) { thetaOverSinTheta = theta / Math.sin(theta); } return Cartesian3.multiplyByScalar(quaternion, thetaOverSinTheta, result); }; /** * The exponential quaternion function. * * @param {Cartesian3} cartesian The cartesian. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. */ Quaternion.exp = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var theta = Cartesian3.magnitude(cartesian); var sinThetaOverTheta = 0.0; if (theta !== 0.0) { sinThetaOverTheta = Math.sin(theta) / theta; } result.x = cartesian.x * sinThetaOverTheta; result.y = cartesian.y * sinThetaOverTheta; result.z = cartesian.z * sinThetaOverTheta; result.w = Math.cos(theta); return result; }; var squadScratchCartesian0 = new Cartesian3(); var squadScratchCartesian1 = new Cartesian3(); var squadScratchQuaternion0 = new Quaternion(); var squadScratchQuaternion1 = new Quaternion(); /** * Computes an inner quadrangle point. *

This will compute quaternions that ensure a squad curve is C1.

* * @param {Quaternion} q0 The first quaternion. * @param {Quaternion} q1 The second quaternion. * @param {Quaternion} q2 The third quaternion. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. * * @see Quaternion#squad */ Quaternion.computeInnerQuadrangle = function (q0, q1, q2, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("q0", q0); Check.typeOf.object("q1", q1); Check.typeOf.object("q2", q2); Check.typeOf.object("result", result); //>>includeEnd('debug'); var qInv = Quaternion.conjugate(q1, squadScratchQuaternion0); Quaternion.multiply(qInv, q2, squadScratchQuaternion1); var cart0 = Quaternion.log(squadScratchQuaternion1, squadScratchCartesian0); Quaternion.multiply(qInv, q0, squadScratchQuaternion1); var cart1 = Quaternion.log(squadScratchQuaternion1, squadScratchCartesian1); Cartesian3.add(cart0, cart1, cart0); Cartesian3.multiplyByScalar(cart0, 0.25, cart0); Cartesian3.negate(cart0, cart0); Quaternion.exp(cart0, squadScratchQuaternion0); return Quaternion.multiply(q1, squadScratchQuaternion0, result); }; /** * Computes the spherical quadrangle interpolation between quaternions. * * @param {Quaternion} q0 The first quaternion. * @param {Quaternion} q1 The second quaternion. * @param {Quaternion} s0 The first inner quadrangle. * @param {Quaternion} s1 The second inner quadrangle. * @param {Number} t The time in [0,1] used to interpolate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. * * * @example * // 1. compute the squad interpolation between two quaternions on a curve * var s0 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i - 1], quaternions[i], quaternions[i + 1], new Cesium.Quaternion()); * var s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i], quaternions[i + 1], quaternions[i + 2], new Cesium.Quaternion()); * var q = Cesium.Quaternion.squad(quaternions[i], quaternions[i + 1], s0, s1, t, new Cesium.Quaternion()); * * // 2. compute the squad interpolation as above but where the first quaternion is a end point. * var s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[0], quaternions[1], quaternions[2], new Cesium.Quaternion()); * var q = Cesium.Quaternion.squad(quaternions[0], quaternions[1], quaternions[0], s1, t, new Cesium.Quaternion()); * * @see Quaternion#computeInnerQuadrangle */ Quaternion.squad = function (q0, q1, s0, s1, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("q0", q0); Check.typeOf.object("q1", q1); Check.typeOf.object("s0", s0); Check.typeOf.object("s1", s1); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); var slerp0 = Quaternion.slerp(q0, q1, t, squadScratchQuaternion0); var slerp1 = Quaternion.slerp(s0, s1, t, squadScratchQuaternion1); return Quaternion.slerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result); }; var fastSlerpScratchQuaternion = new Quaternion(); var opmu = 1.90110745351730037; var u = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : []; var v = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : []; var bT = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : []; var bD = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : []; for (var i$5 = 0; i$5 < 7; ++i$5) { var s = i$5 + 1.0; var t = 2.0 * s + 1.0; u[i$5] = 1.0 / (s * t); v[i$5] = s / t; } u[7] = opmu / (8.0 * 17.0); v[7] = (opmu * 8.0) / 17.0; /** * Computes the spherical linear interpolation or extrapolation at t using the provided quaternions. * This implementation is faster than {@link Quaternion#slerp}, but is only accurate up to 10-6. * * @param {Quaternion} start The value corresponding to t at 0.0. * @param {Quaternion} end The value corresponding to t at 1.0. * @param {Number} t The point along t at which to interpolate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter. * * @see Quaternion#slerp */ Quaternion.fastSlerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = Quaternion.dot(start, end); var sign; if (x >= 0) { sign = 1.0; } else { sign = -1.0; x = -x; } var xm1 = x - 1.0; var d = 1.0 - t; var sqrT = t * t; var sqrD = d * d; for (var i = 7; i >= 0; --i) { bT[i] = (u[i] * sqrT - v[i]) * xm1; bD[i] = (u[i] * sqrD - v[i]) * xm1; } var cT = sign * t * (1.0 + bT[0] * (1.0 + bT[1] * (1.0 + bT[2] * (1.0 + bT[3] * (1.0 + bT[4] * (1.0 + bT[5] * (1.0 + bT[6] * (1.0 + bT[7])))))))); var cD = d * (1.0 + bD[0] * (1.0 + bD[1] * (1.0 + bD[2] * (1.0 + bD[3] * (1.0 + bD[4] * (1.0 + bD[5] * (1.0 + bD[6] * (1.0 + bD[7])))))))); var temp = Quaternion.multiplyByScalar(start, cD, fastSlerpScratchQuaternion); Quaternion.multiplyByScalar(end, cT, result); return Quaternion.add(temp, result, result); }; /** * Computes the spherical quadrangle interpolation between quaternions. * An implementation that is faster than {@link Quaternion#squad}, but less accurate. * * @param {Quaternion} q0 The first quaternion. * @param {Quaternion} q1 The second quaternion. * @param {Quaternion} s0 The first inner quadrangle. * @param {Quaternion} s1 The second inner quadrangle. * @param {Number} t The time in [0,1] used to interpolate. * @param {Quaternion} result The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new instance if none was provided. * * @see Quaternion#squad */ Quaternion.fastSquad = function (q0, q1, s0, s1, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("q0", q0); Check.typeOf.object("q1", q1); Check.typeOf.object("s0", s0); Check.typeOf.object("s1", s1); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); var slerp0 = Quaternion.fastSlerp(q0, q1, t, squadScratchQuaternion0); var slerp1 = Quaternion.fastSlerp(s0, s1, t, squadScratchQuaternion1); return Quaternion.fastSlerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result); }; /** * Compares the provided quaternions componentwise and returns * true if they are equal, false otherwise. * * @param {Quaternion} [left] The first quaternion. * @param {Quaternion} [right] The second quaternion. * @returns {Boolean} true if left and right are equal, false otherwise. */ Quaternion.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 quaternions componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Quaternion} [left] The first quaternion. * @param {Quaternion} [right] The second quaternion. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Quaternion.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0); 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 Quaternion instance initialized to (0.0, 0.0, 0.0, 0.0). * * @type {Quaternion} * @constant */ Quaternion.ZERO = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 0.0)); /** * An immutable Quaternion instance initialized to (0.0, 0.0, 0.0, 1.0). * * @type {Quaternion} * @constant */ Quaternion.IDENTITY = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 1.0)); /** * Duplicates this Quaternion instance. * * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. */ Quaternion.prototype.clone = function (result) { return Quaternion.clone(this, result); }; /** * Compares this and the provided quaternion componentwise and returns * true if they are equal, false otherwise. * * @param {Quaternion} [right] The right hand side quaternion. * @returns {Boolean} true if left and right are equal, false otherwise. */ Quaternion.prototype.equals = function (right) { return Quaternion.equals(this, right); }; /** * Compares this and the provided quaternion componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Quaternion} [right] The right hand side quaternion. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Quaternion.prototype.equalsEpsilon = function (right, epsilon) { return Quaternion.equalsEpsilon(this, right, epsilon); }; /** * Returns a string representing this quaternion in the format (x, y, z, w). * * @returns {String} A string representing this Quaternion. */ Quaternion.prototype.toString = function () { return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; }; /** * Contains functions for transforming positions to various reference frames. * * @namespace Transforms */ var Transforms = {}; var vectorProductLocalFrame = { up: { south: "east", north: "west", west: "south", east: "north", }, down: { south: "west", north: "east", west: "north", east: "south", }, south: { up: "west", down: "east", west: "down", east: "up", }, north: { up: "east", down: "west", west: "up", east: "down", }, west: { up: "north", down: "south", north: "down", south: "up", }, east: { up: "south", down: "north", north: "up", south: "down", }, }; var degeneratePositionLocalFrame = { north: [-1, 0, 0], east: [0, 1, 0], up: [0, 0, 1], south: [1, 0, 0], west: [0, -1, 0], down: [0, 0, -1], }; var localFrameToFixedFrameCache = {}; var scratchCalculateCartesian = { east: new Cartesian3(), north: new Cartesian3(), up: new Cartesian3(), west: new Cartesian3(), south: new Cartesian3(), down: new Cartesian3(), }; var scratchFirstCartesian = new Cartesian3(); var scratchSecondCartesian = new Cartesian3(); var scratchThirdCartesian = new Cartesian3(); /** * Generates a function that computes a 4x4 transformation matrix from a reference frame * centered at the provided origin to the provided ellipsoid's fixed reference frame. * @param {String} firstAxis name of the first axis of the local reference frame. Must be * 'east', 'north', 'up', 'west', 'south' or 'down'. * @param {String} secondAxis name of the second axis of the local reference frame. Must be * 'east', 'north', 'up', 'west', 'south' or 'down'. * @return {Transforms.LocalFrameToFixedFrame} The function that will computes a * 4x4 transformation matrix from a reference frame, with first axis and second axis compliant with the parameters, */ Transforms.localFrameToFixedFrameGenerator = function (firstAxis, secondAxis) { if ( !vectorProductLocalFrame.hasOwnProperty(firstAxis) || !vectorProductLocalFrame[firstAxis].hasOwnProperty(secondAxis) ) { throw new DeveloperError( "firstAxis and secondAxis must be east, north, up, west, south or down." ); } var thirdAxis = vectorProductLocalFrame[firstAxis][secondAxis]; /** * Computes a 4x4 transformation matrix from a reference frame * centered at the provided origin to the provided ellipsoid's fixed reference frame. * @callback Transforms.LocalFrameToFixedFrame * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. */ var resultat; var hashAxis = firstAxis + secondAxis; if (defined(localFrameToFixedFrameCache[hashAxis])) { resultat = localFrameToFixedFrameCache[hashAxis]; } else { resultat = function (origin, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); if (!defined(origin)) { throw new DeveloperError("origin is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix4(); } if ( Cartesian3.equalsEpsilon(origin, Cartesian3.ZERO, CesiumMath.EPSILON14) ) { // If x, y, and z are zero, use the degenerate local frame, which is a special case Cartesian3.unpack( degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian ); Cartesian3.unpack( degeneratePositionLocalFrame[secondAxis], 0, scratchSecondCartesian ); Cartesian3.unpack( degeneratePositionLocalFrame[thirdAxis], 0, scratchThirdCartesian ); } else if ( CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) && CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14) ) { // If x and y are zero, assume origin is at a pole, which is a special case. var sign = CesiumMath.sign(origin.z); Cartesian3.unpack( degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian ); if (firstAxis !== "east" && firstAxis !== "west") { Cartesian3.multiplyByScalar( scratchFirstCartesian, sign, scratchFirstCartesian ); } Cartesian3.unpack( degeneratePositionLocalFrame[secondAxis], 0, scratchSecondCartesian ); if (secondAxis !== "east" && secondAxis !== "west") { Cartesian3.multiplyByScalar( scratchSecondCartesian, sign, scratchSecondCartesian ); } Cartesian3.unpack( degeneratePositionLocalFrame[thirdAxis], 0, scratchThirdCartesian ); if (thirdAxis !== "east" && thirdAxis !== "west") { Cartesian3.multiplyByScalar( scratchThirdCartesian, sign, scratchThirdCartesian ); } } else { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up); var up = scratchCalculateCartesian.up; var east = scratchCalculateCartesian.east; east.x = -origin.y; east.y = origin.x; east.z = 0.0; Cartesian3.normalize(east, scratchCalculateCartesian.east); Cartesian3.cross(up, east, scratchCalculateCartesian.north); Cartesian3.multiplyByScalar( scratchCalculateCartesian.up, -1, scratchCalculateCartesian.down ); Cartesian3.multiplyByScalar( scratchCalculateCartesian.east, -1, scratchCalculateCartesian.west ); Cartesian3.multiplyByScalar( scratchCalculateCartesian.north, -1, scratchCalculateCartesian.south ); scratchFirstCartesian = scratchCalculateCartesian[firstAxis]; scratchSecondCartesian = scratchCalculateCartesian[secondAxis]; scratchThirdCartesian = scratchCalculateCartesian[thirdAxis]; } result[0] = scratchFirstCartesian.x; result[1] = scratchFirstCartesian.y; result[2] = scratchFirstCartesian.z; result[3] = 0.0; result[4] = scratchSecondCartesian.x; result[5] = scratchSecondCartesian.y; result[6] = scratchSecondCartesian.z; result[7] = 0.0; result[8] = scratchThirdCartesian.x; result[9] = scratchThirdCartesian.y; result[10] = scratchThirdCartesian.z; result[11] = 0.0; result[12] = origin.x; result[13] = origin.y; result[14] = origin.z; result[15] = 1.0; return result; }; localFrameToFixedFrameCache[hashAxis] = resultat; } return resultat; }; /** * Computes a 4x4 transformation matrix from a reference frame with an east-north-up axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: *
    *
  • The x axis points in the local east direction.
  • *
  • The y axis points in the local north direction.
  • *
  • The z axis points in the direction of the ellipsoid surface normal which passes through the position.
  • *
* * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local east-north-up at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center); */ Transforms.eastNorthUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "east", "north" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-east-down axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: *
    *
  • The x axis points in the local north direction.
  • *
  • The y axis points in the local east direction.
  • *
  • The z axis points in the opposite direction of the ellipsoid surface normal which passes through the position.
  • *
* * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-east-down at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northEastDownToFixedFrame(center); */ Transforms.northEastDownToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "east" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-up-east axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: *
    *
  • The x axis points in the local north direction.
  • *
  • The y axis points in the direction of the ellipsoid surface normal which passes through the position.
  • *
  • The z axis points in the local east direction.
  • *
* * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-up-east at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northUpEastToFixedFrame(center); */ Transforms.northUpEastToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "up" ); /** * Computes a 4x4 transformation matrix from a reference frame with an north-west-up axes * centered at the provided origin to the provided ellipsoid's fixed reference frame. * The local axes are defined as: *
    *
  • The x axis points in the local north direction.
  • *
  • The y axis points in the local west direction.
  • *
  • The z axis points in the direction of the ellipsoid surface normal which passes through the position.
  • *
* * @function * @param {Cartesian3} origin The center point of the local reference frame. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local north-West-Up at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var transform = Cesium.Transforms.northWestUpToFixedFrame(center); */ Transforms.northWestUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator( "north", "west" ); var scratchHPRQuaternion = new Quaternion(); var scratchScale$6 = new Cartesian3(1.0, 1.0, 1.0); var scratchHPRMatrix4 = new Matrix4(); /** * Computes a 4x4 transformation matrix from a reference frame with axes computed from the heading-pitch-roll angles * centered at the provided origin to the provided ellipsoid's fixed reference frame. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Cartesian3} origin The center point of the local reference frame. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {Matrix4} [result] The object onto which to store the result. * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided. * * @example * // Get the transform from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var heading = -Cesium.Math.PI_OVER_TWO; * var pitch = Cesium.Math.PI_OVER_FOUR; * var roll = 0.0; * var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll); * var transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr); */ Transforms.headingPitchRollToFixedFrame = function ( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("HeadingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); fixedFrameTransform = defaultValue( fixedFrameTransform, Transforms.eastNorthUpToFixedFrame ); var hprQuaternion = Quaternion.fromHeadingPitchRoll( headingPitchRoll, scratchHPRQuaternion ); var hprMatrix = Matrix4.fromTranslationQuaternionRotationScale( Cartesian3.ZERO, hprQuaternion, scratchScale$6, scratchHPRMatrix4 ); result = fixedFrameTransform(origin, ellipsoid, result); return Matrix4.multiply(result, hprMatrix, result); }; var scratchENUMatrix4 = new Matrix4(); var scratchHPRMatrix3 = new Matrix3(); /** * Computes a quaternion from a reference frame with axes computed from the heading-pitch-roll angles * centered at the provided origin. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Cartesian3} origin The center point of the local reference frame. * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided. * * @example * // Get the quaternion from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame. * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var heading = -Cesium.Math.PI_OVER_TWO; * var pitch = Cesium.Math.PI_OVER_FOUR; * var roll = 0.0; * var hpr = new HeadingPitchRoll(heading, pitch, roll); * var quaternion = Cesium.Transforms.headingPitchRollQuaternion(center, hpr); */ Transforms.headingPitchRollQuaternion = function ( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("HeadingPitchRoll", headingPitchRoll); //>>includeEnd('debug'); var transform = Transforms.headingPitchRollToFixedFrame( origin, headingPitchRoll, ellipsoid, fixedFrameTransform, scratchENUMatrix4 ); var rotation = Matrix4.getMatrix3(transform, scratchHPRMatrix3); return Quaternion.fromRotationMatrix(rotation, result); }; var noScale = new Cartesian3(1.0, 1.0, 1.0); var hprCenterScratch = new Cartesian3(); var ffScratch = new Matrix4(); var hprTransformScratch = new Matrix4(); var hprRotationScratch = new Matrix3(); var hprQuaternionScratch = new Quaternion(); /** * Computes heading-pitch-roll angles from a transform in a particular reference frame. Heading is the rotation from the local north * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis. * * @param {Matrix4} transform The transform * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation * matrix from a reference frame to the provided ellipsoid's fixed reference frame * @param {HeadingPitchRoll} [result] The object onto which to store the result. * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if none was provided. */ Transforms.fixedFrameToHeadingPitchRoll = function ( transform, ellipsoid, fixedFrameTransform, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("transform", transform); //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); fixedFrameTransform = defaultValue( fixedFrameTransform, Transforms.eastNorthUpToFixedFrame ); if (!defined(result)) { result = new HeadingPitchRoll(); } var center = Matrix4.getTranslation(transform, hprCenterScratch); if (Cartesian3.equals(center, Cartesian3.ZERO)) { result.heading = 0; result.pitch = 0; result.roll = 0; return result; } var toFixedFrame = Matrix4.inverseTransformation( fixedFrameTransform(center, ellipsoid, ffScratch), ffScratch ); var transformCopy = Matrix4.setScale(transform, noScale, hprTransformScratch); transformCopy = Matrix4.setTranslation( transformCopy, Cartesian3.ZERO, transformCopy ); toFixedFrame = Matrix4.multiply(toFixedFrame, transformCopy, toFixedFrame); var quaternionRotation = Quaternion.fromRotationMatrix( Matrix4.getMatrix3(toFixedFrame, hprRotationScratch), hprQuaternionScratch ); quaternionRotation = Quaternion.normalize( quaternionRotation, quaternionRotation ); return HeadingPitchRoll.fromQuaternion(quaternionRotation, result); }; var gmstConstant0 = 6 * 3600 + 41 * 60 + 50.54841; var gmstConstant1 = 8640184.812866; var gmstConstant2 = 0.093104; var gmstConstant3 = -6.2e-6; var rateCoef = 1.1772758384668e-19; var wgs84WRPrecessing = 7.2921158553e-5; var twoPiOverSecondsInDay = CesiumMath.TWO_PI / 86400.0; var dateInUtc = new JulianDate(); /** * Computes a rotation matrix to transform a point or vector from True Equator Mean Equinox (TEME) axes to the * pseudo-fixed axes at a given time. This method treats the UT1 time standard as equivalent to UTC. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided. * * @example * //Set the view to the inertial frame. * scene.postUpdate.addEventListener(function(scene, time) { * var now = Cesium.JulianDate.now(); * var offset = Cesium.Matrix4.multiplyByPoint(camera.transform, camera.position, new Cesium.Cartesian3()); * var transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Transforms.computeTemeToPseudoFixedMatrix(now)); * var inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4()); * Cesium.Matrix4.multiplyByPoint(inverseTransform, offset, offset); * camera.lookAtTransform(transform, offset); * }); */ Transforms.computeTemeToPseudoFixedMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); // GMST is actually computed using UT1. We're using UTC as an approximation of UT1. // We do not want to use the function like convertTaiToUtc in JulianDate because // we explicitly do not want to fail when inside the leap second. dateInUtc = JulianDate.addSeconds( date, -JulianDate.computeTaiMinusUtc(date), dateInUtc ); var utcDayNumber = dateInUtc.dayNumber; var utcSecondsIntoDay = dateInUtc.secondsOfDay; var t; var diffDays = utcDayNumber - 2451545; if (utcSecondsIntoDay >= 43200.0) { t = (diffDays + 0.5) / TimeConstants$1.DAYS_PER_JULIAN_CENTURY; } else { t = (diffDays - 0.5) / TimeConstants$1.DAYS_PER_JULIAN_CENTURY; } var gmst0 = gmstConstant0 + t * (gmstConstant1 + t * (gmstConstant2 + t * gmstConstant3)); var angle = (gmst0 * twoPiOverSecondsInDay) % CesiumMath.TWO_PI; var ratio = wgs84WRPrecessing + rateCoef * (utcDayNumber - 2451545.5); var secondsSinceMidnight = (utcSecondsIntoDay + TimeConstants$1.SECONDS_PER_DAY * 0.5) % TimeConstants$1.SECONDS_PER_DAY; var gha = angle + ratio * secondsSinceMidnight; var cosGha = Math.cos(gha); var sinGha = Math.sin(gha); if (!defined(result)) { return new Matrix3( cosGha, sinGha, 0.0, -sinGha, cosGha, 0.0, 0.0, 0.0, 1.0 ); } result[0] = cosGha; result[1] = -sinGha; result[2] = 0.0; result[3] = sinGha; result[4] = cosGha; result[5] = 0.0; result[6] = 0.0; result[7] = 0.0; result[8] = 1.0; return result; }; /** * The source of IAU 2006 XYS data, used for computing the transformation between the * Fixed and ICRF axes. * @type {Iau2006XysData} * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * * @private */ Transforms.iau2006XysData = new Iau2006XysData(); /** * The source of Earth Orientation Parameters (EOP) data, used for computing the transformation * between the Fixed and ICRF axes. By default, zero values are used for all EOP values, * yielding a reasonable but not completely accurate representation of the ICRF axes. * @type {EarthOrientationParameters} * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * * @private */ Transforms.earthOrientationParameters = EarthOrientationParameters.NONE; var ttMinusTai = 32.184; var j2000ttDays = 2451545.0; /** * Preloads the data necessary to transform between the ICRF and Fixed axes, in either * direction, over a given interval. This function returns a promise that, when resolved, * indicates that the preload has completed. * * @param {TimeInterval} timeInterval The interval to preload. * @returns {Promise} A promise that, when resolved, indicates that the preload has completed * and evaluation of the transformation between the fixed and ICRF axes will * no longer return undefined for a time inside the interval. * * * @example * var interval = new Cesium.TimeInterval(...); * when(Cesium.Transforms.preloadIcrfFixed(interval), function() { * // the data is now loaded * }); * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * @see when */ Transforms.preloadIcrfFixed = function (timeInterval) { var startDayTT = timeInterval.start.dayNumber; var startSecondTT = timeInterval.start.secondsOfDay + ttMinusTai; var stopDayTT = timeInterval.stop.dayNumber; var stopSecondTT = timeInterval.stop.secondsOfDay + ttMinusTai; var xysPromise = Transforms.iau2006XysData.preload( startDayTT, startSecondTT, stopDayTT, stopSecondTT ); var eopPromise = Transforms.earthOrientationParameters.getPromiseToLoad(); return when.all([xysPromise, eopPromise]); }; /** * Computes a rotation matrix to transform a point or vector from the International Celestial * Reference Frame (GCRF/ICRF) inertial frame axes to the Earth-Fixed frame axes (ITRF) * at a given time. This function may return undefined if the data necessary to * do the transformation is not yet loaded. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is * not specified, a new instance is created and returned. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the * transformation is not yet loaded. * * * @example * scene.postUpdate.addEventListener(function(scene, time) { * // View in ICRF. * var icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time); * if (Cesium.defined(icrfToFixed)) { * var offset = Cesium.Cartesian3.clone(camera.position); * var transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed); * camera.lookAtTransform(transform, offset); * } * }); * * @see Transforms.preloadIcrfFixed */ Transforms.computeIcrfToFixedMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix3(); } var fixedToIcrfMtx = Transforms.computeFixedToIcrfMatrix(date, result); if (!defined(fixedToIcrfMtx)) { return undefined; } return Matrix3.transpose(fixedToIcrfMtx, result); }; var xysScratch = new Iau2006XysSample(0.0, 0.0, 0.0); var eopScratch = new EarthOrientationParametersSample( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ); var rotation1Scratch = new Matrix3(); var rotation2Scratch = new Matrix3(); /** * Computes a rotation matrix to transform a point or vector from the Earth-Fixed frame axes (ITRF) * to the International Celestial Reference Frame (GCRF/ICRF) inertial frame axes * at a given time. This function may return undefined if the data necessary to * do the transformation is not yet loaded. * * @param {JulianDate} date The time at which to compute the rotation matrix. * @param {Matrix3} [result] The object onto which to store the result. If this parameter is * not specified, a new instance is created and returned. * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the * transformation is not yet loaded. * * * @example * // Transform a point from the ICRF axes to the Fixed axes. * var now = Cesium.JulianDate.now(); * var pointInFixed = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var fixedToIcrf = Cesium.Transforms.computeIcrfToFixedMatrix(now); * var pointInInertial = new Cesium.Cartesian3(); * if (Cesium.defined(fixedToIcrf)) { * pointInInertial = Cesium.Matrix3.multiplyByVector(fixedToIcrf, pointInFixed, pointInInertial); * } * * @see Transforms.preloadIcrfFixed */ Transforms.computeFixedToIcrfMatrix = function (date, result) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Matrix3(); } // Compute pole wander var eop = Transforms.earthOrientationParameters.compute(date, eopScratch); if (!defined(eop)) { return undefined; } // There is no external conversion to Terrestrial Time (TT). // So use International Atomic Time (TAI) and convert using offsets. // Here we are assuming that dayTT and secondTT are positive var dayTT = date.dayNumber; // It's possible here that secondTT could roll over 86400 // This does not seem to affect the precision (unit tests check for this) var secondTT = date.secondsOfDay + ttMinusTai; var xys = Transforms.iau2006XysData.computeXysRadians( dayTT, secondTT, xysScratch ); if (!defined(xys)) { return undefined; } var x = xys.x + eop.xPoleOffset; var y = xys.y + eop.yPoleOffset; // Compute XYS rotation var a = 1.0 / (1.0 + Math.sqrt(1.0 - x * x - y * y)); var rotation1 = rotation1Scratch; rotation1[0] = 1.0 - a * x * x; rotation1[3] = -a * x * y; rotation1[6] = x; rotation1[1] = -a * x * y; rotation1[4] = 1 - a * y * y; rotation1[7] = y; rotation1[2] = -x; rotation1[5] = -y; rotation1[8] = 1 - a * (x * x + y * y); var rotation2 = Matrix3.fromRotationZ(-xys.s, rotation2Scratch); var matrixQ = Matrix3.multiply(rotation1, rotation2, rotation1Scratch); // Similar to TT conversions above // It's possible here that secondTT could roll over 86400 // This does not seem to affect the precision (unit tests check for this) var dateUt1day = date.dayNumber; var dateUt1sec = date.secondsOfDay - JulianDate.computeTaiMinusUtc(date) + eop.ut1MinusUtc; // Compute Earth rotation angle // The IERS standard for era is // era = 0.7790572732640 + 1.00273781191135448 * Tu // where // Tu = JulianDateInUt1 - 2451545.0 // However, you get much more precision if you make the following simplification // era = a + (1 + b) * (JulianDayNumber + FractionOfDay - 2451545) // era = a + (JulianDayNumber - 2451545) + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay) // era = a + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay) // since (JulianDayNumber - 2451545) represents an integer number of revolutions which will be discarded anyway. var daysSinceJ2000 = dateUt1day - 2451545; var fractionOfDay = dateUt1sec / TimeConstants$1.SECONDS_PER_DAY; var era = 0.779057273264 + fractionOfDay + 0.00273781191135448 * (daysSinceJ2000 + fractionOfDay); era = (era % 1.0) * CesiumMath.TWO_PI; var earthRotation = Matrix3.fromRotationZ(era, rotation2Scratch); // pseudoFixed to ICRF var pfToIcrf = Matrix3.multiply(matrixQ, earthRotation, rotation1Scratch); // Compute pole wander matrix var cosxp = Math.cos(eop.xPoleWander); var cosyp = Math.cos(eop.yPoleWander); var sinxp = Math.sin(eop.xPoleWander); var sinyp = Math.sin(eop.yPoleWander); var ttt = dayTT - j2000ttDays + secondTT / TimeConstants$1.SECONDS_PER_DAY; ttt /= 36525.0; // approximate sp value in rad var sp = (-47.0e-6 * ttt * CesiumMath.RADIANS_PER_DEGREE) / 3600.0; var cossp = Math.cos(sp); var sinsp = Math.sin(sp); var fToPfMtx = rotation2Scratch; fToPfMtx[0] = cosxp * cossp; fToPfMtx[1] = cosxp * sinsp; fToPfMtx[2] = sinxp; fToPfMtx[3] = -cosyp * sinsp + sinyp * sinxp * cossp; fToPfMtx[4] = cosyp * cossp + sinyp * sinxp * sinsp; fToPfMtx[5] = -sinyp * cosxp; fToPfMtx[6] = -sinyp * sinsp - cosyp * sinxp * cossp; fToPfMtx[7] = sinyp * cossp - cosyp * sinxp * sinsp; fToPfMtx[8] = cosyp * cosxp; return Matrix3.multiply(pfToIcrf, fToPfMtx, result); }; var pointToWindowCoordinatesTemp = new Cartesian4(); /** * Transform a point from model coordinates to window coordinates. * * @param {Matrix4} modelViewProjectionMatrix The 4x4 model-view-projection matrix. * @param {Matrix4} viewportTransformation The 4x4 viewport transformation. * @param {Cartesian3} point The point to transform. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. */ Transforms.pointToWindowCoordinates = function ( modelViewProjectionMatrix, viewportTransformation, point, result ) { result = Transforms.pointToGLWindowCoordinates( modelViewProjectionMatrix, viewportTransformation, point, result ); result.y = 2.0 * viewportTransformation[5] - result.y; return result; }; /** * @private */ Transforms.pointToGLWindowCoordinates = function ( modelViewProjectionMatrix, viewportTransformation, point, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(modelViewProjectionMatrix)) { throw new DeveloperError("modelViewProjectionMatrix is required."); } if (!defined(viewportTransformation)) { throw new DeveloperError("viewportTransformation is required."); } if (!defined(point)) { throw new DeveloperError("point is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian2(); } var tmp = pointToWindowCoordinatesTemp; Matrix4.multiplyByVector( modelViewProjectionMatrix, Cartesian4.fromElements(point.x, point.y, point.z, 1, tmp), tmp ); Cartesian4.multiplyByScalar(tmp, 1.0 / tmp.w, tmp); Matrix4.multiplyByVector(viewportTransformation, tmp, tmp); return Cartesian2.fromCartesian4(tmp, result); }; var normalScratch$5 = new Cartesian3(); var rightScratch$1 = new Cartesian3(); var upScratch = new Cartesian3(); /** * Transform a position and velocity to a rotation matrix. * * @param {Cartesian3} position The position to transform. * @param {Cartesian3} velocity The velocity vector to transform. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @param {Matrix3} [result] The object onto which to store the result. * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided. */ Transforms.rotationMatrixFromPositionVelocity = function ( position, velocity, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(position)) { throw new DeveloperError("position is required."); } if (!defined(velocity)) { throw new DeveloperError("velocity is required."); } //>>includeEnd('debug'); var normal = defaultValue(ellipsoid, Ellipsoid.WGS84).geodeticSurfaceNormal( position, normalScratch$5 ); var right = Cartesian3.cross(velocity, normal, rightScratch$1); if (Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) { right = Cartesian3.clone(Cartesian3.UNIT_X, right); } var up = Cartesian3.cross(right, velocity, upScratch); Cartesian3.normalize(up, up); Cartesian3.cross(velocity, up, right); Cartesian3.negate(right, right); Cartesian3.normalize(right, right); if (!defined(result)) { result = new Matrix3(); } result[0] = velocity.x; result[1] = velocity.y; result[2] = velocity.z; result[3] = right.x; result[4] = right.y; result[5] = right.z; result[6] = up.x; result[7] = up.y; result[8] = up.z; return result; }; var swizzleMatrix = new Matrix4( 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ); var scratchCartographic$f = new Cartographic(); var scratchCartesian3Projection$1 = new Cartesian3(); var scratchCenter$6 = new Cartesian3(); var scratchRotation$2 = new Matrix3(); var scratchFromENU = new Matrix4(); var scratchToENU = new Matrix4(); /** * @private */ Transforms.basisTo2D = function (projection, matrix, result) { //>>includeStart('debug', pragmas.debug); if (!defined(projection)) { throw new DeveloperError("projection is required."); } if (!defined(matrix)) { throw new DeveloperError("matrix is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var rtcCenter = Matrix4.getTranslation(matrix, scratchCenter$6); var ellipsoid = projection.ellipsoid; // Get the 2D Center var cartographic = ellipsoid.cartesianToCartographic( rtcCenter, scratchCartographic$f ); var projectedPosition = projection.project( cartographic, scratchCartesian3Projection$1 ); Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition ); // Assuming the instance are positioned in WGS84, invert the WGS84 transform to get the local transform and then convert to 2D var fromENU = Transforms.eastNorthUpToFixedFrame( rtcCenter, ellipsoid, scratchFromENU ); var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); var rotation = Matrix4.getMatrix3(matrix, scratchRotation$2); var local = Matrix4.multiplyByMatrix3(toENU, rotation, result); Matrix4.multiply(swizzleMatrix, local, result); // Swap x, y, z for 2D Matrix4.setTranslation(result, projectedPosition, result); // Use the projected center return result; }; /** * @private */ Transforms.wgs84To2DModelMatrix = function (projection, center, result) { //>>includeStart('debug', pragmas.debug); if (!defined(projection)) { throw new DeveloperError("projection is required."); } if (!defined(center)) { throw new DeveloperError("center is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var ellipsoid = projection.ellipsoid; var fromENU = Transforms.eastNorthUpToFixedFrame( center, ellipsoid, scratchFromENU ); var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU); var cartographic = ellipsoid.cartesianToCartographic( center, scratchCartographic$f ); var projectedPosition = projection.project( cartographic, scratchCartesian3Projection$1 ); Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, projectedPosition.y, projectedPosition ); var translation = Matrix4.fromTranslation(projectedPosition, scratchFromENU); Matrix4.multiply(swizzleMatrix, toENU, result); Matrix4.multiply(translation, result, result); return result; }; /** * A simple map projection where longitude and latitude are linearly mapped to X and Y by multiplying * them by the {@link Ellipsoid#maximumRadius}. This projection * is commonly known as geographic, equirectangular, equidistant cylindrical, or plate carrée. It * is also known as EPSG:4326. * * @alias GeographicProjection * @constructor * * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid. * * @see WebMercatorProjection */ function GeographicProjection(ellipsoid) { this._ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); this._semimajorAxis = this._ellipsoid.maximumRadius; this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis; } Object.defineProperties(GeographicProjection.prototype, { /** * Gets the {@link Ellipsoid}. * * @memberof GeographicProjection.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: 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. * * @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. * * @param {Cartesian3} cartesian The Cartesian position to unproject with height (z) in meters. * @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) { //>>includeStart('debug', pragmas.debug); if (!defined(cartesian)) { throw new DeveloperError("cartesian is required"); } //>>includeEnd('debug'); 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; }; /** * 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 somewhere entirely * outside of the frustum's 6 planes (OUTSIDE). * * @enum {Number} */ var Intersect = { /** * Represents that an object is not contained within the frustum. * * @type {Number} * @constant */ OUTSIDE: -1, /** * Represents that an object intersects one of the frustum's planes. * * @type {Number} * @constant */ INTERSECTING: 0, /** * Represents that an object is fully within the frustum. * * @type {Number} * @constant */ INSIDE: 1, }; var Intersect$1 = Object.freeze(Intersect); /** * 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. */ function Interval(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); } /** * A two dimensional region specified as longitude and latitude coordinates. * * @alias Rectangle * @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]. * * @see Packable */ function Rectangle(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); } Object.defineProperties(Rectangle.prototype, { /** * Gets the width of the rectangle in radians. * @memberof Rectangle.prototype * @type {Number} * @readonly */ width: { get: function () { return Rectangle.computeWidth(this); }, }, /** * Gets the height of the rectangle in radians. * @memberof Rectangle.prototype * @type {Number} * @readonly */ height: { get: function () { return Rectangle.computeHeight(this); }, }, }); /** * The number of elements used to pack the object into an array. * @type {Number} */ Rectangle.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {Rectangle} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Rectangle.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.west; array[startingIndex++] = value.south; array[startingIndex++] = value.east; array[startingIndex] = value.north; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Rectangle} [result] The object into which to store the result. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if one was not provided. */ Rectangle.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Rectangle(); } result.west = array[startingIndex++]; result.south = array[startingIndex++]; result.east = array[startingIndex++]; result.north = array[startingIndex]; return result; }; /** * Computes the width of a rectangle in radians. * @param {Rectangle} rectangle The rectangle to compute the width of. * @returns {Number} The width. */ Rectangle.computeWidth = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var east = rectangle.east; var west = rectangle.west; if (east < west) { east += CesiumMath.TWO_PI; } return east - west; }; /** * Computes the height of a rectangle in radians. * @param {Rectangle} rectangle The rectangle to compute the height of. * @returns {Number} The height. */ Rectangle.computeHeight = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); return rectangle.north - rectangle.south; }; /** * Creates a rectangle given the boundary longitude and latitude in degrees. * * @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 {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. * * @example * var rectangle = Cesium.Rectangle.fromDegrees(0.0, 20.0, 10.0, 30.0); */ Rectangle.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 Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Creates a rectangle given the boundary longitude and latitude in radians. * * @param {Number} [west=0.0] The westernmost longitude in radians in the range [-Math.PI, Math.PI]. * @param {Number} [south=0.0] The southernmost latitude in radians in the range [-Math.PI/2, Math.PI/2]. * @param {Number} [east=0.0] The easternmost longitude in radians in the range [-Math.PI, Math.PI]. * @param {Number} [north=0.0] The northernmost latitude in radians in the range [-Math.PI/2, Math.PI/2]. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. * * @example * var rectangle = Cesium.Rectangle.fromRadians(0.0, Math.PI/4, Math.PI/8, 3*Math.PI/4); */ Rectangle.fromRadians = function (west, south, east, north, result) { if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = defaultValue(west, 0.0); result.south = defaultValue(south, 0.0); result.east = defaultValue(east, 0.0); result.north = defaultValue(north, 0.0); return result; }; /** * Creates the smallest possible Rectangle that encloses all positions in the provided array. * * @param {Cartographic[]} cartographics The list of Cartographic instances. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. */ Rectangle.fromCartographicArray = function (cartographics, result) { //>>includeStart('debug', pragmas.debug); Check.defined("cartographics", cartographics); //>>includeEnd('debug'); var west = Number.MAX_VALUE; var east = -Number.MAX_VALUE; var westOverIDL = Number.MAX_VALUE; var eastOverIDL = -Number.MAX_VALUE; var south = Number.MAX_VALUE; var north = -Number.MAX_VALUE; for (var i = 0, len = cartographics.length; i < len; i++) { var position = cartographics[i]; west = Math.min(west, position.longitude); east = Math.max(east, position.longitude); south = Math.min(south, position.latitude); north = Math.max(north, position.latitude); var lonAdjusted = position.longitude >= 0 ? position.longitude : position.longitude + CesiumMath.TWO_PI; westOverIDL = Math.min(westOverIDL, lonAdjusted); eastOverIDL = Math.max(eastOverIDL, lonAdjusted); } if (east - west > eastOverIDL - westOverIDL) { west = westOverIDL; east = eastOverIDL; if (east > CesiumMath.PI) { east = east - CesiumMath.TWO_PI; } if (west > CesiumMath.PI) { west = west - CesiumMath.TWO_PI; } } if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Creates the smallest possible Rectangle that encloses all positions in the provided array. * * @param {Cartesian3[]} cartesians The list of Cartesian instances. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid the cartesians are on. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. */ Rectangle.fromCartesianArray = function (cartesians, ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var west = Number.MAX_VALUE; var east = -Number.MAX_VALUE; var westOverIDL = Number.MAX_VALUE; var eastOverIDL = -Number.MAX_VALUE; var south = Number.MAX_VALUE; var north = -Number.MAX_VALUE; for (var i = 0, len = cartesians.length; i < len; i++) { var position = ellipsoid.cartesianToCartographic(cartesians[i]); west = Math.min(west, position.longitude); east = Math.max(east, position.longitude); south = Math.min(south, position.latitude); north = Math.max(north, position.latitude); var lonAdjusted = position.longitude >= 0 ? position.longitude : position.longitude + CesiumMath.TWO_PI; westOverIDL = Math.min(westOverIDL, lonAdjusted); eastOverIDL = Math.max(eastOverIDL, lonAdjusted); } if (east - west > eastOverIDL - westOverIDL) { west = westOverIDL; east = eastOverIDL; if (east > CesiumMath.PI) { east = east - CesiumMath.TWO_PI; } if (west > CesiumMath.PI) { west = west - CesiumMath.TWO_PI; } } if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Duplicates a Rectangle. * * @param {Rectangle} rectangle The rectangle to clone. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. (Returns undefined if rectangle is undefined) */ Rectangle.clone = function (rectangle, result) { if (!defined(rectangle)) { return undefined; } if (!defined(result)) { return new Rectangle( rectangle.west, rectangle.south, rectangle.east, rectangle.north ); } result.west = rectangle.west; result.south = rectangle.south; result.east = rectangle.east; result.north = rectangle.north; return result; }; /** * Compares the provided Rectangles componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {Rectangle} [left] The first Rectangle. * @param {Rectangle} [right] The second Rectangle. * @param {Number} [absoluteEpsilon=0] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Rectangle.equalsEpsilon = function (left, right, absoluteEpsilon) { absoluteEpsilon = defaultValue(absoluteEpsilon, 0); return ( left === right || (defined(left) && defined(right) && Math.abs(left.west - right.west) <= absoluteEpsilon && Math.abs(left.south - right.south) <= absoluteEpsilon && Math.abs(left.east - right.east) <= absoluteEpsilon && Math.abs(left.north - right.north) <= absoluteEpsilon) ); }; /** * Duplicates this Rectangle. * * @param {Rectangle} [result] The object onto which to store the result. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. */ Rectangle.prototype.clone = function (result) { return Rectangle.clone(this, result); }; /** * Compares the provided Rectangle with this Rectangle componentwise and returns * true if they are equal, false otherwise. * * @param {Rectangle} [other] The Rectangle to compare. * @returns {Boolean} true if the Rectangles are equal, false otherwise. */ Rectangle.prototype.equals = function (other) { return Rectangle.equals(this, other); }; /** * Compares the provided rectangles and returns true if they are equal, * false otherwise. * * @param {Rectangle} [left] The first Rectangle. * @param {Rectangle} [right] The second Rectangle. * @returns {Boolean} true if left and right are equal; otherwise false. */ Rectangle.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 Rectangle with this Rectangle componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Rectangle} [other] The Rectangle to compare. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if the Rectangles are within the provided epsilon, false otherwise. */ Rectangle.prototype.equalsEpsilon = function (other, epsilon) { return Rectangle.equalsEpsilon(this, other, epsilon); }; /** * Checks a Rectangle's properties and throws if they are not in valid ranges. * * @param {Rectangle} rectangle The rectangle to validate * * @exception {DeveloperError} north must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} south must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} east must be in the interval [-Pi, Pi]. * @exception {DeveloperError} west must be in the interval [-Pi, Pi]. */ Rectangle.validate = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); var north = rectangle.north; Check.typeOf.number.greaterThanOrEquals( "north", north, -CesiumMath.PI_OVER_TWO ); Check.typeOf.number.lessThanOrEquals("north", north, CesiumMath.PI_OVER_TWO); var south = rectangle.south; Check.typeOf.number.greaterThanOrEquals( "south", south, -CesiumMath.PI_OVER_TWO ); Check.typeOf.number.lessThanOrEquals("south", south, CesiumMath.PI_OVER_TWO); var west = rectangle.west; Check.typeOf.number.greaterThanOrEquals("west", west, -Math.PI); Check.typeOf.number.lessThanOrEquals("west", west, Math.PI); var east = rectangle.east; Check.typeOf.number.greaterThanOrEquals("east", east, -Math.PI); Check.typeOf.number.lessThanOrEquals("east", east, Math.PI); //>>includeEnd('debug'); }; /** * Computes the southwest corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @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. */ Rectangle.southwest = function (rectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); if (!defined(result)) { return new Cartographic(rectangle.west, rectangle.south); } result.longitude = rectangle.west; result.latitude = rectangle.south; result.height = 0.0; return result; }; /** * Computes the northwest corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @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. */ Rectangle.northwest = function (rectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); if (!defined(result)) { return new Cartographic(rectangle.west, rectangle.north); } result.longitude = rectangle.west; result.latitude = rectangle.north; result.height = 0.0; return result; }; /** * Computes the northeast corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @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. */ Rectangle.northeast = function (rectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); if (!defined(result)) { return new Cartographic(rectangle.east, rectangle.north); } result.longitude = rectangle.east; result.latitude = rectangle.north; result.height = 0.0; return result; }; /** * Computes the southeast corner of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the corner * @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. */ Rectangle.southeast = function (rectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); if (!defined(result)) { return new Cartographic(rectangle.east, rectangle.south); } result.longitude = rectangle.east; result.latitude = rectangle.south; result.height = 0.0; return result; }; /** * Computes the center of a rectangle. * * @param {Rectangle} rectangle The rectangle for which to find the center * @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. */ Rectangle.center = function (rectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var east = rectangle.east; var west = rectangle.west; if (east < west) { east += CesiumMath.TWO_PI; } var longitude = CesiumMath.negativePiToPi((west + east) * 0.5); var latitude = (rectangle.south + rectangle.north) * 0.5; if (!defined(result)) { return new Cartographic(longitude, latitude); } result.longitude = longitude; result.latitude = latitude; result.height = 0.0; return result; }; /** * Computes the intersection of two rectangles. This function assumes that the rectangle's coordinates are * latitude and longitude in radians and produces a correct intersection, taking into account the fact that * the same angle can be represented with multiple values as well as the wrapping of longitude at the * anti-meridian. For a simple intersection that ignores these factors and can be used with projected * coordinates, see {@link Rectangle.simpleIntersection}. * * @param {Rectangle} rectangle On rectangle to find an intersection * @param {Rectangle} otherRectangle Another rectangle to find an intersection * @param {Rectangle} [result] The object onto which to store the result. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection. */ Rectangle.intersection = function (rectangle, otherRectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("otherRectangle", otherRectangle); //>>includeEnd('debug'); var rectangleEast = rectangle.east; var rectangleWest = rectangle.west; var otherRectangleEast = otherRectangle.east; var otherRectangleWest = otherRectangle.west; if (rectangleEast < rectangleWest && otherRectangleEast > 0.0) { rectangleEast += CesiumMath.TWO_PI; } else if (otherRectangleEast < otherRectangleWest && rectangleEast > 0.0) { otherRectangleEast += CesiumMath.TWO_PI; } if (rectangleEast < rectangleWest && otherRectangleWest < 0.0) { otherRectangleWest += CesiumMath.TWO_PI; } else if (otherRectangleEast < otherRectangleWest && rectangleWest < 0.0) { rectangleWest += CesiumMath.TWO_PI; } var west = CesiumMath.negativePiToPi( Math.max(rectangleWest, otherRectangleWest) ); var east = CesiumMath.negativePiToPi( Math.min(rectangleEast, otherRectangleEast) ); if ( (rectangle.west < rectangle.east || otherRectangle.west < otherRectangle.east) && east <= west ) { return undefined; } var south = Math.max(rectangle.south, otherRectangle.south); var north = Math.min(rectangle.north, otherRectangle.north); if (south >= north) { return undefined; } if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Computes a simple intersection of two rectangles. Unlike {@link Rectangle.intersection}, this function * does not attempt to put the angular coordinates into a consistent range or to account for crossing the * anti-meridian. As such, it can be used for rectangles where the coordinates are not simply latitude * and longitude (i.e. projected coordinates). * * @param {Rectangle} rectangle On rectangle to find an intersection * @param {Rectangle} otherRectangle Another rectangle to find an intersection * @param {Rectangle} [result] The object onto which to store the result. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection. */ Rectangle.simpleIntersection = function (rectangle, otherRectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("otherRectangle", otherRectangle); //>>includeEnd('debug'); var west = Math.max(rectangle.west, otherRectangle.west); var south = Math.max(rectangle.south, otherRectangle.south); var east = Math.min(rectangle.east, otherRectangle.east); var north = Math.min(rectangle.north, otherRectangle.north); if (south >= north || west >= east) { return undefined; } if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Computes a rectangle that is the union of two rectangles. * * @param {Rectangle} rectangle A rectangle to enclose in rectangle. * @param {Rectangle} otherRectangle A rectangle to enclose in a rectangle. * @param {Rectangle} [result] The object onto which to store the result. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. */ Rectangle.union = function (rectangle, otherRectangle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("otherRectangle", otherRectangle); //>>includeEnd('debug'); if (!defined(result)) { result = new Rectangle(); } var rectangleEast = rectangle.east; var rectangleWest = rectangle.west; var otherRectangleEast = otherRectangle.east; var otherRectangleWest = otherRectangle.west; if (rectangleEast < rectangleWest && otherRectangleEast > 0.0) { rectangleEast += CesiumMath.TWO_PI; } else if (otherRectangleEast < otherRectangleWest && rectangleEast > 0.0) { otherRectangleEast += CesiumMath.TWO_PI; } if (rectangleEast < rectangleWest && otherRectangleWest < 0.0) { otherRectangleWest += CesiumMath.TWO_PI; } else if (otherRectangleEast < otherRectangleWest && rectangleWest < 0.0) { rectangleWest += CesiumMath.TWO_PI; } var west = CesiumMath.convertLongitudeRange( Math.min(rectangleWest, otherRectangleWest) ); var east = CesiumMath.convertLongitudeRange( Math.max(rectangleEast, otherRectangleEast) ); result.west = west; result.south = Math.min(rectangle.south, otherRectangle.south); result.east = east; result.north = Math.max(rectangle.north, otherRectangle.north); return result; }; /** * Computes a rectangle by enlarging the provided rectangle until it contains the provided cartographic. * * @param {Rectangle} rectangle A rectangle to expand. * @param {Cartographic} cartographic A cartographic to enclose in a rectangle. * @param {Rectangle} [result] The object onto which to store the result. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if one was not provided. */ Rectangle.expand = function (rectangle, cartographic, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("cartographic", cartographic); //>>includeEnd('debug'); if (!defined(result)) { result = new Rectangle(); } result.west = Math.min(rectangle.west, cartographic.longitude); result.south = Math.min(rectangle.south, cartographic.latitude); result.east = Math.max(rectangle.east, cartographic.longitude); result.north = Math.max(rectangle.north, cartographic.latitude); return result; }; /** * Returns true if the cartographic is on or inside the rectangle, false otherwise. * * @param {Rectangle} rectangle The rectangle * @param {Cartographic} cartographic The cartographic to test. * @returns {Boolean} true if the provided cartographic is inside the rectangle, false otherwise. */ Rectangle.contains = function (rectangle, cartographic) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("cartographic", cartographic); //>>includeEnd('debug'); var longitude = cartographic.longitude; var latitude = cartographic.latitude; var west = rectangle.west; var east = rectangle.east; if (east < west) { east += CesiumMath.TWO_PI; if (longitude < 0.0) { longitude += CesiumMath.TWO_PI; } } return ( (longitude > west || CesiumMath.equalsEpsilon(longitude, west, CesiumMath.EPSILON14)) && (longitude < east || CesiumMath.equalsEpsilon(longitude, east, CesiumMath.EPSILON14)) && latitude >= rectangle.south && latitude <= rectangle.north ); }; var subsampleLlaScratch = new Cartographic(); /** * Samples a rectangle so that it includes a list of Cartesian points suitable for passing to * {@link BoundingSphere#fromPoints}. Sampling is necessary to account * for rectangles that cover the poles or cross the equator. * * @param {Rectangle} rectangle The rectangle to subsample. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. * @param {Number} [surfaceHeight=0.0] The height of the rectangle above the ellipsoid. * @param {Cartesian3[]} [result] The array of Cartesians onto which to store the result. * @returns {Cartesian3[]} The modified result parameter or a new Array of Cartesians instances if none was provided. */ Rectangle.subsample = function (rectangle, ellipsoid, surfaceHeight, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); surfaceHeight = defaultValue(surfaceHeight, 0.0); if (!defined(result)) { result = []; } var length = 0; var north = rectangle.north; var south = rectangle.south; var east = rectangle.east; var west = rectangle.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) { lla.longitude = -Math.PI + i * CesiumMath.PI_OVER_TWO; if (Rectangle.contains(rectangle, lla)) { 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 rectangle. * * @type {Rectangle} * @constant */ Rectangle.MAX_VALUE = Object.freeze( new Rectangle( -Math.PI, -CesiumMath.PI_OVER_TWO, Math.PI, CesiumMath.PI_OVER_TWO ) ); /** * 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 * @see Packable */ function BoundingSphere(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(); var volumeConstant = (4.0 / 3.0) * CesiumMath.PI; /** * 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. * * @param {Cartesian3[]} [positions] An array of points that the bounding sphere will enclose. Each point must have x, y, and z 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 {@link http://help.agi.com/AGIComponents/html/BlogBoundingSphere.htm|Bounding Sphere computation article} */ 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; var i; for (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.midpoint( minBoxPt, maxBoxPt, 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$1 = new GeographicProjection(); var fromRectangle2DLowerLeft = new Cartesian3(); var fromRectangle2DUpperRight = new Cartesian3(); var fromRectangle2DSouthwest = new Cartographic(); var fromRectangle2DNortheast = new Cartographic(); /** * Computes a bounding sphere from a rectangle projected in 2D. * * @param {Rectangle} [rectangle] The rectangle around which to create a bounding sphere. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle 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.fromRectangle2D = function (rectangle, projection, result) { return BoundingSphere.fromRectangleWithHeights2D( rectangle, projection, 0.0, 0.0, result ); }; /** * Computes a bounding sphere from a rectangle projected in 2D. The bounding sphere accounts for the * object's minimum and maximum heights over the rectangle. * * @param {Rectangle} [rectangle] The rectangle around which to create a bounding sphere. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. * @param {Number} [minimumHeight=0.0] The minimum height over the rectangle. * @param {Number} [maximumHeight=0.0] The maximum height over the rectangle. * @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.fromRectangleWithHeights2D = function ( rectangle, projection, minimumHeight, maximumHeight, result ) { if (!defined(result)) { result = new BoundingSphere(); } if (!defined(rectangle)) { result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } projection = defaultValue(projection, defaultProjection$1); Rectangle.southwest(rectangle, fromRectangle2DSouthwest); fromRectangle2DSouthwest.height = minimumHeight; Rectangle.northeast(rectangle, fromRectangle2DNortheast); fromRectangle2DNortheast.height = maximumHeight; var lowerLeft = projection.project( fromRectangle2DSouthwest, fromRectangle2DLowerLeft ); var upperRight = projection.project( fromRectangle2DNortheast, fromRectangle2DUpperRight ); 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 fromRectangle3DScratch = []; /** * Computes a bounding sphere from a rectangle in 3D. The bounding sphere is created using a subsample of points * on the ellipsoid and contained in the rectangle. It may not be accurate for all rectangles on all types of ellipsoids. * * @param {Rectangle} [rectangle] The valid rectangle used to create a bounding sphere. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle. * @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.fromRectangle3D = function ( rectangle, ellipsoid, surfaceHeight, result ) { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); surfaceHeight = defaultValue(surfaceHeight, 0.0); if (!defined(result)) { result = new BoundingSphere(); } if (!defined(rectangle)) { result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } var positions = Rectangle.subsample( rectangle, ellipsoid, surfaceHeight, fromRectangle3DScratch ); 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. * * @param {Number[]} [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. * * @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 Cesium.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 = Cesium.BoundingSphere.fromVertices(points, center, 5); * * @see {@link http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/|Bounding Sphere computation article} */ 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); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("stride", stride, 3); //>>includeEnd('debug'); 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; var i; for (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.midpoint( minBoxPt, maxBoxPt, 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 tight-fitting bounding sphere enclosing a list of EncodedCartesian3s, where the points are * stored in parallel flat arrays 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. * * @param {Number[]} [positionsHigh] An array of high bits of the encoded cartesians that the bounding sphere will enclose. Each point * is formed from three elements in the array in the order X, Y, Z. * @param {Number[]} [positionsLow] An array of low bits of the encoded cartesians that the bounding sphere will enclose. Each point * is formed from three elements in the array in the order X, Y, Z. * @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 {@link http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/|Bounding Sphere computation article} */ BoundingSphere.fromEncodedCartesianVertices = function ( positionsHigh, positionsLow, result ) { if (!defined(result)) { result = new BoundingSphere(); } if ( !defined(positionsHigh) || !defined(positionsLow) || positionsHigh.length !== positionsLow.length || positionsHigh.length === 0 ) { result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } var currentPos = fromPointsCurrentPos; currentPos.x = positionsHigh[0] + positionsLow[0]; currentPos.y = positionsHigh[1] + positionsLow[1]; currentPos.z = positionsHigh[2] + positionsLow[2]; 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 = positionsHigh.length; var i; for (i = 0; i < numElements; i += 3) { var x = positionsHigh[i] + positionsLow[i]; var y = positionsHigh[i + 1] + positionsLow[i + 1]; var z = positionsHigh[i + 2] + positionsLow[i + 2]; 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.midpoint( minBoxPt, maxBoxPt, fromPointsNaiveCenterScratch ); // Begin 2nd pass to find naive radius and modify the ritter sphere. var naiveRadius = 0; for (i = 0; i < numElements; i += 3) { currentPos.x = positionsHigh[i] + positionsLow[i]; currentPos.y = positionsHigh[i + 1] + positionsLow[i + 1]; currentPos.z = positionsHigh[i + 2] + positionsLow[i + 2]; // 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. * * @param {Cartesian3} [corner] The minimum height over the rectangle. * @param {Cartesian3} [oppositeCorner] The maximum height over the rectangle. * @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. * * @example * // Create a bounding sphere around the unit cube * var sphere = Cesium.BoundingSphere.fromCornerPoints(new Cesium.Cartesian3(-0.5, -0.5, -0.5), new Cesium.Cartesian3(0.5, 0.5, 0.5)); */ BoundingSphere.fromCornerPoints = function (corner, oppositeCorner, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("corner", corner); Check.typeOf.object("oppositeCorner", oppositeCorner); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } var center = Cartesian3.midpoint(corner, oppositeCorner, result.center); result.radius = Cartesian3.distance(center, oppositeCorner); return result; }; /** * Creates a bounding sphere encompassing an ellipsoid. * * @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. * * @example * var boundingSphere = Cesium.BoundingSphere.fromEllipsoid(ellipsoid); */ BoundingSphere.fromEllipsoid = function (ellipsoid, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("ellipsoid", ellipsoid); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } Cartesian3.clone(Cartesian3.ZERO, result.center); result.radius = ellipsoid.maximumRadius; return result; }; var fromBoundingSpheresScratch = new Cartesian3(); /** * Computes a tight-fitting bounding sphere enclosing the provided array of bounding spheres. * * @param {BoundingSphere[]} [boundingSpheres] The array of bounding spheres. * @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.fromBoundingSpheres = function (boundingSpheres, result) { if (!defined(result)) { result = new BoundingSphere(); } if (!defined(boundingSpheres) || boundingSpheres.length === 0) { result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); result.radius = 0.0; return result; } var length = boundingSpheres.length; if (length === 1) { return BoundingSphere.clone(boundingSpheres[0], result); } if (length === 2) { return BoundingSphere.union(boundingSpheres[0], boundingSpheres[1], result); } var positions = []; var i; for (i = 0; i < length; i++) { positions.push(boundingSpheres[i].center); } result = BoundingSphere.fromPoints(positions, result); var center = result.center; var radius = result.radius; for (i = 0; i < length; i++) { var tmp = boundingSpheres[i]; radius = Math.max( radius, Cartesian3.distance(center, tmp.center, fromBoundingSpheresScratch) + tmp.radius ); } result.radius = radius; return result; }; var fromOrientedBoundingBoxScratchU = new Cartesian3(); var fromOrientedBoundingBoxScratchV = new Cartesian3(); var fromOrientedBoundingBoxScratchW = new Cartesian3(); /** * Computes a tight-fitting bounding sphere enclosing the provided oriented bounding box. * * @param {OrientedBoundingBox} orientedBoundingBox The oriented bounding box. * @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.fromOrientedBoundingBox = function ( orientedBoundingBox, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("orientedBoundingBox", orientedBoundingBox); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } var halfAxes = orientedBoundingBox.halfAxes; var u = Matrix3.getColumn(halfAxes, 0, fromOrientedBoundingBoxScratchU); var v = Matrix3.getColumn(halfAxes, 1, fromOrientedBoundingBoxScratchV); var w = Matrix3.getColumn(halfAxes, 2, fromOrientedBoundingBoxScratchW); Cartesian3.add(u, v, u); Cartesian3.add(u, w, u); result.center = Cartesian3.clone(orientedBoundingBox.center, result.center); result.radius = Cartesian3.magnitude(u); return result; }; /** * Duplicates a BoundingSphere instance. * * @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; }; /** * The number of elements used to pack the object into an array. * @type {Number} */ BoundingSphere.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {BoundingSphere} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ BoundingSphere.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var center = value.center; array[startingIndex++] = center.x; array[startingIndex++] = center.y; array[startingIndex++] = center.z; array[startingIndex] = value.radius; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {BoundingSphere} [result] The object into which to store the result. * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. */ BoundingSphere.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new BoundingSphere(); } var center = result.center; center.x = array[startingIndex++]; center.y = array[startingIndex++]; center.z = array[startingIndex++]; result.radius = array[startingIndex]; return result; }; var unionScratch = new Cartesian3(); var unionScratchCenter = new Cartesian3(); /** * Computes a bounding sphere that contains both the left and right bounding spheres. * * @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. */ BoundingSphere.union = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } var leftCenter = left.center; var leftRadius = left.radius; var rightCenter = right.center; var rightRadius = right.radius; var toRightCenter = Cartesian3.subtract( rightCenter, leftCenter, unionScratch ); var centerSeparation = Cartesian3.magnitude(toRightCenter); if (leftRadius >= centerSeparation + rightRadius) { // Left sphere wins. left.clone(result); return result; } if (rightRadius >= centerSeparation + leftRadius) { // Right sphere wins. right.clone(result); return result; } // There are two tangent points, one on far side of each sphere. var halfDistanceBetweenTangentPoints = (leftRadius + centerSeparation + rightRadius) * 0.5; // Compute the center point halfway between the two tangent points. var center = Cartesian3.multiplyByScalar( toRightCenter, (-leftRadius + halfDistanceBetweenTangentPoints) / centerSeparation, unionScratchCenter ); Cartesian3.add(center, leftCenter, center); Cartesian3.clone(center, result.center); result.radius = halfDistanceBetweenTangentPoints; return result; }; var expandScratch = new Cartesian3(); /** * Computes a bounding sphere by enlarging the provided sphere to contain the provided point. * * @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. */ BoundingSphere.expand = function (sphere, point, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("point", point); //>>includeEnd('debug'); 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. * * @param {BoundingSphere} sphere The bounding sphere to test. * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere * intersects the plane. */ BoundingSphere.intersectPlane = function (sphere, plane) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("plane", plane); //>>includeEnd('debug'); var center = sphere.center; var radius = sphere.radius; var normal = plane.normal; var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance; if (distanceToPlane < -radius) { // The center point is negative side of the plane normal return Intersect$1.OUTSIDE; } else if (distanceToPlane < radius) { // The center point is positive side of the plane, but radius extends beyond it; partial overlap return Intersect$1.INTERSECTING; } return Intersect$1.INSIDE; }; /** * Applies a 4x4 affine transformation matrix to a bounding sphere. * * @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. */ BoundingSphere.transform = function (sphere, transform, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("transform", transform); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } result.center = Matrix4.multiplyByPoint( transform, sphere.center, result.center ); result.radius = Matrix4.getMaximumScale(transform) * sphere.radius; return result; }; var distanceSquaredToScratch = new Cartesian3(); /** * Computes the estimated distance squared from the closest point on a bounding sphere to a point. * * @param {BoundingSphere} sphere The sphere. * @param {Cartesian3} cartesian The point * @returns {Number} The estimated distance squared from the bounding sphere to the point. * * @example * // Sort bounding spheres from back to front * spheres.sort(function(a, b) { * return Cesium.BoundingSphere.distanceSquaredTo(b, camera.positionWC) - Cesium.BoundingSphere.distanceSquaredTo(a, camera.positionWC); * }); */ BoundingSphere.distanceSquaredTo = function (sphere, cartesian) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); var diff = Cartesian3.subtract( sphere.center, cartesian, distanceSquaredToScratch ); return Cartesian3.magnitudeSquared(diff) - sphere.radius * sphere.radius; }; /** * 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 BoundingSphere.transform}. * * @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. * * @example * var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid); * var boundingSphere = new Cesium.BoundingSphere(); * var newBoundingSphere = Cesium.BoundingSphere.transformWithoutScale(boundingSphere, modelMatrix); */ BoundingSphere.transformWithoutScale = function (sphere, transform, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("transform", transform); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingSphere(); } result.center = Matrix4.multiplyByPoint( transform, sphere.center, result.center ); result.radius = sphere.radius; return result; }; var scratchCartesian3$d = 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. *
* 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. * * @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 {Interval} [result] A Interval to store the nearest and farthest distances. * @returns {Interval} The nearest and farthest distances on the bounding sphere from position in direction. */ BoundingSphere.computePlaneDistances = function ( sphere, position, direction, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("position", position); Check.typeOf.object("direction", direction); //>>includeEnd('debug'); if (!defined(result)) { result = new Interval(); } var toCenter = Cartesian3.subtract( sphere.center, position, scratchCartesian3$d ); var mag = Cartesian3.dot(direction, toCenter); 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. * * @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. */ BoundingSphere.projectTo2D = function (sphere, projection, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); //>>includeEnd('debug'); projection = defaultValue(projection, projectTo2DProjection); var ellipsoid = projection.ellipsoid; var center = sphere.center; var radius = sphere.radius; var normal; if (Cartesian3.equals(center, Cartesian3.ZERO)) { // Bounding sphere is at the center. The geodetic surface normal is not // defined here so pick the x-axis as a fallback. normal = Cartesian3.clone(Cartesian3.UNIT_X, projectTo2DNormalScratch); } else { 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; }; /** * Determines whether or not a sphere is hidden from view by the occluder. * * @param {BoundingSphere} sphere The bounding sphere surrounding the occludee object. * @param {Occluder} occluder The occluder. * @returns {Boolean} true if the sphere is not visible; otherwise false. */ BoundingSphere.isOccluded = function (sphere, occluder) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("sphere", sphere); Check.typeOf.object("occluder", occluder); //>>includeEnd('debug'); return !occluder.isBoundingSphereVisible(sphere); }; /** * Compares the provided BoundingSphere componentwise and returns * true if they are equal, false otherwise. * * @param {BoundingSphere} [left] The first BoundingSphere. * @param {BoundingSphere} [right] The second BoundingSphere. * @returns {Boolean} true if left and right are equal, false otherwise. */ BoundingSphere.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && Cartesian3.equals(left.center, right.center) && left.radius === right.radius) ); }; /** * Determines which side of a plane the sphere is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere * intersects the plane. */ BoundingSphere.prototype.intersectPlane = function (plane) { return BoundingSphere.intersectPlane(this, plane); }; /** * Computes the estimated distance squared from the closest point on a bounding sphere to a point. * * @param {Cartesian3} cartesian The point * @returns {Number} The estimated distance squared from the bounding sphere to the point. * * @example * // Sort bounding spheres from back to front * spheres.sort(function(a, b) { * return b.distanceSquaredTo(camera.positionWC) - a.distanceSquaredTo(camera.positionWC); * }); */ BoundingSphere.prototype.distanceSquaredTo = function (cartesian) { return BoundingSphere.distanceSquaredTo(this, cartesian); }; /** * 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. *
* 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. * * @param {Cartesian3} position The position to calculate the distance from. * @param {Cartesian3} direction The direction from position. * @param {Interval} [result] A Interval to store the nearest and farthest distances. * @returns {Interval} The nearest and farthest distances on the bounding sphere from position in direction. */ BoundingSphere.prototype.computePlaneDistances = function ( position, direction, result ) { return BoundingSphere.computePlaneDistances( this, position, direction, result ); }; /** * Determines whether or not a sphere is hidden from view by the occluder. * * @param {Occluder} occluder The occluder. * @returns {Boolean} true if the sphere is not visible; otherwise false. */ BoundingSphere.prototype.isOccluded = function (occluder) { return BoundingSphere.isOccluded(this, occluder); }; /** * Compares this BoundingSphere against the provided BoundingSphere componentwise and returns * true if they are equal, false otherwise. * * @param {BoundingSphere} [right] The right hand side BoundingSphere. * @returns {Boolean} true if they are equal, false otherwise. */ BoundingSphere.prototype.equals = function (right) { return BoundingSphere.equals(this, right); }; /** * Duplicates this BoundingSphere instance. * * @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 the radius of the BoundingSphere. * @returns {Number} The radius of the BoundingSphere. */ BoundingSphere.prototype.volume = function () { var radius = this.radius; return volumeConstant * radius * radius * radius; }; /** * A tiling scheme for geometry referenced to a simple {@link GeographicProjection} where * longitude and latitude are directly mapped to X and Y. This projection is commonly * known as geographic, equirectangular, equidistant cylindrical, or plate carrée. * * @alias GeographicTilingScheme * @constructor * * @param {Object} [options] Object with the following properties: * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to * the WGS84 ellipsoid. * @param {Rectangle} [options.rectangle=Rectangle.MAX_VALUE] The rectangle, in radians, covered by the tiling scheme. * @param {Number} [options.numberOfLevelZeroTilesX=2] The number of tiles in the X direction at level zero of * the tile tree. * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of * the tile tree. */ function GeographicTilingScheme(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._rectangle = defaultValue(options.rectangle, Rectangle.MAX_VALUE); this._projection = new GeographicProjection(this._ellipsoid); this._numberOfLevelZeroTilesX = defaultValue( options.numberOfLevelZeroTilesX, 2 ); this._numberOfLevelZeroTilesY = defaultValue( options.numberOfLevelZeroTilesY, 1 ); } Object.defineProperties(GeographicTilingScheme.prototype, { /** * Gets the ellipsoid that is tiled by this tiling scheme. * @memberof GeographicTilingScheme.prototype * @type {Ellipsoid} */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets the rectangle, in radians, covered by this tiling scheme. * @memberof GeographicTilingScheme.prototype * @type {Rectangle} */ rectangle: { get: function () { return this._rectangle; }, }, /** * Gets the map projection used by this tiling scheme. * @memberof GeographicTilingScheme.prototype * @type {MapProjection} */ projection: { get: function () { return this._projection; }, }, }); /** * Gets the total number of tiles in the X direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the X direction at the given level. */ GeographicTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) { return this._numberOfLevelZeroTilesX << level; }; /** * Gets the total number of tiles in the Y direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the Y direction at the given level. */ GeographicTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) { return this._numberOfLevelZeroTilesY << level; }; /** * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * * @param {Rectangle} rectangle The rectangle to transform. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result' * is undefined. */ GeographicTilingScheme.prototype.rectangleToNativeRectangle = function ( rectangle, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("rectangle", rectangle); //>>includeEnd('debug'); var west = CesiumMath.toDegrees(rectangle.west); var south = CesiumMath.toDegrees(rectangle.south); var east = CesiumMath.toDegrees(rectangle.east); var north = CesiumMath.toDegrees(rectangle.north); if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ GeographicTilingScheme.prototype.tileXYToNativeRectangle = function ( x, y, level, result ) { var rectangleRadians = this.tileXYToRectangle(x, y, level, result); rectangleRadians.west = CesiumMath.toDegrees(rectangleRadians.west); rectangleRadians.south = CesiumMath.toDegrees(rectangleRadians.south); rectangleRadians.east = CesiumMath.toDegrees(rectangleRadians.east); rectangleRadians.north = CesiumMath.toDegrees(rectangleRadians.north); return rectangleRadians; }; /** * Converts tile x, y coordinates and level to a cartographic rectangle in radians. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ GeographicTilingScheme.prototype.tileXYToRectangle = function ( x, y, level, result ) { var rectangle = this._rectangle; var xTiles = this.getNumberOfXTilesAtLevel(level); var yTiles = this.getNumberOfYTilesAtLevel(level); var xTileWidth = rectangle.width / xTiles; var west = x * xTileWidth + rectangle.west; var east = (x + 1) * xTileWidth + rectangle.west; var yTileHeight = rectangle.height / yTiles; var north = rectangle.north - y * yTileHeight; var south = rectangle.north - (y + 1) * yTileHeight; if (!defined(result)) { result = new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Calculates the tile x, y coordinates of the tile containing * a given cartographic position. * * @param {Cartographic} position The position. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates * if 'result' is undefined. */ GeographicTilingScheme.prototype.positionToTileXY = function ( position, level, result ) { var rectangle = this._rectangle; if (!Rectangle.contains(rectangle, position)) { // outside the bounds of the tiling scheme return undefined; } var xTiles = this.getNumberOfXTilesAtLevel(level); var yTiles = this.getNumberOfYTilesAtLevel(level); var xTileWidth = rectangle.width / xTiles; var yTileHeight = rectangle.height / yTiles; var longitude = position.longitude; if (rectangle.east < rectangle.west) { longitude += CesiumMath.TWO_PI; } var xTileCoordinate = ((longitude - rectangle.west) / xTileWidth) | 0; if (xTileCoordinate >= xTiles) { xTileCoordinate = xTiles - 1; } var yTileCoordinate = ((rectangle.north - position.latitude) / yTileHeight) | 0; if (yTileCoordinate >= yTiles) { yTileCoordinate = yTiles - 1; } if (!defined(result)) { return new Cartesian2(xTileCoordinate, yTileCoordinate); } result.x = xTileCoordinate; result.y = yTileCoordinate; return result; }; var scratchDiagonalCartesianNE = new Cartesian3(); var scratchDiagonalCartesianSW = new Cartesian3(); var scratchDiagonalCartographic = new Cartographic(); var scratchCenterCartesian = new Cartesian3(); var scratchSurfaceCartesian = new Cartesian3(); var scratchBoundingSphere$4 = new BoundingSphere(); var tilingScheme = new GeographicTilingScheme(); var scratchCorners = [ new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), ]; var scratchTileXY = new Cartesian2(); /** * A collection of functions for approximating terrain height * @private */ var ApproximateTerrainHeights = {}; /** * Initializes the minimum and maximum terrain heights * @return {Promise} */ ApproximateTerrainHeights.initialize = function () { var initPromise = ApproximateTerrainHeights._initPromise; if (defined(initPromise)) { return initPromise; } initPromise = Resource.fetchJson( buildModuleUrl("Assets/approximateTerrainHeights.json") ).then(function (json) { ApproximateTerrainHeights._terrainHeights = json; }); ApproximateTerrainHeights._initPromise = initPromise; return initPromise; }; /** * Computes the minimum and maximum terrain heights for a given rectangle * @param {Rectangle} rectangle The bounding rectangle * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid * @return {{minimumTerrainHeight: Number, maximumTerrainHeight: Number}} */ ApproximateTerrainHeights.getMinimumMaximumHeights = function ( rectangle, ellipsoid ) { //>>includeStart('debug', pragmas.debug); Check.defined("rectangle", rectangle); if (!defined(ApproximateTerrainHeights._terrainHeights)) { throw new DeveloperError( "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function" ); } //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var xyLevel = getTileXYLevel(rectangle); // Get the terrain min/max for that tile var minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight; var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight; if (defined(xyLevel)) { var key = xyLevel.level + "-" + xyLevel.x + "-" + xyLevel.y; var heights = ApproximateTerrainHeights._terrainHeights[key]; if (defined(heights)) { minTerrainHeight = heights[0]; maxTerrainHeight = heights[1]; } // Compute min by taking the center of the NE->SW diagonal and finding distance to the surface ellipsoid.cartographicToCartesian( Rectangle.northeast(rectangle, scratchDiagonalCartographic), scratchDiagonalCartesianNE ); ellipsoid.cartographicToCartesian( Rectangle.southwest(rectangle, scratchDiagonalCartographic), scratchDiagonalCartesianSW ); Cartesian3.midpoint( scratchDiagonalCartesianSW, scratchDiagonalCartesianNE, scratchCenterCartesian ); var surfacePosition = ellipsoid.scaleToGeodeticSurface( scratchCenterCartesian, scratchSurfaceCartesian ); if (defined(surfacePosition)) { var distance = Cartesian3.distance( scratchCenterCartesian, surfacePosition ); minTerrainHeight = Math.min(minTerrainHeight, -distance); } else { minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight; } } minTerrainHeight = Math.max( ApproximateTerrainHeights._defaultMinTerrainHeight, minTerrainHeight ); return { minimumTerrainHeight: minTerrainHeight, maximumTerrainHeight: maxTerrainHeight, }; }; /** * Computes the bounding sphere based on the tile heights in the rectangle * @param {Rectangle} rectangle The bounding rectangle * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid * @return {BoundingSphere} The result bounding sphere */ ApproximateTerrainHeights.getBoundingSphere = function (rectangle, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined("rectangle", rectangle); if (!defined(ApproximateTerrainHeights._terrainHeights)) { throw new DeveloperError( "You must call ApproximateTerrainHeights.initialize and wait for the promise to resolve before using this function" ); } //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var xyLevel = getTileXYLevel(rectangle); // Get the terrain max for that tile var maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight; if (defined(xyLevel)) { var key = xyLevel.level + "-" + xyLevel.x + "-" + xyLevel.y; var heights = ApproximateTerrainHeights._terrainHeights[key]; if (defined(heights)) { maxTerrainHeight = heights[1]; } } var result = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, 0.0); BoundingSphere.fromRectangle3D( rectangle, ellipsoid, maxTerrainHeight, scratchBoundingSphere$4 ); return BoundingSphere.union(result, scratchBoundingSphere$4, result); }; function getTileXYLevel(rectangle) { Cartographic.fromRadians( rectangle.east, rectangle.north, 0.0, scratchCorners[0] ); Cartographic.fromRadians( rectangle.west, rectangle.north, 0.0, scratchCorners[1] ); Cartographic.fromRadians( rectangle.east, rectangle.south, 0.0, scratchCorners[2] ); Cartographic.fromRadians( rectangle.west, rectangle.south, 0.0, scratchCorners[3] ); // Determine which tile the bounding rectangle is in var lastLevelX = 0, lastLevelY = 0; var currentX = 0, currentY = 0; var maxLevel = ApproximateTerrainHeights._terrainHeightsMaxLevel; var i; for (i = 0; i <= maxLevel; ++i) { var failed = false; for (var j = 0; j < 4; ++j) { var corner = scratchCorners[j]; tilingScheme.positionToTileXY(corner, i, scratchTileXY); if (j === 0) { currentX = scratchTileXY.x; currentY = scratchTileXY.y; } else if (currentX !== scratchTileXY.x || currentY !== scratchTileXY.y) { failed = true; break; } } if (failed) { break; } lastLevelX = currentX; lastLevelY = currentY; } if (i === 0) { return undefined; } return { x: lastLevelX, y: lastLevelY, level: i > maxLevel ? maxLevel : i - 1, }; } ApproximateTerrainHeights._terrainHeightsMaxLevel = 6; ApproximateTerrainHeights._defaultMaxTerrainHeight = 9000.0; ApproximateTerrainHeights._defaultMinTerrainHeight = -100000.0; ApproximateTerrainHeights._terrainHeights = undefined; ApproximateTerrainHeights._initPromise = undefined; Object.defineProperties(ApproximateTerrainHeights, { /** * Determines if the terrain heights are initialized and ready to use. To initialize the terrain heights, * call {@link ApproximateTerrainHeights#initialize} and wait for the returned promise to resolve. * @type {Boolean} * @readonly * @memberof ApproximateTerrainHeights */ initialized: { get: function () { return defined(ApproximateTerrainHeights._terrainHeights); }, }, }); /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.0.8/LICENSE */ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } var hasOwnProperty = Object.hasOwnProperty, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen; var freeze = Object.freeze, seal = Object.seal, create = Object.create; // eslint-disable-line import/no-mutable-exports var _ref = typeof Reflect !== 'undefined' && Reflect, apply = _ref.apply, construct = _ref.construct; if (!apply) { apply = function apply(fun, thisValue, args) { return fun.apply(thisValue, args); }; } if (!freeze) { freeze = function freeze(x) { return x; }; } if (!seal) { seal = function seal(x) { return x; }; } if (!construct) { construct = function construct(Func, args) { return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); }; } var arrayForEach = unapply(Array.prototype.forEach); var arrayPop = unapply(Array.prototype.pop); var arrayPush = unapply(Array.prototype.push); var stringToLowerCase = unapply(String.prototype.toLowerCase); var stringMatch = unapply(String.prototype.match); var stringReplace = unapply(String.prototype.replace); var stringIndexOf = unapply(String.prototype.indexOf); var stringTrim = unapply(String.prototype.trim); var regExpTest = unapply(RegExp.prototype.test); var typeErrorCreate = unconstruct(TypeError); function unapply(func) { return function (thisArg) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return apply(func, thisArg, args); }; } function unconstruct(func) { return function () { for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return construct(func, args); }; } /* Add properties to a lookup table */ function addToSet(set, array) { if (setPrototypeOf) { // Make 'in' and truthy checks like Boolean(set.constructor) // independent of any properties defined on Object.prototype. // Prevent prototype setters from intercepting set as a this value. setPrototypeOf(set, null); } var l = array.length; while (l--) { var element = array[l]; if (typeof element === 'string') { var lcElement = stringToLowerCase(element); if (lcElement !== element) { // Config presets (e.g. tags.js, attrs.js) are immutable. if (!isFrozen(array)) { array[l] = lcElement; } element = lcElement; } } set[element] = true; } return set; } /* Shallow clone an object */ function clone(object) { var newObject = create(null); var property = void 0; for (property in object) { if (apply(hasOwnProperty, object, [property])) { newObject[property] = object[property]; } } return newObject; } var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'video', 'view', 'vkern']); var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']); var text = freeze(['#text']); var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']); var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']); var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']); var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); // eslint-disable-next-line unicorn/better-regex var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm); var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape ); var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } var getGlobal = function getGlobal() { return typeof window === 'undefined' ? null : window; }; /** * Creates a no-op policy for internal use only. * Don't export this function outside this module! * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory. * @param {Document} document The document object (to determine policy name suffix) * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types * are not supported). */ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { return null; } // Allow the callers to control the unique policy name // by adding a data-tt-policy-suffix to the script element with the DOMPurify. // Policy creation with duplicate names throws in Trusted Types. var suffix = null; var ATTR_NAME = 'data-tt-policy-suffix'; if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { suffix = document.currentScript.getAttribute(ATTR_NAME); } var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); try { return trustedTypes.createPolicy(policyName, { createHTML: function createHTML(html$$1) { return html$$1; } }); } catch (_) { // Policy creation failed (most likely another DOMPurify script has // already run). Skip creating the policy, as this will only cause errors // if TT are enforced. console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); return null; } }; function createDOMPurify() { var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); var DOMPurify = function DOMPurify(root) { return createDOMPurify(root); }; /** * Version label, exposed for easier checks * if DOMPurify is up to date or not */ DOMPurify.version = '2.2.2'; /** * Array of elements that DOMPurify removed during sanitation. * Empty if nothing was removed. */ DOMPurify.removed = []; if (!window || !window.document || window.document.nodeType !== 9) { // Not running in a browser, provide a factory function // so that you can pass your own Window DOMPurify.isSupported = false; return DOMPurify; } var originalDocument = window.document; var document = window.document; var DocumentFragment = window.DocumentFragment, HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, NodeFilter = window.NodeFilter, _window$NamedNodeMap = window.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, Text = window.Text, Comment = window.Comment, DOMParser = window.DOMParser, trustedTypes = window.trustedTypes; // As per issue #47, the web-components registry is inherited by a // new document created via createHTMLDocument. As per the spec // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) // a new empty registry is used when creating a template contents owner // document, so we use that as our parent document to ensure nothing // is inherited. if (typeof HTMLTemplateElement === 'function') { var template = document.createElement('template'); if (template.content && template.content.ownerDocument) { document = template.content.ownerDocument; } } var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; var _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, getElementsByTagName = _document.getElementsByTagName, createDocumentFragment = _document.createDocumentFragment; var importNode = originalDocument.importNode; var documentMode = {}; try { documentMode = clone(document).documentMode ? document.documentMode : {}; } catch (_) {} var hooks = {}; /** * Expose whether this browser supports running the full DOMPurify. */ DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, ERB_EXPR$$1 = ERB_EXPR, DATA_ATTR$$1 = DATA_ATTR, ARIA_ATTR$$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; /** * We consider the elements and attributes below to be safe. Ideally * don't add any new ones but feel free to remove unwanted ones. */ /* allowed element names */ var ALLOWED_TAGS = null; var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text))); /* Allowed attribute names */ var ALLOWED_ATTR = null; var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml))); /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ var FORBID_TAGS = null; /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ var FORBID_ATTR = null; /* Decide if ARIA attributes are okay */ var ALLOW_ARIA_ATTR = true; /* Decide if custom data attributes are okay */ var ALLOW_DATA_ATTR = true; /* Decide if unknown protocols are okay */ var ALLOW_UNKNOWN_PROTOCOLS = false; /* Output should be safe for common template engines. * This means, DOMPurify removes data attributes, mustaches and ERB */ var SAFE_FOR_TEMPLATES = false; /* Decide if document with ... should be returned */ var WHOLE_DOCUMENT = false; /* Track whether config is already set on this instance of DOMPurify. */ var SET_CONFIG = false; /* Decide if all elements (e.g. style, script) must be children of * document.body. By default, browsers might move them to document.head */ var FORCE_BODY = false; /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported). * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead */ var RETURN_DOM = false; /* Decide if a DOM `DocumentFragment` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported) */ var RETURN_DOM_FRAGMENT = false; /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM * `Node` is imported into the current `Document`. If this flag is not enabled the * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by * DOMPurify. * * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false` * might cause XSS from attacks hidden in closed shadowroots in case the browser * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/ */ var RETURN_DOM_IMPORT = true; /* Try to return a Trusted Type object instead of a string, return a string in * case Trusted Types are not supported */ var RETURN_TRUSTED_TYPE = false; /* Output should be free from DOM clobbering attacks? */ var SANITIZE_DOM = true; /* Keep element content when removing element? */ var KEEP_CONTENT = true; /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead * of importing it into a new Document and returning a sanitized copy */ var IN_PLACE = false; /* Allow usage of profiles like html, svg and mathMl */ var USE_PROFILES = {}; /* Tags to ignore content of when KEEP_CONTENT is true */ var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']); /* Tags that are safe for data: URIs */ var DATA_URI_TAGS = null; var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); /* Attributes safe for values like "javascript:" */ var URI_SAFE_ATTRIBUTES = null; var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']); /* Keep a reference to config to pass to hooks */ var CONFIG = null; /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ var formElement = document.createElement('form'); /** * _parseConfig * * @param {Object} cfg optional config literal */ // eslint-disable-next-line complexity var _parseConfig = function _parseConfig(cfg) { if (CONFIG && CONFIG === cfg) { return; } /* Shield configuration object from tampering */ if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { cfg = {}; } /* Shield configuration object from prototype pollution */ cfg = clone(cfg); /* Set configuration parameters */ ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false RETURN_DOM = cfg.RETURN_DOM || false; // Default false RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false FORCE_BODY = cfg.FORCE_BODY || false; // Default false SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true IN_PLACE = cfg.IN_PLACE || false; // Default false IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; if (SAFE_FOR_TEMPLATES) { ALLOW_DATA_ATTR = false; } if (RETURN_DOM_FRAGMENT) { RETURN_DOM = true; } /* Parse profile info */ if (USE_PROFILES) { ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); ALLOWED_ATTR = []; if (USE_PROFILES.html === true) { addToSet(ALLOWED_TAGS, html); addToSet(ALLOWED_ATTR, html$1); } if (USE_PROFILES.svg === true) { addToSet(ALLOWED_TAGS, svg); addToSet(ALLOWED_ATTR, svg$1); addToSet(ALLOWED_ATTR, xml); } if (USE_PROFILES.svgFilters === true) { addToSet(ALLOWED_TAGS, svgFilters); addToSet(ALLOWED_ATTR, svg$1); addToSet(ALLOWED_ATTR, xml); } if (USE_PROFILES.mathMl === true) { addToSet(ALLOWED_TAGS, mathMl); addToSet(ALLOWED_ATTR, mathMl$1); addToSet(ALLOWED_ATTR, xml); } } /* Merge configuration parameters */ if (cfg.ADD_TAGS) { if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { ALLOWED_TAGS = clone(ALLOWED_TAGS); } addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); } if (cfg.ADD_ATTR) { if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { ALLOWED_ATTR = clone(ALLOWED_ATTR); } addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); } if (cfg.ADD_URI_SAFE_ATTR) { addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); } /* Add #text in case KEEP_CONTENT is set to true */ if (KEEP_CONTENT) { ALLOWED_TAGS['#text'] = true; } /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ if (WHOLE_DOCUMENT) { addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); } /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ if (ALLOWED_TAGS.table) { addToSet(ALLOWED_TAGS, ['tbody']); delete FORBID_TAGS.tbody; } // Prevent further manipulation of configuration. // Not available in IE8, Safari 5, etc. if (freeze) { freeze(cfg); } CONFIG = cfg; }; /** * _forceRemove * * @param {Node} node a DOM node */ var _forceRemove = function _forceRemove(node) { arrayPush(DOMPurify.removed, { element: node }); try { node.parentNode.removeChild(node); } catch (_) { node.outerHTML = emptyHTML; } }; /** * _removeAttribute * * @param {String} name an Attribute name * @param {Node} node a DOM node */ var _removeAttribute = function _removeAttribute(name, node) { try { arrayPush(DOMPurify.removed, { attribute: node.getAttributeNode(name), from: node }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, from: node }); } node.removeAttribute(name); }; /** * _initDocument * * @param {String} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ var _initDocument = function _initDocument(dirty) { /* Create a HTML document */ var doc = void 0; var leadingWhitespace = void 0; if (FORCE_BODY) { dirty = '' + dirty; } else { /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ var matches = stringMatch(dirty, /^[\r\n\t ]+/); leadingWhitespace = matches && matches[0]; } var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; /* Use the DOMParser API by default, fallback later if needs be */ try { doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); } catch (_) {} /* Use createHTMLDocument in case DOMParser is not available */ if (!doc || !doc.documentElement) { doc = implementation.createHTMLDocument(''); var _doc = doc, body = _doc.body; body.parentNode.removeChild(body.parentNode.firstElementChild); body.outerHTML = dirtyPayload; } if (dirty && leadingWhitespace) { doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null); } /* Work on whole document or just its body */ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; }; /** * _createIterator * * @param {Document} root document/fragment to create iterator for * @return {Iterator} iterator instance */ var _createIterator = function _createIterator(root) { return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () { return NodeFilter.FILTER_ACCEPT; }, false); }; /** * _isClobbered * * @param {Node} elm element to check for clobbering attacks * @return {Boolean} true if clobbered, false if safe */ var _isClobbered = function _isClobbered(elm) { if (elm instanceof Text || elm instanceof Comment) { return false; } if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string') { return true; } return false; }; /** * _isNode * * @param {Node} obj object to check whether it's a DOM node * @return {Boolean} true is object is a DOM node */ var _isNode = function _isNode(object) { return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; }; /** * _executeHook * Execute user configurable hooks * * @param {String} entryPoint Name of the hook's entry point * @param {Node} currentNode node to work on with the hook * @param {Object} data additional hook parameters */ var _executeHook = function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { return; } arrayForEach(hooks[entryPoint], function (hook) { hook.call(DOMPurify, currentNode, data, CONFIG); }); }; /** * _sanitizeElements * * @protect nodeName * @protect textContent * @protect removeChild * * @param {Node} currentNode to check for permission to exist * @return {Boolean} true if node was killed, false if left alive */ var _sanitizeElements = function _sanitizeElements(currentNode) { var content = void 0; /* Execute a hook if present */ _executeHook('beforeSanitizeElements', currentNode, null); /* Check if element is clobbered or can clobber */ if (_isClobbered(currentNode)) { _forceRemove(currentNode); return true; } /* Check if tagname contains Unicode */ if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { _forceRemove(currentNode); return true; } /* Now let's check the element's type and name */ var tagName = stringToLowerCase(currentNode.nodeName); /* Execute a hook if present */ _executeHook('uponSanitizeElement', currentNode, { tagName: tagName, allowedTags: ALLOWED_TAGS }); /* Take care of an mXSS pattern using p, br inside svg, math */ if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br, form, table').length !== 0) { _forceRemove(currentNode); return true; } /* Detect mXSS attempts abusing namespace confusion */ if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[!/\w]/g, currentNode.innerHTML) && regExpTest(/<[!/\w]/g, currentNode.textContent)) { _forceRemove(currentNode); return true; } /* Remove element if anything forbids its presence */ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { /* Keep content except for bad-listed elements */ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') { try { var htmlToInsert = currentNode.innerHTML; currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert); } catch (_) {} } _forceRemove(currentNode); return true; } /* Remove in case a noscript/noembed XSS is suspected */ if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) { _forceRemove(currentNode); return true; } /* Sanitize element content to be template-safe */ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { /* Get the element's text content */ content = currentNode.textContent; content = stringReplace(content, MUSTACHE_EXPR$$1, ' '); content = stringReplace(content, ERB_EXPR$$1, ' '); if (currentNode.textContent !== content) { arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() }); currentNode.textContent = content; } } /* Execute a hook if present */ _executeHook('afterSanitizeElements', currentNode, null); return false; }; /** * _isValidAttribute * * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. * @return {Boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { /* Make sure attribute cannot clobber */ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { return false; } /* Allow valid data-* attributes: At least one character after "-" (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) We don't need to check the value; it's always URI safe. */ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { return false; /* Check value is safe. First, is attr inert? If so, is safe */ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else { return false; } return true; }; /** * _sanitizeAttributes * * @protect attributes * @protect nodeName * @protect removeAttribute * @protect setAttribute * * @param {Node} currentNode to sanitize */ var _sanitizeAttributes = function _sanitizeAttributes(currentNode) { var attr = void 0; var value = void 0; var lcName = void 0; var l = void 0; /* Execute a hook if present */ _executeHook('beforeSanitizeAttributes', currentNode, null); var attributes = currentNode.attributes; /* Check if we have attributes; if not we might have a text node */ if (!attributes) { return; } var hookEvent = { attrName: '', attrValue: '', keepAttr: true, allowedAttributes: ALLOWED_ATTR }; l = attributes.length; /* Go backwards over all attributes; safely remove bad ones */ while (l--) { attr = attributes[l]; var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI; value = stringTrim(attr.value); lcName = stringToLowerCase(name); /* Execute a hook if present */ hookEvent.attrName = lcName; hookEvent.attrValue = value; hookEvent.keepAttr = true; hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set _executeHook('uponSanitizeAttribute', currentNode, hookEvent); value = hookEvent.attrValue; /* Did the hooks approve of the attribute? */ if (hookEvent.forceKeepAttr) { continue; } /* Remove attribute */ _removeAttribute(name, currentNode); /* Did the hooks approve of the attribute? */ if (!hookEvent.keepAttr) { continue; } /* Work around a security issue in jQuery 3.0 */ if (regExpTest(/\/>/i, value)) { _removeAttribute(name, currentNode); continue; } /* Sanitize attribute content to be template-safe */ if (SAFE_FOR_TEMPLATES) { value = stringReplace(value, MUSTACHE_EXPR$$1, ' '); value = stringReplace(value, ERB_EXPR$$1, ' '); } /* Is `value` valid for this attribute? */ var lcTag = currentNode.nodeName.toLowerCase(); if (!_isValidAttribute(lcTag, lcName, value)) { continue; } /* Handle invalid data-* attribute set by try-catching it */ try { if (namespaceURI) { currentNode.setAttributeNS(namespaceURI, name, value); } else { /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */ currentNode.setAttribute(name, value); } arrayPop(DOMPurify.removed); } catch (_) {} } /* Execute a hook if present */ _executeHook('afterSanitizeAttributes', currentNode, null); }; /** * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively */ var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { var shadowNode = void 0; var shadowIterator = _createIterator(fragment); /* Execute a hook if present */ _executeHook('beforeSanitizeShadowDOM', fragment, null); while (shadowNode = shadowIterator.nextNode()) { /* Execute a hook if present */ _executeHook('uponSanitizeShadowNode', shadowNode, null); /* Sanitize tags and elements */ if (_sanitizeElements(shadowNode)) { continue; } /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { _sanitizeShadowDOM(shadowNode.content); } /* Check attributes, sanitize if necessary */ _sanitizeAttributes(shadowNode); } /* Execute a hook if present */ _executeHook('afterSanitizeShadowDOM', fragment, null); }; /** * Sanitize * Public method providing core sanitation functionality * * @param {String|Node} dirty string or DOM node * @param {Object} configuration object */ // eslint-disable-next-line complexity DOMPurify.sanitize = function (dirty, cfg) { var body = void 0; var importedNode = void 0; var currentNode = void 0; var oldNode = void 0; var returnNode = void 0; /* Make sure we have a string to sanitize. DO NOT return early, as this will return the wrong type if the user has requested a DOM object rather than a string */ if (!dirty) { dirty = ''; } /* Stringify, in case dirty is an object */ if (typeof dirty !== 'string' && !_isNode(dirty)) { // eslint-disable-next-line no-negated-condition if (typeof dirty.toString !== 'function') { throw typeErrorCreate('toString is not a function'); } else { dirty = dirty.toString(); if (typeof dirty !== 'string') { throw typeErrorCreate('dirty is not a string, aborting'); } } } /* Check we can run. Otherwise fall back or ignore */ if (!DOMPurify.isSupported) { if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') { if (typeof dirty === 'string') { return window.toStaticHTML(dirty); } if (_isNode(dirty)) { return window.toStaticHTML(dirty.outerHTML); } } return dirty; } /* Assign config vars */ if (!SET_CONFIG) { _parseConfig(cfg); } /* Clean up removed elements */ DOMPurify.removed = []; /* Check if dirty is correctly typed for IN_PLACE */ if (typeof dirty === 'string') { IN_PLACE = false; } if (IN_PLACE) ; else if (dirty instanceof Node) { /* If dirty is a DOM element, append to an empty document to avoid elements being stripped by the parser */ body = _initDocument(''); importedNode = body.ownerDocument.importNode(dirty, true); if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { /* Node is already a body, use as is */ body = importedNode; } else if (importedNode.nodeName === 'HTML') { body = importedNode; } else { // eslint-disable-next-line unicorn/prefer-node-append body.appendChild(importedNode); } } else { /* Exit directly if we have nothing to do */ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes dirty.indexOf('<') === -1) { return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; } /* Initialize the document to work on */ body = _initDocument(dirty); /* Check we have a DOM node from the data */ if (!body) { return RETURN_DOM ? null : emptyHTML; } } /* Remove first element node (ours) if FORCE_BODY is set */ if (body && FORCE_BODY) { _forceRemove(body.firstChild); } /* Get node iterator */ var nodeIterator = _createIterator(IN_PLACE ? dirty : body); /* Now start iterating over the created document */ while (currentNode = nodeIterator.nextNode()) { /* Fix IE's strange behavior with manipulated textNodes #89 */ if (currentNode.nodeType === 3 && currentNode === oldNode) { continue; } /* Sanitize tags and elements */ if (_sanitizeElements(currentNode)) { continue; } /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { _sanitizeShadowDOM(currentNode.content); } /* Check attributes, sanitize if necessary */ _sanitizeAttributes(currentNode); oldNode = currentNode; } oldNode = null; /* If we sanitized `dirty` in-place, return it. */ if (IN_PLACE) { return dirty; } /* Return sanitized string or DOM */ if (RETURN_DOM) { if (RETURN_DOM_FRAGMENT) { returnNode = createDocumentFragment.call(body.ownerDocument); while (body.firstChild) { // eslint-disable-next-line unicorn/prefer-node-append returnNode.appendChild(body.firstChild); } } else { returnNode = body; } if (RETURN_DOM_IMPORT) { /* AdoptNode() is not used because internal state is not reset (e.g. the past names map of a HTMLFormElement), this is safe in theory but we would rather not risk another attack vector. The state that is cloned by importNode() is explicitly defined by the specs. */ returnNode = importNode.call(originalDocument, returnNode, true); } return returnNode; } var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; /* Sanitize final string template-safe */ if (SAFE_FOR_TEMPLATES) { serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' '); serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' '); } return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; }; /** * Public method to set the configuration once * setConfig * * @param {Object} cfg configuration object */ DOMPurify.setConfig = function (cfg) { _parseConfig(cfg); SET_CONFIG = true; }; /** * Public method to remove the configuration * clearConfig * */ DOMPurify.clearConfig = function () { CONFIG = null; SET_CONFIG = false; }; /** * Public method to check if an attribute value is valid. * Uses last set config, if any. Otherwise, uses config defaults. * isValidAttribute * * @param {string} tag Tag name of containing element. * @param {string} attr Attribute name. * @param {string} value Attribute value. * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false. */ DOMPurify.isValidAttribute = function (tag, attr, value) { /* Initialize shared config vars if necessary. */ if (!CONFIG) { _parseConfig({}); } var lcTag = stringToLowerCase(tag); var lcName = stringToLowerCase(attr); return _isValidAttribute(lcTag, lcName, value); }; /** * AddHook * Public method to add DOMPurify hooks * * @param {String} entryPoint entry point for the hook to add * @param {Function} hookFunction function to execute */ DOMPurify.addHook = function (entryPoint, hookFunction) { if (typeof hookFunction !== 'function') { return; } hooks[entryPoint] = hooks[entryPoint] || []; arrayPush(hooks[entryPoint], hookFunction); }; /** * RemoveHook * Public method to remove a DOMPurify hook at a given entryPoint * (pops it from the stack of hooks if more are present) * * @param {String} entryPoint entry point for the hook to remove */ DOMPurify.removeHook = function (entryPoint) { if (hooks[entryPoint]) { arrayPop(hooks[entryPoint]); } }; /** * RemoveHooks * Public method to remove all DOMPurify hooks at a given entryPoint * * @param {String} entryPoint entry point for the hooks to remove */ DOMPurify.removeHooks = function (entryPoint) { if (hooks[entryPoint]) { hooks[entryPoint] = []; } }; /** * RemoveAllHooks * Public method to remove all DOMPurify hooks * */ DOMPurify.removeAllHooks = function () { hooks = {}; }; return DOMPurify; } var purify = createDOMPurify(); var nextCreditId = 0; var creditToId = {}; /** * A credit contains data pertaining to how to display attributions/credits for certain content on the screen. * @param {String} html An string representing an html code snippet * @param {Boolean} [showOnScreen=false] If true, the credit will be visible in the main credit container. Otherwise, it will appear in a popover * * @alias Credit * @constructor * * @exception {DeveloperError} html is required. * * @example * //Create a credit with a tooltip, image and link * var credit = new Cesium.Credit(''); */ function Credit(html, showOnScreen) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("html", html); //>>includeEnd('debug'); var id; var key = html; if (defined(creditToId[key])) { id = creditToId[key]; } else { id = nextCreditId++; creditToId[key] = id; } showOnScreen = defaultValue(showOnScreen, false); // Credits are immutable so generate an id to use to optimize equal() this._id = id; this._html = html; this._showOnScreen = showOnScreen; this._element = undefined; } Object.defineProperties(Credit.prototype, { /** * The credit content * @memberof Credit.prototype * @type {String} * @readonly */ html: { get: function () { return this._html; }, }, /** * @memberof Credit.prototype * @type {Number} * @readonly * * @private */ id: { get: function () { return this._id; }, }, /** * Whether the credit should be displayed on screen or in a lightbox * @memberof Credit.prototype * @type {Boolean} * @readonly */ showOnScreen: { get: function () { return this._showOnScreen; }, }, /** * Gets the credit element * @memberof Credit.prototype * @type {HTMLElement} * @readonly */ element: { get: function () { if (!defined(this._element)) { var html = purify.sanitize(this._html); var div = document.createElement("div"); div._creditId = this._id; div.style.display = "inline"; div.innerHTML = html; var links = div.querySelectorAll("a"); for (var i = 0; i < links.length; i++) { links[i].setAttribute("target", "_blank"); } this._element = div; } return this._element; }, }, }); /** * Returns true if the credits are equal * * @param {Credit} left The first credit * @param {Credit} right The second credit * @returns {Boolean} true if left and right are equal, false otherwise. */ Credit.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left._id === right._id) ); }; /** * Returns true if the credits are equal * * @param {Credit} credit The credit to compare to. * @returns {Boolean} true if left and right are equal, false otherwise. */ Credit.prototype.equals = function (credit) { return Credit.equals(this, credit); }; /** * @private * @param attribution * @return {Credit} */ Credit.getIonCredit = function (attribution) { var showOnScreen = defined(attribution.collapsible) && !attribution.collapsible; var credit = new Credit(attribution.html, showOnScreen); credit._isIon = credit.html.indexOf("ion-credit.png") !== -1; return credit; }; /** * Duplicates a Credit instance. * * @param {Credit} [credit] The Credit to duplicate. * @returns {Credit} A new Credit instance that is a duplicate of the one provided. (Returns undefined if the credit is undefined) */ Credit.clone = function (credit) { if (defined(credit)) { return new Credit(credit.html, credit.showOnScreen); } }; /** * The encoding that is used for a heightmap * * @enum {Number} */ var HeightmapEncoding = { /** * No encoding * * @type {Number} * @constant */ NONE: 0, /** * LERC encoding * * @type {Number} * @constant * * @see {@link https://github.com/Esri/lerc|The LERC specification} */ LERC: 1, }; var HeightmapEncoding$1 = Object.freeze(HeightmapEncoding); /** * Creates an instance of an AxisAlignedBoundingBox from the minimum and maximum points along the x, y, and z axes. * @alias AxisAlignedBoundingBox * @constructor * * @param {Cartesian3} [minimum=Cartesian3.ZERO] The minimum point along the x, y, and z axes. * @param {Cartesian3} [maximum=Cartesian3.ZERO] The maximum point along the x, y, and z axes. * @param {Cartesian3} [center] The center of the box; automatically computed if not supplied. * * @see BoundingSphere * @see BoundingRectangle */ function AxisAlignedBoundingBox(minimum, maximum, center) { /** * The minimum point defining the bounding box. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.minimum = Cartesian3.clone(defaultValue(minimum, Cartesian3.ZERO)); /** * The maximum point defining the bounding box. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.maximum = Cartesian3.clone(defaultValue(maximum, Cartesian3.ZERO)); //If center was not defined, compute it. if (!defined(center)) { center = Cartesian3.midpoint(this.minimum, this.maximum, new Cartesian3()); } else { center = Cartesian3.clone(center); } /** * The center point of the bounding box. * @type {Cartesian3} */ this.center = center; } /** * Computes an instance of an AxisAlignedBoundingBox. The box is determined by * finding the points spaced the farthest apart on the x, y, and z axes. * * @param {Cartesian3[]} positions List of points that the bounding box will enclose. Each point must have a x, y, and z properties. * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if one was not provided. * * @example * // Compute an axis aligned bounding box enclosing two points. * var box = Cesium.AxisAlignedBoundingBox.fromPoints([new Cesium.Cartesian3(2, 0, 0), new Cesium.Cartesian3(-2, 0, 0)]); */ AxisAlignedBoundingBox.fromPoints = function (positions, result) { if (!defined(result)) { result = new AxisAlignedBoundingBox(); } if (!defined(positions) || positions.length === 0) { result.minimum = Cartesian3.clone(Cartesian3.ZERO, result.minimum); result.maximum = Cartesian3.clone(Cartesian3.ZERO, result.maximum); result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); return result; } var minimumX = positions[0].x; var minimumY = positions[0].y; var minimumZ = positions[0].z; var maximumX = positions[0].x; var maximumY = positions[0].y; var maximumZ = positions[0].z; var length = positions.length; for (var i = 1; i < length; i++) { var p = positions[i]; var x = p.x; var y = p.y; var z = p.z; minimumX = Math.min(x, minimumX); maximumX = Math.max(x, maximumX); minimumY = Math.min(y, minimumY); maximumY = Math.max(y, maximumY); minimumZ = Math.min(z, minimumZ); maximumZ = Math.max(z, maximumZ); } var minimum = result.minimum; minimum.x = minimumX; minimum.y = minimumY; minimum.z = minimumZ; var maximum = result.maximum; maximum.x = maximumX; maximum.y = maximumY; maximum.z = maximumZ; result.center = Cartesian3.midpoint(minimum, maximum, result.center); return result; }; /** * Duplicates a AxisAlignedBoundingBox instance. * * @param {AxisAlignedBoundingBox} box The bounding box to duplicate. * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if none was provided. (Returns undefined if box is undefined) */ AxisAlignedBoundingBox.clone = function (box, result) { if (!defined(box)) { return undefined; } if (!defined(result)) { return new AxisAlignedBoundingBox(box.minimum, box.maximum, box.center); } result.minimum = Cartesian3.clone(box.minimum, result.minimum); result.maximum = Cartesian3.clone(box.maximum, result.maximum); result.center = Cartesian3.clone(box.center, result.center); return result; }; /** * Compares the provided AxisAlignedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {AxisAlignedBoundingBox} [left] The first AxisAlignedBoundingBox. * @param {AxisAlignedBoundingBox} [right] The second AxisAlignedBoundingBox. * @returns {Boolean} true if left and right are equal, false otherwise. */ AxisAlignedBoundingBox.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && Cartesian3.equals(left.center, right.center) && Cartesian3.equals(left.minimum, right.minimum) && Cartesian3.equals(left.maximum, right.maximum)) ); }; var intersectScratch = new Cartesian3(); /** * Determines which side of a plane a box is located. * * @param {AxisAlignedBoundingBox} box The bounding box to test. * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ AxisAlignedBoundingBox.intersectPlane = function (box, plane) { //>>includeStart('debug', pragmas.debug); Check.defined("box", box); Check.defined("plane", plane); //>>includeEnd('debug'); intersectScratch = Cartesian3.subtract( box.maximum, box.minimum, intersectScratch ); var h = Cartesian3.multiplyByScalar(intersectScratch, 0.5, intersectScratch); //The positive half diagonal var normal = plane.normal; var e = h.x * Math.abs(normal.x) + h.y * Math.abs(normal.y) + h.z * Math.abs(normal.z); var s = Cartesian3.dot(box.center, normal) + plane.distance; //signed distance from center if (s - e > 0) { return Intersect$1.INSIDE; } if (s + e < 0) { //Not in front because normals point inward return Intersect$1.OUTSIDE; } return Intersect$1.INTERSECTING; }; /** * Duplicates this AxisAlignedBoundingBox instance. * * @param {AxisAlignedBoundingBox} [result] The object onto which to store the result. * @returns {AxisAlignedBoundingBox} The modified result parameter or a new AxisAlignedBoundingBox instance if one was not provided. */ AxisAlignedBoundingBox.prototype.clone = function (result) { return AxisAlignedBoundingBox.clone(this, result); }; /** * Determines which side of a plane this box is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ AxisAlignedBoundingBox.prototype.intersectPlane = function (plane) { return AxisAlignedBoundingBox.intersectPlane(this, plane); }; /** * Compares this AxisAlignedBoundingBox against the provided AxisAlignedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {AxisAlignedBoundingBox} [right] The right hand side AxisAlignedBoundingBox. * @returns {Boolean} true if they are equal, false otherwise. */ AxisAlignedBoundingBox.prototype.equals = function (right) { return AxisAlignedBoundingBox.equals(this, right); }; /** * Determine whether or not other objects are visible or hidden behind the visible horizon defined by * an {@link Ellipsoid} and a camera position. The ellipsoid is assumed to be located at the * origin of the coordinate system. This class uses the algorithm described in the * {@link https://cesium.com/blog/2013/04/25/Horizon-culling/|Horizon Culling} blog post. * * @alias EllipsoidalOccluder * * @param {Ellipsoid} ellipsoid The ellipsoid to use as an occluder. * @param {Cartesian3} [cameraPosition] The coordinate of the viewer/camera. If this parameter is not * specified, {@link EllipsoidalOccluder#cameraPosition} must be called before * testing visibility. * * @constructor * * @example * // Construct an ellipsoidal occluder with radii 1.0, 1.1, and 0.9. * var cameraPosition = new Cesium.Cartesian3(5.0, 6.0, 7.0); * var occluderEllipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9); * var occluder = new Cesium.EllipsoidalOccluder(occluderEllipsoid, cameraPosition); * * @private */ function EllipsoidalOccluder(ellipsoid, cameraPosition) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("ellipsoid", ellipsoid); //>>includeEnd('debug'); this._ellipsoid = ellipsoid; this._cameraPosition = new Cartesian3(); this._cameraPositionInScaledSpace = new Cartesian3(); this._distanceToLimbInScaledSpaceSquared = 0.0; // cameraPosition fills in the above values if (defined(cameraPosition)) { this.cameraPosition = cameraPosition; } } Object.defineProperties(EllipsoidalOccluder.prototype, { /** * Gets the occluding ellipsoid. * @memberof EllipsoidalOccluder.prototype * @type {Ellipsoid} */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets or sets the position of the camera. * @memberof EllipsoidalOccluder.prototype * @type {Cartesian3} */ cameraPosition: { get: function () { return this._cameraPosition; }, set: function (cameraPosition) { // See https://cesium.com/blog/2013/04/25/Horizon-culling/ var ellipsoid = this._ellipsoid; var cv = ellipsoid.transformPositionToScaledSpace( cameraPosition, this._cameraPositionInScaledSpace ); var vhMagnitudeSquared = Cartesian3.magnitudeSquared(cv) - 1.0; Cartesian3.clone(cameraPosition, this._cameraPosition); this._cameraPositionInScaledSpace = cv; this._distanceToLimbInScaledSpaceSquared = vhMagnitudeSquared; }, }, }); var scratchCartesian$9 = new Cartesian3(); /** * Determines whether or not a point, the occludee, is hidden from view by the occluder. * * @param {Cartesian3} occludee The point to test for visibility. * @returns {Boolean} true if the occludee is visible; otherwise false. * * @example * var cameraPosition = new Cesium.Cartesian3(0, 0, 2.5); * var ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9); * var occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition); * var point = new Cesium.Cartesian3(0, -3, -3); * occluder.isPointVisible(point); //returns true */ EllipsoidalOccluder.prototype.isPointVisible = function (occludee) { var ellipsoid = this._ellipsoid; var occludeeScaledSpacePosition = ellipsoid.transformPositionToScaledSpace( occludee, scratchCartesian$9 ); return isScaledSpacePointVisible( occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared ); }; /** * Determines whether or not a point expressed in the ellipsoid scaled space, is hidden from view by the * occluder. To transform a Cartesian X, Y, Z position in the coordinate system aligned with the ellipsoid * into the scaled space, call {@link Ellipsoid#transformPositionToScaledSpace}. * * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space. * @returns {Boolean} true if the occludee is visible; otherwise false. * * @example * var cameraPosition = new Cesium.Cartesian3(0, 0, 2.5); * var ellipsoid = new Cesium.Ellipsoid(1.0, 1.1, 0.9); * var occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition); * var point = new Cesium.Cartesian3(0, -3, -3); * var scaledSpacePoint = ellipsoid.transformPositionToScaledSpace(point); * occluder.isScaledSpacePointVisible(scaledSpacePoint); //returns true */ EllipsoidalOccluder.prototype.isScaledSpacePointVisible = function ( occludeeScaledSpacePosition ) { return isScaledSpacePointVisible( occludeeScaledSpacePosition, this._cameraPositionInScaledSpace, this._distanceToLimbInScaledSpaceSquared ); }; var scratchCameraPositionInScaledSpaceShrunk = new Cartesian3(); /** * Similar to {@link EllipsoidalOccluder#isScaledSpacePointVisible} except tests against an * ellipsoid that has been shrunk by the minimum height when the minimum height is below * the ellipsoid. This is intended to be used with points generated by * {@link EllipsoidalOccluder#computeHorizonCullingPointPossiblyUnderEllipsoid} or * {@link EllipsoidalOccluder#computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid}. * * @param {Cartesian3} occludeeScaledSpacePosition The point to test for visibility, represented in the scaled space of the possibly-shrunk ellipsoid. * @returns {Boolean} true if the occludee is visible; otherwise false. */ EllipsoidalOccluder.prototype.isScaledSpacePointVisiblePossiblyUnderEllipsoid = function ( occludeeScaledSpacePosition, minimumHeight ) { var ellipsoid = this._ellipsoid; var vhMagnitudeSquared; var cv; if ( defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight ) { // This code is similar to the cameraPosition setter, but unrolled for performance because it will be called a lot. cv = scratchCameraPositionInScaledSpaceShrunk; cv.x = this._cameraPosition.x / (ellipsoid.radii.x + minimumHeight); cv.y = this._cameraPosition.y / (ellipsoid.radii.y + minimumHeight); cv.z = this._cameraPosition.z / (ellipsoid.radii.z + minimumHeight); vhMagnitudeSquared = cv.x * cv.x + cv.y * cv.y + cv.z * cv.z - 1.0; } else { cv = this._cameraPositionInScaledSpace; vhMagnitudeSquared = this._distanceToLimbInScaledSpaceSquared; } return isScaledSpacePointVisible( occludeeScaledSpacePosition, cv, vhMagnitudeSquared ); }; /** * Computes a point that can be used for horizon culling from a list of positions. If the point is below * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point * is expressed in the ellipsoid-scaled space and is suitable for use with * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. * * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. * A reasonable direction to use is the direction from the center of the ellipsoid to * the center of the bounding sphere computed from the positions. The direction need not * be normalized. * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions * must be expressed in a reference frame centered at the ellipsoid and aligned with the * ellipsoid's axes. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. */ EllipsoidalOccluder.prototype.computeHorizonCullingPoint = function ( directionToPoint, positions, result ) { return computeHorizonCullingPointFromPositions( this._ellipsoid, directionToPoint, positions, result ); }; var scratchEllipsoidShrunk = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); /** * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPoint} except computes the culling * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}. * * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. * A reasonable direction to use is the direction from the center of the ellipsoid to * the center of the bounding sphere computed from the positions. The direction need not * be normalized. * @param {Cartesian3[]} positions The positions from which to compute the horizon culling point. The positions * must be expressed in a reference frame centered at the ellipsoid and aligned with the * ellipsoid's axes. * @param {Number} [minimumHeight] The minimum height of all positions. If this value is undefined, all positions are assumed to be above the ellipsoid. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space. */ EllipsoidalOccluder.prototype.computeHorizonCullingPointPossiblyUnderEllipsoid = function ( directionToPoint, positions, minimumHeight, result ) { var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid( this._ellipsoid, minimumHeight, scratchEllipsoidShrunk ); return computeHorizonCullingPointFromPositions( possiblyShrunkEllipsoid, directionToPoint, positions, result ); }; /** * Computes a point that can be used for horizon culling from a list of positions. If the point is below * the horizon, all of the positions are guaranteed to be below the horizon as well. The returned point * is expressed in the ellipsoid-scaled space and is suitable for use with * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. * * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. * A reasonable direction to use is the direction from the center of the ellipsoid to * the center of the bounding sphere computed from the positions. The direction need not * be normalized. * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions * must be expressed in a reference frame centered at the ellipsoid and aligned with the * ellipsoid's axes. * @param {Number} [stride=3] * @param {Cartesian3} [center=Cartesian3.ZERO] * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. */ EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVertices = function ( directionToPoint, vertices, stride, center, result ) { return computeHorizonCullingPointFromVertices( this._ellipsoid, directionToPoint, vertices, stride, center, result ); }; /** * Similar to {@link EllipsoidalOccluder#computeHorizonCullingPointFromVertices} except computes the culling * point relative to an ellipsoid that has been shrunk by the minimum height when the minimum height is below * the ellipsoid. The returned point is expressed in the possibly-shrunk ellipsoid-scaled space and is suitable * for use with {@link EllipsoidalOccluder#isScaledSpacePointVisiblePossiblyUnderEllipsoid}. * * @param {Cartesian3} directionToPoint The direction that the computed point will lie along. * A reasonable direction to use is the direction from the center of the ellipsoid to * the center of the bounding sphere computed from the positions. The direction need not * be normalized. * @param {Number[]} vertices The vertices from which to compute the horizon culling point. The positions * must be expressed in a reference frame centered at the ellipsoid and aligned with the * ellipsoid's axes. * @param {Number} [stride=3] * @param {Cartesian3} [center=Cartesian3.ZERO] * @param {Number} [minimumHeight] The minimum height of all vertices. If this value is undefined, all vertices are assumed to be above the ellipsoid. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the possibly-shrunk ellipsoid-scaled space. */ EllipsoidalOccluder.prototype.computeHorizonCullingPointFromVerticesPossiblyUnderEllipsoid = function ( directionToPoint, vertices, stride, center, minimumHeight, result ) { var possiblyShrunkEllipsoid = getPossiblyShrunkEllipsoid( this._ellipsoid, minimumHeight, scratchEllipsoidShrunk ); return computeHorizonCullingPointFromVertices( possiblyShrunkEllipsoid, directionToPoint, vertices, stride, center, result ); }; var subsampleScratch = []; /** * Computes a point that can be used for horizon culling of a rectangle. If the point is below * the horizon, the ellipsoid-conforming rectangle is guaranteed to be below the horizon as well. * The returned point is expressed in the ellipsoid-scaled space and is suitable for use with * {@link EllipsoidalOccluder#isScaledSpacePointVisible}. * * @param {Rectangle} rectangle The rectangle for which to compute the horizon culling point. * @param {Ellipsoid} ellipsoid The ellipsoid on which the rectangle is defined. This may be different from * the ellipsoid used by this instance for occlusion testing. * @param {Cartesian3} [result] The instance on which to store the result instead of allocating a new instance. * @returns {Cartesian3} The computed horizon culling point, expressed in the ellipsoid-scaled space. */ EllipsoidalOccluder.prototype.computeHorizonCullingPointFromRectangle = function ( rectangle, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var positions = Rectangle.subsample( rectangle, ellipsoid, 0.0, subsampleScratch ); var bs = BoundingSphere.fromPoints(positions); // If the bounding sphere center is too close to the center of the occluder, it doesn't make // sense to try to horizon cull it. if (Cartesian3.magnitude(bs.center) < 0.1 * ellipsoid.minimumRadius) { return undefined; } return this.computeHorizonCullingPoint(bs.center, positions, result); }; var scratchEllipsoidShrunkRadii = new Cartesian3(); function getPossiblyShrunkEllipsoid(ellipsoid, minimumHeight, result) { if ( defined(minimumHeight) && minimumHeight < 0.0 && ellipsoid.minimumRadius > -minimumHeight ) { var ellipsoidShrunkRadii = Cartesian3.fromElements( ellipsoid.radii.x + minimumHeight, ellipsoid.radii.y + minimumHeight, ellipsoid.radii.z + minimumHeight, scratchEllipsoidShrunkRadii ); ellipsoid = Ellipsoid.fromCartesian3(ellipsoidShrunkRadii, result); } return ellipsoid; } function computeHorizonCullingPointFromPositions( ellipsoid, directionToPoint, positions, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("directionToPoint", directionToPoint); Check.defined("positions", positions); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint( ellipsoid, directionToPoint ); var resultMagnitude = 0.0; for (var i = 0, len = positions.length; i < len; ++i) { var position = positions[i]; var candidateMagnitude = computeMagnitude( ellipsoid, position, scaledSpaceDirectionToPoint ); if (candidateMagnitude < 0.0) { // all points should face the same direction, but this one doesn't, so return undefined return undefined; } resultMagnitude = Math.max(resultMagnitude, candidateMagnitude); } return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result); } var positionScratch$b = new Cartesian3(); function computeHorizonCullingPointFromVertices( ellipsoid, directionToPoint, vertices, stride, center, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("directionToPoint", directionToPoint); Check.defined("vertices", vertices); Check.typeOf.number("stride", stride); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } stride = defaultValue(stride, 3); center = defaultValue(center, Cartesian3.ZERO); var scaledSpaceDirectionToPoint = computeScaledSpaceDirectionToPoint( ellipsoid, directionToPoint ); var resultMagnitude = 0.0; for (var i = 0, len = vertices.length; i < len; i += stride) { positionScratch$b.x = vertices[i] + center.x; positionScratch$b.y = vertices[i + 1] + center.y; positionScratch$b.z = vertices[i + 2] + center.z; var candidateMagnitude = computeMagnitude( ellipsoid, positionScratch$b, scaledSpaceDirectionToPoint ); if (candidateMagnitude < 0.0) { // all points should face the same direction, but this one doesn't, so return undefined return undefined; } resultMagnitude = Math.max(resultMagnitude, candidateMagnitude); } return magnitudeToPoint(scaledSpaceDirectionToPoint, resultMagnitude, result); } function isScaledSpacePointVisible( occludeeScaledSpacePosition, cameraPositionInScaledSpace, distanceToLimbInScaledSpaceSquared ) { // See https://cesium.com/blog/2013/04/25/Horizon-culling/ var cv = cameraPositionInScaledSpace; var vhMagnitudeSquared = distanceToLimbInScaledSpaceSquared; var vt = Cartesian3.subtract( occludeeScaledSpacePosition, cv, scratchCartesian$9 ); var vtDotVc = -Cartesian3.dot(vt, cv); // If vhMagnitudeSquared < 0 then we are below the surface of the ellipsoid and // in this case, set the culling plane to be on V. var isOccluded = vhMagnitudeSquared < 0 ? vtDotVc > 0 : vtDotVc > vhMagnitudeSquared && (vtDotVc * vtDotVc) / Cartesian3.magnitudeSquared(vt) > vhMagnitudeSquared; return !isOccluded; } var scaledSpaceScratch = new Cartesian3(); var directionScratch = new Cartesian3(); function computeMagnitude(ellipsoid, position, scaledSpaceDirectionToPoint) { var scaledSpacePosition = ellipsoid.transformPositionToScaledSpace( position, scaledSpaceScratch ); var magnitudeSquared = Cartesian3.magnitudeSquared(scaledSpacePosition); var magnitude = Math.sqrt(magnitudeSquared); var direction = Cartesian3.divideByScalar( scaledSpacePosition, magnitude, directionScratch ); // For the purpose of this computation, points below the ellipsoid are consider to be on it instead. magnitudeSquared = Math.max(1.0, magnitudeSquared); magnitude = Math.max(1.0, magnitude); var cosAlpha = Cartesian3.dot(direction, scaledSpaceDirectionToPoint); var sinAlpha = Cartesian3.magnitude( Cartesian3.cross(direction, scaledSpaceDirectionToPoint, direction) ); var cosBeta = 1.0 / magnitude; var sinBeta = Math.sqrt(magnitudeSquared - 1.0) * cosBeta; return 1.0 / (cosAlpha * cosBeta - sinAlpha * sinBeta); } function magnitudeToPoint( scaledSpaceDirectionToPoint, resultMagnitude, result ) { // The horizon culling point is undefined if there were no positions from which to compute it, // the directionToPoint is pointing opposite all of the positions, or if we computed NaN or infinity. if ( resultMagnitude <= 0.0 || resultMagnitude === 1.0 / 0.0 || resultMagnitude !== resultMagnitude ) { return undefined; } return Cartesian3.multiplyByScalar( scaledSpaceDirectionToPoint, resultMagnitude, result ); } var directionToPointScratch = new Cartesian3(); function computeScaledSpaceDirectionToPoint(ellipsoid, directionToPoint) { if (Cartesian3.equals(directionToPoint, Cartesian3.ZERO)) { return directionToPoint; } ellipsoid.transformPositionToScaledSpace( directionToPoint, directionToPointScratch ); return Cartesian3.normalize(directionToPointScratch, directionToPointScratch); } /** * Defines functions for 2nd order polynomial functions of one variable with only real coefficients. * * @namespace QuadraticRealPolynomial */ var QuadraticRealPolynomial = {}; /** * Provides the discriminant of the quadratic equation from the supplied coefficients. * * @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. */ QuadraticRealPolynomial.computeDiscriminant = function (a, b, c) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); var discriminant = b * b - 4.0 * a * c; return discriminant; }; function addWithCancellationCheck$1(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. * * @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 real valued roots. */ QuadraticRealPolynomial.computeRealRoots = function (a, b, c) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); 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$1(b2, -four_ac, CesiumMath.EPSILON14); if (radicand < 0.0) { // Both roots are complex. return []; } var q = -0.5 * addWithCancellationCheck$1( b, CesiumMath.sign(b) * Math.sqrt(radicand), CesiumMath.EPSILON14 ); if (b > 0.0) { return [q / a, c / q]; } return [c / q, q / a]; }; /** * Defines functions for 3rd order polynomial functions of one variable with only real coefficients. * * @namespace CubicRealPolynomial */ var CubicRealPolynomial = {}; /** * Provides the discriminant of the cubic equation from the supplied coefficients. * * @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. */ CubicRealPolynomial.computeDiscriminant = function (a, b, c, d) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); 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. * * @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 real valued roots. */ CubicRealPolynomial.computeRealRoots = function (a, b, c, d) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); var roots; var ratio; if (a === 0.0) { // Quadratic function: b * x^2 + c * x + d = 0. return QuadraticRealPolynomial.computeRealRoots(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.computeRealRoots(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.computeRealRoots(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); }; /** * Defines functions for 4th order polynomial functions of one variable with only real coefficients. * * @namespace QuarticRealPolynomial */ var QuarticRealPolynomial = {}; /** * Provides the discriminant of the quartic equation from the supplied coefficients. * * @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. */ QuarticRealPolynomial.computeDiscriminant = function (a, b, c, d, e) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); 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.computeRealRoots( 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.computeRealRoots(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.computeRealRoots(1.0, h, m); var roots2 = QuadraticRealPolynomial.computeRealRoots(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.computeRealRoots(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.computeRealRoots(1.0, G, H); var roots2 = QuadraticRealPolynomial.computeRealRoots(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]]; } 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. * * @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 real valued roots. */ QuarticRealPolynomial.computeRealRoots = function (a, b, c, d, e) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); if (Math.abs(a) < CesiumMath.EPSILON15) { return CubicRealPolynomial.computeRealRoots(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; } }; /** * Represents a ray that extends infinitely from the provided origin in the provided direction. * @alias Ray * @constructor * * @param {Cartesian3} [origin=Cartesian3.ZERO] The origin of the ray. * @param {Cartesian3} [direction=Cartesian3.ZERO] The direction of the ray. */ function Ray(origin, direction) { direction = Cartesian3.clone(defaultValue(direction, Cartesian3.ZERO)); if (!Cartesian3.equals(direction, Cartesian3.ZERO)) { Cartesian3.normalize(direction, direction); } /** * The origin of the ray. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.origin = Cartesian3.clone(defaultValue(origin, Cartesian3.ZERO)); /** * The direction of the ray. * @type {Cartesian3} */ this.direction = direction; } /** * Duplicates a Ray instance. * * @param {Ray} ray The ray to duplicate. * @param {Ray} [result] The object onto which to store the result. * @returns {Ray} The modified result parameter or a new Ray instance if one was not provided. (Returns undefined if ray is undefined) */ Ray.clone = function (ray, result) { if (!defined(ray)) { return undefined; } if (!defined(result)) { return new Ray(ray.origin, ray.direction); } result.origin = Cartesian3.clone(ray.origin); result.direction = Cartesian3.clone(ray.direction); return result; }; /** * Computes the point along the ray given by r(t) = o + t*d, * where o is the origin of the ray and d is the direction. * * @param {Ray} ray The ray. * @param {Number} t A scalar value. * @param {Cartesian3} [result] The object in which the result will be stored. * @returns {Cartesian3} The modified result parameter, or a new instance if none was provided. * * @example * //Get the first intersection point of a ray and an ellipsoid. * var intersection = Cesium.IntersectionTests.rayEllipsoid(ray, ellipsoid); * var point = Cesium.Ray.getPoint(ray, intersection.start); */ Ray.getPoint = function (ray, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("ray", ray); Check.typeOf.number("t", t); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } result = Cartesian3.multiplyByScalar(ray.direction, t, result); return Cartesian3.add(ray.origin, result, result); }; /** * Functions for computing the intersection between geometries such as rays, planes, triangles, and ellipsoids. * * @namespace IntersectionTests */ var IntersectionTests = {}; /** * Computes the intersection of a ray and a plane. * * @param {Ray} ray The ray. * @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 intersections. */ IntersectionTests.rayPlane = function (ray, plane, result) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError("ray is required."); } if (!defined(plane)) { throw new DeveloperError("plane is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } 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 scratchEdge0 = new Cartesian3(); var scratchEdge1 = new Cartesian3(); var scratchPVec = new Cartesian3(); var scratchTVec = new Cartesian3(); var scratchQVec = new Cartesian3(); /** * Computes the intersection of a ray and a triangle as a parametric distance along the input ray. The result is negative when the triangle is behind the ray. * * Implements {@link https://cadxfem.org/inf/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf| * Fast Minimum Storage Ray/Triangle Intersection} by Tomas Moller and Ben Trumbore. * * @memberof IntersectionTests * * @param {Ray} ray The ray. * @param {Cartesian3} p0 The first vertex of the triangle. * @param {Cartesian3} p1 The second vertex of the triangle. * @param {Cartesian3} p2 The third vertex of the triangle. * @param {Boolean} [cullBackFaces=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. * @returns {Number} The intersection as a parametric distance along the ray, or undefined if there is no intersection. */ IntersectionTests.rayTriangleParametric = function ( ray, p0, p1, p2, cullBackFaces ) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError("ray is required."); } if (!defined(p0)) { throw new DeveloperError("p0 is required."); } if (!defined(p1)) { throw new DeveloperError("p1 is required."); } if (!defined(p2)) { throw new DeveloperError("p2 is required."); } //>>includeEnd('debug'); cullBackFaces = defaultValue(cullBackFaces, false); var origin = ray.origin; var direction = ray.direction; var edge0 = Cartesian3.subtract(p1, p0, scratchEdge0); var edge1 = Cartesian3.subtract(p2, p0, scratchEdge1); var p = Cartesian3.cross(direction, edge1, scratchPVec); var det = Cartesian3.dot(edge0, p); var tvec; var q; var u; var v; var t; if (cullBackFaces) { if (det < CesiumMath.EPSILON6) { return undefined; } tvec = Cartesian3.subtract(origin, p0, scratchTVec); u = Cartesian3.dot(tvec, p); if (u < 0.0 || u > det) { return undefined; } q = Cartesian3.cross(tvec, edge0, scratchQVec); v = Cartesian3.dot(direction, q); if (v < 0.0 || u + v > det) { return undefined; } t = Cartesian3.dot(edge1, q) / det; } else { if (Math.abs(det) < CesiumMath.EPSILON6) { return undefined; } var invDet = 1.0 / det; tvec = Cartesian3.subtract(origin, p0, scratchTVec); u = Cartesian3.dot(tvec, p) * invDet; if (u < 0.0 || u > 1.0) { return undefined; } q = Cartesian3.cross(tvec, edge0, scratchQVec); v = Cartesian3.dot(direction, q) * invDet; if (v < 0.0 || u + v > 1.0) { return undefined; } t = Cartesian3.dot(edge1, q) * invDet; } return t; }; /** * Computes the intersection of a ray and a triangle as a Cartesian3 coordinate. * * Implements {@link https://cadxfem.org/inf/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf| * Fast Minimum Storage Ray/Triangle Intersection} by Tomas Moller and Ben Trumbore. * * @memberof IntersectionTests * * @param {Ray} ray The ray. * @param {Cartesian3} p0 The first vertex of the triangle. * @param {Cartesian3} p1 The second vertex of the triangle. * @param {Cartesian3} p2 The third vertex of the triangle. * @param {Boolean} [cullBackFaces=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ IntersectionTests.rayTriangle = function ( ray, p0, p1, p2, cullBackFaces, result ) { var t = IntersectionTests.rayTriangleParametric( ray, p0, p1, p2, cullBackFaces ); if (!defined(t) || t < 0.0) { return undefined; } if (!defined(result)) { result = new Cartesian3(); } Cartesian3.multiplyByScalar(ray.direction, t, result); return Cartesian3.add(ray.origin, result, result); }; var scratchLineSegmentTriangleRay = new Ray(); /** * Computes the intersection of a line segment and a triangle. * @memberof IntersectionTests * * @param {Cartesian3} v0 The an end point of the line segment. * @param {Cartesian3} v1 The other end point of the line segment. * @param {Cartesian3} p0 The first vertex of the triangle. * @param {Cartesian3} p1 The second vertex of the triangle. * @param {Cartesian3} p2 The third vertex of the triangle. * @param {Boolean} [cullBackFaces=false] If true, will only compute an intersection with the front face of the triangle * and return undefined for intersections with the back face. * @param {Cartesian3} [result] The Cartesian3 onto which to store the result. * @returns {Cartesian3} The intersection point or undefined if there is no intersections. */ IntersectionTests.lineSegmentTriangle = function ( v0, v1, p0, p1, p2, cullBackFaces, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(v0)) { throw new DeveloperError("v0 is required."); } if (!defined(v1)) { throw new DeveloperError("v1 is required."); } if (!defined(p0)) { throw new DeveloperError("p0 is required."); } if (!defined(p1)) { throw new DeveloperError("p1 is required."); } if (!defined(p2)) { throw new DeveloperError("p2 is required."); } //>>includeEnd('debug'); var ray = scratchLineSegmentTriangleRay; Cartesian3.clone(v0, ray.origin); Cartesian3.subtract(v1, v0, ray.direction); Cartesian3.normalize(ray.direction, ray.direction); var t = IntersectionTests.rayTriangleParametric( ray, p0, p1, p2, cullBackFaces ); if (!defined(t) || t < 0.0 || t > Cartesian3.distance(v0, v1)) { return undefined; } if (!defined(result)) { result = new Cartesian3(); } Cartesian3.multiplyByScalar(ray.direction, t, result); return Cartesian3.add(ray.origin, result, result); }; function solveQuadratic(a, b, c, result) { var det = b * b - 4.0 * a * c; if (det < 0.0) { return undefined; } else if (det > 0.0) { var denom = 1.0 / (2.0 * a); var disc = Math.sqrt(det); var root0 = (-b + disc) * denom; var root1 = (-b - disc) * denom; if (root0 < root1) { result.root0 = root0; result.root1 = root1; } else { result.root0 = root1; result.root1 = root0; } return result; } var root = -b / (2.0 * a); if (root === 0.0) { return undefined; } result.root0 = result.root1 = root; return result; } var raySphereRoots = { root0: 0.0, root1: 0.0, }; function raySphere(ray, sphere, result) { if (!defined(result)) { result = new Interval(); } var origin = ray.origin; var direction = ray.direction; var center = sphere.center; var radiusSquared = sphere.radius * sphere.radius; var diff = Cartesian3.subtract(origin, center, scratchPVec); var a = Cartesian3.dot(direction, direction); var b = 2.0 * Cartesian3.dot(direction, diff); var c = Cartesian3.magnitudeSquared(diff) - radiusSquared; var roots = solveQuadratic(a, b, c, raySphereRoots); if (!defined(roots)) { return undefined; } result.start = roots.root0; result.stop = roots.root1; return result; } /** * Computes the intersection points of a ray with a sphere. * @memberof IntersectionTests * * @param {Ray} ray The ray. * @param {BoundingSphere} sphere The sphere. * @param {Interval} [result] The result onto which to store the result. * @returns {Interval} The interval containing scalar points along the ray or undefined if there are no intersections. */ IntersectionTests.raySphere = function (ray, sphere, result) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError("ray is required."); } if (!defined(sphere)) { throw new DeveloperError("sphere is required."); } //>>includeEnd('debug'); result = raySphere(ray, sphere, result); if (!defined(result) || result.stop < 0.0) { return undefined; } result.start = Math.max(result.start, 0.0); return result; }; var scratchLineSegmentRay = new Ray(); /** * Computes the intersection points of a line segment with a sphere. * @memberof IntersectionTests * * @param {Cartesian3} p0 An end point of the line segment. * @param {Cartesian3} p1 The other end point of the line segment. * @param {BoundingSphere} sphere The sphere. * @param {Interval} [result] The result onto which to store the result. * @returns {Interval} The interval containing scalar points along the ray or undefined if there are no intersections. */ IntersectionTests.lineSegmentSphere = function (p0, p1, sphere, result) { //>>includeStart('debug', pragmas.debug); if (!defined(p0)) { throw new DeveloperError("p0 is required."); } if (!defined(p1)) { throw new DeveloperError("p1 is required."); } if (!defined(sphere)) { throw new DeveloperError("sphere is required."); } //>>includeEnd('debug'); var ray = scratchLineSegmentRay; Cartesian3.clone(p0, ray.origin); var direction = Cartesian3.subtract(p1, p0, ray.direction); var maxT = Cartesian3.magnitude(direction); Cartesian3.normalize(direction, direction); result = raySphere(ray, sphere, result); if (!defined(result) || result.stop < 0.0 || result.start > maxT) { return undefined; } result.start = Math.max(result.start, 0.0); result.stop = Math.min(result.stop, maxT); return result; }; var scratchQ = new Cartesian3(); var scratchW$1 = new Cartesian3(); /** * Computes the intersection points of a ray with an ellipsoid. * * @param {Ray} ray The ray. * @param {Ellipsoid} ellipsoid The ellipsoid. * @returns {Interval} The interval containing scalar points along the ray or undefined if there are no intersections. */ IntersectionTests.rayEllipsoid = function (ray, ellipsoid) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError("ray is required."); } if (!defined(ellipsoid)) { throw new DeveloperError("ellipsoid is required."); } //>>includeEnd('debug'); var inverseRadii = ellipsoid.oneOverRadii; var q = Cartesian3.multiplyComponents(inverseRadii, ray.origin, scratchQ); var w = Cartesian3.multiplyComponents(inverseRadii, ray.direction, scratchW$1); 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 new Interval(root0, root1); } return { start: root1, stop: root0, }; } // qw2 == product. Repeated roots (2 intersections). var root = Math.sqrt(difference / w2); return new Interval(root, 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 new Interval(0.0, temp / w2); } // q2 == 1.0. On ellipsoid. if (qw < 0.0) { // Looking inward. w2 = Cartesian3.magnitudeSquared(w); return new Interval(0.0, -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.computeRealRoots(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.computeRealRoots(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; } var firstAxisScratch = new Cartesian3(); var secondAxisScratch = new Cartesian3(); var thirdAxisScratch = new Cartesian3(); var referenceScratch = new Cartesian3(); var bCart = new Cartesian3(); var bScratch = new Matrix3(); var btScratch = new Matrix3(); var diScratch = new Matrix3(); var dScratch = new Matrix3(); var cScratch = new Matrix3(); var tempMatrix = new Matrix3(); var aScratch = new Matrix3(); var sScratch = new Cartesian3(); var closestScratch = new Cartesian3(); var surfPointScratch = new Cartographic(); /** * Provides the point along the ray which is nearest to the ellipsoid. * * @param {Ray} ray The ray. * @param {Ellipsoid} ellipsoid The ellipsoid. * @returns {Cartesian3} The nearest planetodetic point on the ray. */ IntersectionTests.grazingAltitudeLocation = function (ray, ellipsoid) { //>>includeStart('debug', pragmas.debug); if (!defined(ray)) { throw new DeveloperError("ray is required."); } if (!defined(ellipsoid)) { throw new DeveloperError("ellipsoid is required."); } //>>includeEnd('debug'); var position = ray.origin; var direction = ray.direction; if (!Cartesian3.equals(position, Cartesian3.ZERO)) { var normal = ellipsoid.geodeticSurfaceNormal(position, firstAxisScratch); 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, firstAxisScratch); // Constructs a basis from the unit scaled direction vector. Construct its rotation and transpose. var firstAxis = Cartesian3.normalize(f, f); var reference = Cartesian3.mostOrthogonalAxis(f, referenceScratch); var secondAxis = Cartesian3.normalize( Cartesian3.cross(reference, firstAxis, secondAxisScratch), secondAxisScratch ); var thirdAxis = Cartesian3.normalize( Cartesian3.cross(firstAxis, secondAxis, thirdAxisScratch), thirdAxisScratch ); var B = bScratch; B[0] = firstAxis.x; B[1] = firstAxis.y; B[2] = firstAxis.z; B[3] = secondAxis.x; B[4] = secondAxis.y; B[5] = secondAxis.z; B[6] = thirdAxis.x; B[7] = thirdAxis.y; B[8] = thirdAxis.z; var B_T = Matrix3.transpose(B, btScratch); // Get the scaling matrix and its inverse. var D_I = Matrix3.fromScale(ellipsoid.radii, diScratch); var D = Matrix3.fromScale(ellipsoid.oneOverRadii, dScratch); var C = cScratch; C[0] = 0.0; C[1] = -direction.z; C[2] = direction.y; C[3] = direction.z; C[4] = 0.0; C[5] = -direction.x; C[6] = -direction.y; C[7] = direction.x; C[8] = 0.0; var temp = Matrix3.multiply( Matrix3.multiply(B_T, D, tempMatrix), C, tempMatrix ); var A = Matrix3.multiply(Matrix3.multiply(temp, D_I, aScratch), B, aScratch); var b = Matrix3.multiplyByVector(temp, position, bCart); // Solve for the solutions to the expression in standard form: var solutions = quadraticVectorExpression( A, Cartesian3.negate(b, firstAxisScratch), 0.0, 0.0, 1.0 ); var s; var altitude; var length = solutions.length; if (length > 0) { var closest = Cartesian3.clone(Cartesian3.ZERO, closestScratch); var maximumValue = Number.NEGATIVE_INFINITY; for (var i = 0; i < length; ++i) { s = Matrix3.multiplyByVector( D_I, Matrix3.multiplyByVector(B, solutions[i], sScratch), sScratch ); var v = Cartesian3.normalize( Cartesian3.subtract(s, position, referenceScratch), referenceScratch ); var dotProduct = Cartesian3.dot(v, direction); if (dotProduct > maximumValue) { maximumValue = dotProduct; closest = Cartesian3.clone(s, closest); } } var surfacePoint = ellipsoid.cartesianToCartographic( closest, surfPointScratch ); maximumValue = CesiumMath.clamp(maximumValue, 0.0, 1.0); altitude = Cartesian3.magnitude( Cartesian3.subtract(closest, position, referenceScratch) ) * Math.sqrt(1.0 - maximumValue * maximumValue); altitude = intersects ? -altitude : altitude; surfacePoint.height = altitude; return ellipsoid.cartographicToCartesian(surfacePoint, new Cartesian3()); } return undefined; }; var lineSegmentPlaneDifference = new Cartesian3(); /** * Computes the intersection of a line segment and a plane. * * @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. * * @example * var origin = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); * var normal = ellipsoid.geodeticSurfaceNormal(origin); * var plane = Cesium.Plane.fromPointNormal(origin, normal); * * var p0 = new Cesium.Cartesian3(...); * var p1 = new Cesium.Cartesian3(...); * * // find the intersection of the line segment from p0 to p1 and the tangent plane at origin. * var intersection = Cesium.IntersectionTests.lineSegmentPlane(p0, p1, plane); */ IntersectionTests.lineSegmentPlane = function ( endPoint0, endPoint1, plane, result ) { //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } 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) Cartesian3.multiplyByScalar(difference, t, result); Cartesian3.add(endPoint0, result, result); return result; }; /** * Computes the intersection of a triangle and a plane * * @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 positions and indices, which are arrays that represent three triangles that do not cross the plane. (Undefined if no intersection exists) * * @example * var origin = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); * var normal = ellipsoid.geodeticSurfaceNormal(origin); * var plane = Cesium.Plane.fromPointNormal(origin, normal); * * var p0 = new Cesium.Cartesian3(...); * var p1 = new Cesium.Cartesian3(...); * var p2 = new Cesium.Cartesian3(...); * * // convert the triangle composed of points (p0, p1, p2) to three triangles that don't cross the plane * var triangles = Cesium.IntersectionTests.trianglePlaneIntersection(p0, p1, p2, plane); */ IntersectionTests.trianglePlaneIntersection = function (p0, p1, p2, plane) { //>>includeStart('debug', pragmas.debug); if (!defined(p0) || !defined(p1) || !defined(p2) || !defined(plane)) { throw new DeveloperError("p0, p1, p2, and plane are required."); } //>>includeEnd('debug'); 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; }; /** * A plane in Hessian Normal Form defined by *
	 * ax + by + cz + d = 0
	 * 
* where (a, b, c) is the plane's normal, d is the signed * distance 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 * distance determines which side of the plane the origin * is on. If distance 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. * * @example * // The plane x=0 * var plane = new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0); * * @exception {DeveloperError} Normal must be normalized */ function Plane(normal, distance) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("normal", normal); if ( !CesiumMath.equalsEpsilon( Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6 ) ) { throw new DeveloperError("normal must be normalized."); } Check.typeOf.number("distance", distance); //>>includeEnd('debug'); /** * The plane's normal. * * @type {Cartesian3} */ this.normal = Cartesian3.clone(normal); /** * The shortest distance from the origin to the plane. The sign of * distance determines which side of the plane the origin * is on. If distance 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. * * @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. * * @example * var point = Cesium.Cartesian3.fromDegrees(-72.0, 40.0); * var normal = ellipsoid.geodeticSurfaceNormal(point); * var tangentPlane = Cesium.Plane.fromPointNormal(point, normal); * * @exception {DeveloperError} Normal must be normalized */ Plane.fromPointNormal = function (point, normal, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("point", point); Check.typeOf.object("normal", normal); if ( !CesiumMath.equalsEpsilon( Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6 ) ) { throw new DeveloperError("normal must be normalized."); } //>>includeEnd('debug'); var distance = -Cartesian3.dot(normal, point); if (!defined(result)) { return new Plane(normal, distance); } Cartesian3.clone(normal, result.normal); result.distance = distance; return result; }; var scratchNormal$7 = new Cartesian3(); /** * Creates a plane from the general equation * * @param {Cartesian4} coefficients 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} Normal must be normalized */ Plane.fromCartesian4 = function (coefficients, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("coefficients", coefficients); //>>includeEnd('debug'); var normal = Cartesian3.fromCartesian4(coefficients, scratchNormal$7); var distance = coefficients.w; //>>includeStart('debug', pragmas.debug); if ( !CesiumMath.equalsEpsilon( Cartesian3.magnitude(normal), 1.0, CesiumMath.EPSILON6 ) ) { throw new DeveloperError("normal must be normalized."); } //>>includeEnd('debug'); 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. * * @param {Plane} plane The plane. * @param {Cartesian3} point The point. * @returns {Number} The signed shortest distance of the point to the plane. */ Plane.getPointDistance = function (plane, point) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("plane", plane); Check.typeOf.object("point", point); //>>includeEnd('debug'); return Cartesian3.dot(plane.normal, point) + plane.distance; }; var scratchCartesian$8 = new Cartesian3(); /** * Projects a point onto the plane. * @param {Plane} plane The plane to project the point onto * @param {Cartesian3} point The point to project onto the plane * @param {Cartesian3} [result] The result point. If undefined, a new Cartesian3 will be created. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. */ Plane.projectPointOntoPlane = function (plane, point, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("plane", plane); Check.typeOf.object("point", point); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } // projectedPoint = point - (normal.point + scale) * normal var pointDistance = Plane.getPointDistance(plane, point); var scaledNormal = Cartesian3.multiplyByScalar( plane.normal, pointDistance, scratchCartesian$8 ); return Cartesian3.subtract(point, scaledNormal, result); }; var scratchInverseTranspose = new Matrix4(); var scratchPlaneCartesian4 = new Cartesian4(); var scratchTransformNormal = new Cartesian3(); /** * Transforms the plane by the given transformation matrix. * * @param {Plane} plane The plane. * @param {Matrix4} transform The transformation matrix. * @param {Plane} [result] The object into which to store the result. * @returns {Plane} The plane transformed by the given transformation matrix. */ Plane.transform = function (plane, transform, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("plane", plane); Check.typeOf.object("transform", transform); //>>includeEnd('debug'); var normal = plane.normal; var distance = plane.distance; var inverseTranspose = Matrix4.inverseTranspose( transform, scratchInverseTranspose ); var planeAsCartesian4 = Cartesian4.fromElements( normal.x, normal.y, normal.z, distance, scratchPlaneCartesian4 ); planeAsCartesian4 = Matrix4.multiplyByVector( inverseTranspose, planeAsCartesian4, planeAsCartesian4 ); // Convert the transformed plane to Hessian Normal Form var transformedNormal = Cartesian3.fromCartesian4( planeAsCartesian4, scratchTransformNormal ); planeAsCartesian4 = Cartesian4.divideByScalar( planeAsCartesian4, Cartesian3.magnitude(transformedNormal), planeAsCartesian4 ); return Plane.fromCartesian4(planeAsCartesian4, result); }; /** * Duplicates a Plane instance. * * @param {Plane} plane The plane to duplicate. * @param {Plane} [result] The object onto which to store the result. * @returns {Plane} The modified result parameter or a new Plane instance if one was not provided. */ Plane.clone = function (plane, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("plane", plane); //>>includeEnd('debug'); if (!defined(result)) { return new Plane(plane.normal, plane.distance); } Cartesian3.clone(plane.normal, result.normal); result.distance = plane.distance; return result; }; /** * Compares the provided Planes by normal and distance and returns * true if they are equal, false otherwise. * * @param {Plane} left The first plane. * @param {Plane} right The second plane. * @returns {Boolean} true if left and right are equal, false otherwise. */ Plane.equals = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); return ( left.distance === right.distance && Cartesian3.equals(left.normal, right.normal) ); }; /** * A constant initialized to the XY plane passing through the origin, with normal in positive Z. * * @type {Plane} * @constant */ Plane.ORIGIN_XY_PLANE = Object.freeze(new Plane(Cartesian3.UNIT_Z, 0.0)); /** * A constant initialized to the YZ plane passing through the origin, with normal in positive X. * * @type {Plane} * @constant */ Plane.ORIGIN_YZ_PLANE = Object.freeze(new Plane(Cartesian3.UNIT_X, 0.0)); /** * A constant initialized to the ZX plane passing through the origin, with normal in positive Y. * * @type {Plane} * @constant */ Plane.ORIGIN_ZX_PLANE = Object.freeze(new Plane(Cartesian3.UNIT_Y, 0.0)); var scratchCart4 = new Cartesian4(); /** * A plane tangent to the provided ellipsoid at the provided origin. * If origin is not on the surface of the ellipsoid, it's surface projection will be used. * If origin is at the center of the ellipsoid, an exception will be thrown. * @alias EllipsoidTangentPlane * @constructor * * @param {Cartesian3} origin The point on the surface of the ellipsoid where the tangent plane touches. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. * * @exception {DeveloperError} origin must not be at the center of the ellipsoid. */ function EllipsoidTangentPlane(origin, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined("origin", origin); //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); origin = ellipsoid.scaleToGeodeticSurface(origin); //>>includeStart('debug', pragmas.debug); if (!defined(origin)) { throw new DeveloperError( "origin must not be at the center of the ellipsoid." ); } //>>includeEnd('debug'); var eastNorthUp = Transforms.eastNorthUpToFixedFrame(origin, ellipsoid); this._ellipsoid = ellipsoid; this._origin = origin; this._xAxis = Cartesian3.fromCartesian4( Matrix4.getColumn(eastNorthUp, 0, scratchCart4) ); this._yAxis = Cartesian3.fromCartesian4( Matrix4.getColumn(eastNorthUp, 1, scratchCart4) ); var normal = Cartesian3.fromCartesian4( Matrix4.getColumn(eastNorthUp, 2, scratchCart4) ); this._plane = Plane.fromPointNormal(origin, normal); } Object.defineProperties(EllipsoidTangentPlane.prototype, { /** * Gets the ellipsoid. * @memberof EllipsoidTangentPlane.prototype * @type {Ellipsoid} */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets the origin. * @memberof EllipsoidTangentPlane.prototype * @type {Cartesian3} */ origin: { get: function () { return this._origin; }, }, /** * Gets the plane which is tangent to the ellipsoid. * @memberof EllipsoidTangentPlane.prototype * @readonly * @type {Plane} */ plane: { get: function () { return this._plane; }, }, /** * Gets the local X-axis (east) of the tangent plane. * @memberof EllipsoidTangentPlane.prototype * @readonly * @type {Cartesian3} */ xAxis: { get: function () { return this._xAxis; }, }, /** * Gets the local Y-axis (north) of the tangent plane. * @memberof EllipsoidTangentPlane.prototype * @readonly * @type {Cartesian3} */ yAxis: { get: function () { return this._yAxis; }, }, /** * Gets the local Z-axis (up) of the tangent plane. * @memberof EllipsoidTangentPlane.prototype * @readonly * @type {Cartesian3} */ zAxis: { get: function () { return this._plane.normal; }, }, }); var tmp$7 = new AxisAlignedBoundingBox(); /** * Creates a new instance from the provided ellipsoid and the center * point of the provided Cartesians. * * @param {Cartesian3[]} cartesians The list of positions surrounding the center point. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. * @returns {EllipsoidTangentPlane} The new instance of EllipsoidTangentPlane. */ EllipsoidTangentPlane.fromPoints = function (cartesians, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); var box = AxisAlignedBoundingBox.fromPoints(cartesians, tmp$7); return new EllipsoidTangentPlane(box.center, ellipsoid); }; var scratchProjectPointOntoPlaneRay = new Ray(); var scratchProjectPointOntoPlaneCartesian3 = new Cartesian3(); /** * Computes the projection of the provided 3D position onto the 2D plane, radially outward from the {@link EllipsoidTangentPlane.ellipsoid} coordinate system origin. * * @param {Cartesian3} cartesian The point to project. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. Undefined if there is no intersection point */ EllipsoidTangentPlane.prototype.projectPointOntoPlane = function ( cartesian, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesian", cartesian); //>>includeEnd('debug'); var ray = scratchProjectPointOntoPlaneRay; ray.origin = cartesian; Cartesian3.normalize(cartesian, ray.direction); var intersectionPoint = IntersectionTests.rayPlane( ray, this._plane, scratchProjectPointOntoPlaneCartesian3 ); if (!defined(intersectionPoint)) { Cartesian3.negate(ray.direction, ray.direction); intersectionPoint = IntersectionTests.rayPlane( ray, this._plane, scratchProjectPointOntoPlaneCartesian3 ); } if (defined(intersectionPoint)) { var v = Cartesian3.subtract( intersectionPoint, this._origin, intersectionPoint ); var x = Cartesian3.dot(this._xAxis, v); var y = Cartesian3.dot(this._yAxis, v); if (!defined(result)) { return new Cartesian2(x, y); } result.x = x; result.y = y; return result; } return undefined; }; /** * Computes the projection of the provided 3D positions onto the 2D plane (where possible), radially outward from the global origin. * The resulting array may be shorter than the input array - if a single projection is impossible it will not be included. * * @see EllipsoidTangentPlane.projectPointOntoPlane * * @param {Cartesian3[]} cartesians The array of points to project. * @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results. * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided. */ EllipsoidTangentPlane.prototype.projectPointsOntoPlane = function ( cartesians, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); if (!defined(result)) { result = []; } var count = 0; var length = cartesians.length; for (var i = 0; i < length; i++) { var p = this.projectPointOntoPlane(cartesians[i], result[count]); if (defined(p)) { result[count] = p; count++; } } result.length = count; return result; }; /** * Computes the projection of the provided 3D position onto the 2D plane, along the plane normal. * * @param {Cartesian3} cartesian The point to project. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided. */ EllipsoidTangentPlane.prototype.projectPointToNearestOnPlane = function ( cartesian, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesian", cartesian); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian2(); } var ray = scratchProjectPointOntoPlaneRay; ray.origin = cartesian; Cartesian3.clone(this._plane.normal, ray.direction); var intersectionPoint = IntersectionTests.rayPlane( ray, this._plane, scratchProjectPointOntoPlaneCartesian3 ); if (!defined(intersectionPoint)) { Cartesian3.negate(ray.direction, ray.direction); intersectionPoint = IntersectionTests.rayPlane( ray, this._plane, scratchProjectPointOntoPlaneCartesian3 ); } var v = Cartesian3.subtract( intersectionPoint, this._origin, intersectionPoint ); var x = Cartesian3.dot(this._xAxis, v); var y = Cartesian3.dot(this._yAxis, v); result.x = x; result.y = y; return result; }; /** * Computes the projection of the provided 3D positions onto the 2D plane, along the plane normal. * * @see EllipsoidTangentPlane.projectPointToNearestOnPlane * * @param {Cartesian3[]} cartesians The array of points to project. * @param {Cartesian2[]} [result] The array of Cartesian2 instances onto which to store results. * @returns {Cartesian2[]} The modified result parameter or a new array of Cartesian2 instances if none was provided. This will have the same length as cartesians. */ EllipsoidTangentPlane.prototype.projectPointsToNearestOnPlane = function ( cartesians, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); if (!defined(result)) { result = []; } var length = cartesians.length; result.length = length; for (var i = 0; i < length; i++) { result[i] = this.projectPointToNearestOnPlane(cartesians[i], result[i]); } return result; }; var projectPointsOntoEllipsoidScratch = new Cartesian3(); /** * Computes the projection of the provided 2D position onto the 3D ellipsoid. * * @param {Cartesian2} cartesian The points to project. * @param {Cartesian3} [result] The Cartesian3 instance to store result. * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. */ EllipsoidTangentPlane.prototype.projectPointOntoEllipsoid = function ( cartesian, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesian", cartesian); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } var ellipsoid = this._ellipsoid; var origin = this._origin; var xAxis = this._xAxis; var yAxis = this._yAxis; var tmp = projectPointsOntoEllipsoidScratch; Cartesian3.multiplyByScalar(xAxis, cartesian.x, tmp); result = Cartesian3.add(origin, tmp, result); Cartesian3.multiplyByScalar(yAxis, cartesian.y, tmp); Cartesian3.add(result, tmp, result); ellipsoid.scaleToGeocentricSurface(result, result); return result; }; /** * Computes the projection of the provided 2D positions onto the 3D ellipsoid. * * @param {Cartesian2[]} cartesians The array of points to project. * @param {Cartesian3[]} [result] The array of Cartesian3 instances onto which to store results. * @returns {Cartesian3[]} The modified result parameter or a new array of Cartesian3 instances if none was provided. */ EllipsoidTangentPlane.prototype.projectPointsOntoEllipsoid = function ( cartesians, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesians", cartesians); //>>includeEnd('debug'); 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.projectPointOntoEllipsoid(cartesians[i], result[i]); } return result; }; /** * Creates an instance of an OrientedBoundingBox. * An OrientedBoundingBox of some object is a closed and convex cuboid. It can provide a tighter bounding volume than {@link BoundingSphere} or {@link AxisAlignedBoundingBox} in many cases. * @alias OrientedBoundingBox * @constructor * * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box. * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box. * Equivalently, the transformation matrix, to rotate and scale a 0x0x0 * cube centered at the origin. * * * @example * // Create an OrientedBoundingBox using a transformation matrix, a position where the box will be translated, and a scale. * var center = new Cesium.Cartesian3(1.0, 0.0, 0.0); * var halfAxes = Cesium.Matrix3.fromScale(new Cesium.Cartesian3(1.0, 3.0, 2.0), new Cesium.Matrix3()); * * var obb = new Cesium.OrientedBoundingBox(center, halfAxes); * * @see BoundingSphere * @see BoundingRectangle */ function OrientedBoundingBox(center, halfAxes) { /** * The center of the box. * @type {Cartesian3} * @default {@link Cartesian3.ZERO} */ this.center = Cartesian3.clone(defaultValue(center, Cartesian3.ZERO)); /** * The transformation matrix, to rotate the box to the right position. * @type {Matrix3} * @default {@link Matrix3.ZERO} */ this.halfAxes = Matrix3.clone(defaultValue(halfAxes, Matrix3.ZERO)); } /** * The number of elements used to pack the object into an array. * @type {Number} */ OrientedBoundingBox.packedLength = Cartesian3.packedLength + Matrix3.packedLength; /** * Stores the provided instance into the provided array. * * @param {OrientedBoundingBox} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ OrientedBoundingBox.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value.center, array, startingIndex); Matrix3.pack(value.halfAxes, array, startingIndex + Cartesian3.packedLength); return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {OrientedBoundingBox} [result] The object into which to store the result. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. */ OrientedBoundingBox.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new OrientedBoundingBox(); } Cartesian3.unpack(array, startingIndex, result.center); Matrix3.unpack( array, startingIndex + Cartesian3.packedLength, result.halfAxes ); return result; }; var scratchCartesian1$8 = new Cartesian3(); var scratchCartesian2$b = new Cartesian3(); var scratchCartesian3$c = new Cartesian3(); var scratchCartesian4$6 = new Cartesian3(); var scratchCartesian5$2 = new Cartesian3(); var scratchCartesian6$1 = new Cartesian3(); var scratchCovarianceResult = new Matrix3(); var scratchEigenResult = { unitary: new Matrix3(), diagonal: new Matrix3(), }; /** * Computes an instance of an OrientedBoundingBox of the given positions. * This is an implementation of Stefan Gottschalk's Collision Queries using Oriented Bounding Boxes solution (PHD thesis). * Reference: http://gamma.cs.unc.edu/users/gottschalk/main.pdf * * @param {Cartesian3[]} [positions] List of {@link Cartesian3} points that the bounding box will enclose. * @param {OrientedBoundingBox} [result] The object onto which to store the result. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. * * @example * // Compute an object oriented bounding box enclosing two points. * var box = Cesium.OrientedBoundingBox.fromPoints([new Cesium.Cartesian3(2, 0, 0), new Cesium.Cartesian3(-2, 0, 0)]); */ OrientedBoundingBox.fromPoints = function (positions, result) { if (!defined(result)) { result = new OrientedBoundingBox(); } if (!defined(positions) || positions.length === 0) { result.halfAxes = Matrix3.ZERO; result.center = Cartesian3.ZERO; return result; } var i; var length = positions.length; var meanPoint = Cartesian3.clone(positions[0], scratchCartesian1$8); for (i = 1; i < length; i++) { Cartesian3.add(meanPoint, positions[i], meanPoint); } var invLength = 1.0 / length; Cartesian3.multiplyByScalar(meanPoint, invLength, meanPoint); var exx = 0.0; var exy = 0.0; var exz = 0.0; var eyy = 0.0; var eyz = 0.0; var ezz = 0.0; var p; for (i = 0; i < length; i++) { p = Cartesian3.subtract(positions[i], meanPoint, scratchCartesian2$b); exx += p.x * p.x; exy += p.x * p.y; exz += p.x * p.z; eyy += p.y * p.y; eyz += p.y * p.z; ezz += p.z * p.z; } exx *= invLength; exy *= invLength; exz *= invLength; eyy *= invLength; eyz *= invLength; ezz *= invLength; var covarianceMatrix = scratchCovarianceResult; covarianceMatrix[0] = exx; covarianceMatrix[1] = exy; covarianceMatrix[2] = exz; covarianceMatrix[3] = exy; covarianceMatrix[4] = eyy; covarianceMatrix[5] = eyz; covarianceMatrix[6] = exz; covarianceMatrix[7] = eyz; covarianceMatrix[8] = ezz; var eigenDecomposition = Matrix3.computeEigenDecomposition( covarianceMatrix, scratchEigenResult ); var rotation = Matrix3.clone(eigenDecomposition.unitary, result.halfAxes); var v1 = Matrix3.getColumn(rotation, 0, scratchCartesian4$6); var v2 = Matrix3.getColumn(rotation, 1, scratchCartesian5$2); var v3 = Matrix3.getColumn(rotation, 2, scratchCartesian6$1); var u1 = -Number.MAX_VALUE; var u2 = -Number.MAX_VALUE; var u3 = -Number.MAX_VALUE; var l1 = Number.MAX_VALUE; var l2 = Number.MAX_VALUE; var l3 = Number.MAX_VALUE; for (i = 0; i < length; i++) { p = positions[i]; u1 = Math.max(Cartesian3.dot(v1, p), u1); u2 = Math.max(Cartesian3.dot(v2, p), u2); u3 = Math.max(Cartesian3.dot(v3, p), u3); l1 = Math.min(Cartesian3.dot(v1, p), l1); l2 = Math.min(Cartesian3.dot(v2, p), l2); l3 = Math.min(Cartesian3.dot(v3, p), l3); } v1 = Cartesian3.multiplyByScalar(v1, 0.5 * (l1 + u1), v1); v2 = Cartesian3.multiplyByScalar(v2, 0.5 * (l2 + u2), v2); v3 = Cartesian3.multiplyByScalar(v3, 0.5 * (l3 + u3), v3); var center = Cartesian3.add(v1, v2, result.center); Cartesian3.add(center, v3, center); var scale = scratchCartesian3$c; scale.x = u1 - l1; scale.y = u2 - l2; scale.z = u3 - l3; Cartesian3.multiplyByScalar(scale, 0.5, scale); Matrix3.multiplyByScale(result.halfAxes, scale, result.halfAxes); return result; }; var scratchOffset = new Cartesian3(); var scratchScale$5 = new Cartesian3(); function fromPlaneExtents( planeOrigin, planeXAxis, planeYAxis, planeZAxis, minimumX, maximumX, minimumY, maximumY, minimumZ, maximumZ, result ) { //>>includeStart('debug', pragmas.debug); if ( !defined(minimumX) || !defined(maximumX) || !defined(minimumY) || !defined(maximumY) || !defined(minimumZ) || !defined(maximumZ) ) { throw new DeveloperError( "all extents (minimum/maximum X/Y/Z) are required." ); } //>>includeEnd('debug'); if (!defined(result)) { result = new OrientedBoundingBox(); } var halfAxes = result.halfAxes; Matrix3.setColumn(halfAxes, 0, planeXAxis, halfAxes); Matrix3.setColumn(halfAxes, 1, planeYAxis, halfAxes); Matrix3.setColumn(halfAxes, 2, planeZAxis, halfAxes); var centerOffset = scratchOffset; centerOffset.x = (minimumX + maximumX) / 2.0; centerOffset.y = (minimumY + maximumY) / 2.0; centerOffset.z = (minimumZ + maximumZ) / 2.0; var scale = scratchScale$5; scale.x = (maximumX - minimumX) / 2.0; scale.y = (maximumY - minimumY) / 2.0; scale.z = (maximumZ - minimumZ) / 2.0; var center = result.center; centerOffset = Matrix3.multiplyByVector(halfAxes, centerOffset, centerOffset); Cartesian3.add(planeOrigin, centerOffset, center); Matrix3.multiplyByScale(halfAxes, scale, halfAxes); return result; } var scratchRectangleCenterCartographic = new Cartographic(); var scratchRectangleCenter = new Cartesian3(); var scratchPerimeterCartographicNC = new Cartographic(); var scratchPerimeterCartographicNW = new Cartographic(); var scratchPerimeterCartographicCW = new Cartographic(); var scratchPerimeterCartographicSW = new Cartographic(); var scratchPerimeterCartographicSC = new Cartographic(); var scratchPerimeterCartesianNC = new Cartesian3(); var scratchPerimeterCartesianNW = new Cartesian3(); var scratchPerimeterCartesianCW = new Cartesian3(); var scratchPerimeterCartesianSW = new Cartesian3(); var scratchPerimeterCartesianSC = new Cartesian3(); var scratchPerimeterProjectedNC = new Cartesian2(); var scratchPerimeterProjectedNW = new Cartesian2(); var scratchPerimeterProjectedCW = new Cartesian2(); var scratchPerimeterProjectedSW = new Cartesian2(); var scratchPerimeterProjectedSC = new Cartesian2(); var scratchPlaneOrigin = new Cartesian3(); var scratchPlaneNormal$1 = new Cartesian3(); var scratchPlaneXAxis = new Cartesian3(); var scratchHorizonCartesian = new Cartesian3(); var scratchHorizonProjected = new Cartesian2(); var scratchMaxY = new Cartesian3(); var scratchMinY = new Cartesian3(); var scratchZ = new Cartesian3(); var scratchPlane$2 = new Plane(Cartesian3.UNIT_X, 0.0); /** * Computes an OrientedBoundingBox that bounds a {@link Rectangle} on the surface of an {@link Ellipsoid}. * There are no guarantees about the orientation of the bounding box. * * @param {Rectangle} rectangle The cartographic rectangle on the surface of the ellipsoid. * @param {Number} [minimumHeight=0.0] The minimum height (elevation) within the tile. * @param {Number} [maximumHeight=0.0] The maximum height (elevation) within the tile. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle is defined. * @param {OrientedBoundingBox} [result] The object onto which to store the result. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided. * * @exception {DeveloperError} rectangle.width must be between 0 and pi. * @exception {DeveloperError} rectangle.height must be between 0 and pi. * @exception {DeveloperError} ellipsoid must be an ellipsoid of revolution (radii.x == radii.y) */ OrientedBoundingBox.fromRectangle = function ( rectangle, minimumHeight, maximumHeight, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(rectangle)) { throw new DeveloperError("rectangle is required"); } if (rectangle.width < 0.0 || rectangle.width > CesiumMath.TWO_PI) { throw new DeveloperError("Rectangle width must be between 0 and 2*pi"); } if (rectangle.height < 0.0 || rectangle.height > CesiumMath.PI) { throw new DeveloperError("Rectangle height must be between 0 and pi"); } if ( defined(ellipsoid) && !CesiumMath.equalsEpsilon( ellipsoid.radii.x, ellipsoid.radii.y, CesiumMath.EPSILON15 ) ) { throw new DeveloperError( "Ellipsoid must be an ellipsoid of revolution (radii.x == radii.y)" ); } //>>includeEnd('debug'); minimumHeight = defaultValue(minimumHeight, 0.0); maximumHeight = defaultValue(maximumHeight, 0.0); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var minX, maxX, minY, maxY, minZ, maxZ, plane; if (rectangle.width <= CesiumMath.PI) { // The bounding box will be aligned with the tangent plane at the center of the rectangle. var tangentPointCartographic = Rectangle.center( rectangle, scratchRectangleCenterCartographic ); var tangentPoint = ellipsoid.cartographicToCartesian( tangentPointCartographic, scratchRectangleCenter ); var tangentPlane = new EllipsoidTangentPlane(tangentPoint, ellipsoid); plane = tangentPlane.plane; // If the rectangle spans the equator, CW is instead aligned with the equator (because it sticks out the farthest at the equator). var lonCenter = tangentPointCartographic.longitude; var latCenter = rectangle.south < 0.0 && rectangle.north > 0.0 ? 0.0 : tangentPointCartographic.latitude; // Compute XY extents using the rectangle at maximum height var perimeterCartographicNC = Cartographic.fromRadians( lonCenter, rectangle.north, maximumHeight, scratchPerimeterCartographicNC ); var perimeterCartographicNW = Cartographic.fromRadians( rectangle.west, rectangle.north, maximumHeight, scratchPerimeterCartographicNW ); var perimeterCartographicCW = Cartographic.fromRadians( rectangle.west, latCenter, maximumHeight, scratchPerimeterCartographicCW ); var perimeterCartographicSW = Cartographic.fromRadians( rectangle.west, rectangle.south, maximumHeight, scratchPerimeterCartographicSW ); var perimeterCartographicSC = Cartographic.fromRadians( lonCenter, rectangle.south, maximumHeight, scratchPerimeterCartographicSC ); var perimeterCartesianNC = ellipsoid.cartographicToCartesian( perimeterCartographicNC, scratchPerimeterCartesianNC ); var perimeterCartesianNW = ellipsoid.cartographicToCartesian( perimeterCartographicNW, scratchPerimeterCartesianNW ); var perimeterCartesianCW = ellipsoid.cartographicToCartesian( perimeterCartographicCW, scratchPerimeterCartesianCW ); var perimeterCartesianSW = ellipsoid.cartographicToCartesian( perimeterCartographicSW, scratchPerimeterCartesianSW ); var perimeterCartesianSC = ellipsoid.cartographicToCartesian( perimeterCartographicSC, scratchPerimeterCartesianSC ); var perimeterProjectedNC = tangentPlane.projectPointToNearestOnPlane( perimeterCartesianNC, scratchPerimeterProjectedNC ); var perimeterProjectedNW = tangentPlane.projectPointToNearestOnPlane( perimeterCartesianNW, scratchPerimeterProjectedNW ); var perimeterProjectedCW = tangentPlane.projectPointToNearestOnPlane( perimeterCartesianCW, scratchPerimeterProjectedCW ); var perimeterProjectedSW = tangentPlane.projectPointToNearestOnPlane( perimeterCartesianSW, scratchPerimeterProjectedSW ); var perimeterProjectedSC = tangentPlane.projectPointToNearestOnPlane( perimeterCartesianSC, scratchPerimeterProjectedSC ); minX = Math.min( perimeterProjectedNW.x, perimeterProjectedCW.x, perimeterProjectedSW.x ); maxX = -minX; // symmetrical maxY = Math.max(perimeterProjectedNW.y, perimeterProjectedNC.y); minY = Math.min(perimeterProjectedSW.y, perimeterProjectedSC.y); // Compute minimum Z using the rectangle at minimum height, since it will be deeper than the maximum height perimeterCartographicNW.height = perimeterCartographicSW.height = minimumHeight; perimeterCartesianNW = ellipsoid.cartographicToCartesian( perimeterCartographicNW, scratchPerimeterCartesianNW ); perimeterCartesianSW = ellipsoid.cartographicToCartesian( perimeterCartographicSW, scratchPerimeterCartesianSW ); minZ = Math.min( Plane.getPointDistance(plane, perimeterCartesianNW), Plane.getPointDistance(plane, perimeterCartesianSW) ); maxZ = maximumHeight; // Since the tangent plane touches the surface at height = 0, this is okay return fromPlaneExtents( tangentPlane.origin, tangentPlane.xAxis, tangentPlane.yAxis, tangentPlane.zAxis, minX, maxX, minY, maxY, minZ, maxZ, result ); } // Handle the case where rectangle width is greater than PI (wraps around more than half the ellipsoid). var fullyAboveEquator = rectangle.south > 0.0; var fullyBelowEquator = rectangle.north < 0.0; var latitudeNearestToEquator = fullyAboveEquator ? rectangle.south : fullyBelowEquator ? rectangle.north : 0.0; var centerLongitude = Rectangle.center( rectangle, scratchRectangleCenterCartographic ).longitude; // Plane is located at the rectangle's center longitude and the rectangle's latitude that is closest to the equator. It rotates around the Z axis. // This results in a better fit than the obb approach for smaller rectangles, which orients with the rectangle's center normal. var planeOrigin = Cartesian3.fromRadians( centerLongitude, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchPlaneOrigin ); planeOrigin.z = 0.0; // center the plane on the equator to simpify plane normal calculation var isPole = Math.abs(planeOrigin.x) < CesiumMath.EPSILON10 && Math.abs(planeOrigin.y) < CesiumMath.EPSILON10; var planeNormal = !isPole ? Cartesian3.normalize(planeOrigin, scratchPlaneNormal$1) : Cartesian3.UNIT_X; var planeYAxis = Cartesian3.UNIT_Z; var planeXAxis = Cartesian3.cross(planeNormal, planeYAxis, scratchPlaneXAxis); plane = Plane.fromPointNormal(planeOrigin, planeNormal, scratchPlane$2); // Get the horizon point relative to the center. This will be the farthest extent in the plane's X dimension. var horizonCartesian = Cartesian3.fromRadians( centerLongitude + CesiumMath.PI_OVER_TWO, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchHorizonCartesian ); maxX = Cartesian3.dot( Plane.projectPointOntoPlane( plane, horizonCartesian, scratchHorizonProjected ), planeXAxis ); minX = -maxX; // symmetrical // Get the min and max Y, using the height that will give the largest extent maxY = Cartesian3.fromRadians( 0.0, rectangle.north, fullyBelowEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMaxY ).z; minY = Cartesian3.fromRadians( 0.0, rectangle.south, fullyAboveEquator ? minimumHeight : maximumHeight, ellipsoid, scratchMinY ).z; var farZ = Cartesian3.fromRadians( rectangle.east, latitudeNearestToEquator, maximumHeight, ellipsoid, scratchZ ); minZ = Plane.getPointDistance(plane, farZ); maxZ = 0.0; // plane origin starts at maxZ already // min and max are local to the plane axes return fromPlaneExtents( planeOrigin, planeXAxis, planeYAxis, planeNormal, minX, maxX, minY, maxY, minZ, maxZ, result ); }; /** * Duplicates a OrientedBoundingBox instance. * * @param {OrientedBoundingBox} box The bounding box to duplicate. * @param {OrientedBoundingBox} [result] The object onto which to store the result. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if none was provided. (Returns undefined if box is undefined) */ OrientedBoundingBox.clone = function (box, result) { if (!defined(box)) { return undefined; } if (!defined(result)) { return new OrientedBoundingBox(box.center, box.halfAxes); } Cartesian3.clone(box.center, result.center); Matrix3.clone(box.halfAxes, result.halfAxes); return result; }; /** * Determines which side of a plane the oriented bounding box is located. * * @param {OrientedBoundingBox} box The oriented bounding box to test. * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ OrientedBoundingBox.intersectPlane = function (box, plane) { //>>includeStart('debug', pragmas.debug); if (!defined(box)) { throw new DeveloperError("box is required."); } if (!defined(plane)) { throw new DeveloperError("plane is required."); } //>>includeEnd('debug'); var center = box.center; var normal = plane.normal; var halfAxes = box.halfAxes; var normalX = normal.x, normalY = normal.y, normalZ = normal.z; // plane is used as if it is its normal; the first three components are assumed to be normalized var radEffective = Math.abs( normalX * halfAxes[Matrix3.COLUMN0ROW0] + normalY * halfAxes[Matrix3.COLUMN0ROW1] + normalZ * halfAxes[Matrix3.COLUMN0ROW2] ) + Math.abs( normalX * halfAxes[Matrix3.COLUMN1ROW0] + normalY * halfAxes[Matrix3.COLUMN1ROW1] + normalZ * halfAxes[Matrix3.COLUMN1ROW2] ) + Math.abs( normalX * halfAxes[Matrix3.COLUMN2ROW0] + normalY * halfAxes[Matrix3.COLUMN2ROW1] + normalZ * halfAxes[Matrix3.COLUMN2ROW2] ); var distanceToPlane = Cartesian3.dot(normal, center) + plane.distance; if (distanceToPlane <= -radEffective) { // The entire box is on the negative side of the plane normal return Intersect$1.OUTSIDE; } else if (distanceToPlane >= radEffective) { // The entire box is on the positive side of the plane normal return Intersect$1.INSIDE; } return Intersect$1.INTERSECTING; }; var scratchCartesianU = new Cartesian3(); var scratchCartesianV = new Cartesian3(); var scratchCartesianW = new Cartesian3(); var scratchPPrime = new Cartesian3(); /** * Computes the estimated distance squared from the closest point on a bounding box to a point. * * @param {OrientedBoundingBox} box The box. * @param {Cartesian3} cartesian The point * @returns {Number} The estimated distance squared from the bounding sphere to the point. * * @example * // Sort bounding boxes from back to front * boxes.sort(function(a, b) { * return Cesium.OrientedBoundingBox.distanceSquaredTo(b, camera.positionWC) - Cesium.OrientedBoundingBox.distanceSquaredTo(a, camera.positionWC); * }); */ OrientedBoundingBox.distanceSquaredTo = function (box, cartesian) { // See Geometric Tools for Computer Graphics 10.4.2 //>>includeStart('debug', pragmas.debug); if (!defined(box)) { throw new DeveloperError("box is required."); } if (!defined(cartesian)) { throw new DeveloperError("cartesian is required."); } //>>includeEnd('debug'); var offset = Cartesian3.subtract(cartesian, box.center, scratchOffset); var halfAxes = box.halfAxes; var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU); var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV); var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW); var uHalf = Cartesian3.magnitude(u); var vHalf = Cartesian3.magnitude(v); var wHalf = Cartesian3.magnitude(w); Cartesian3.normalize(u, u); Cartesian3.normalize(v, v); Cartesian3.normalize(w, w); var pPrime = scratchPPrime; pPrime.x = Cartesian3.dot(offset, u); pPrime.y = Cartesian3.dot(offset, v); pPrime.z = Cartesian3.dot(offset, w); var distanceSquared = 0.0; var d; if (pPrime.x < -uHalf) { d = pPrime.x + uHalf; distanceSquared += d * d; } else if (pPrime.x > uHalf) { d = pPrime.x - uHalf; distanceSquared += d * d; } if (pPrime.y < -vHalf) { d = pPrime.y + vHalf; distanceSquared += d * d; } else if (pPrime.y > vHalf) { d = pPrime.y - vHalf; distanceSquared += d * d; } if (pPrime.z < -wHalf) { d = pPrime.z + wHalf; distanceSquared += d * d; } else if (pPrime.z > wHalf) { d = pPrime.z - wHalf; distanceSquared += d * d; } return distanceSquared; }; var scratchCorner = new Cartesian3(); var scratchToCenter$1 = new Cartesian3(); /** * The distances calculated by the vector from the center of the bounding box to position projected onto direction. *
* 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 box. * * @param {OrientedBoundingBox} box The bounding box to calculate the distance to. * @param {Cartesian3} position The position to calculate the distance from. * @param {Cartesian3} direction The direction from position. * @param {Interval} [result] A Interval to store the nearest and farthest distances. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction. */ OrientedBoundingBox.computePlaneDistances = function ( box, position, direction, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(box)) { throw new DeveloperError("box is required."); } if (!defined(position)) { throw new DeveloperError("position is required."); } if (!defined(direction)) { throw new DeveloperError("direction is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Interval(); } var minDist = Number.POSITIVE_INFINITY; var maxDist = Number.NEGATIVE_INFINITY; var center = box.center; var halfAxes = box.halfAxes; var u = Matrix3.getColumn(halfAxes, 0, scratchCartesianU); var v = Matrix3.getColumn(halfAxes, 1, scratchCartesianV); var w = Matrix3.getColumn(halfAxes, 2, scratchCartesianW); // project first corner var corner = Cartesian3.add(u, v, scratchCorner); Cartesian3.add(corner, w, corner); Cartesian3.add(corner, center, corner); var toCenter = Cartesian3.subtract(corner, position, scratchToCenter$1); var mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project second corner Cartesian3.add(center, u, corner); Cartesian3.add(corner, v, corner); Cartesian3.subtract(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project third corner Cartesian3.add(center, u, corner); Cartesian3.subtract(corner, v, corner); Cartesian3.add(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project fourth corner Cartesian3.add(center, u, corner); Cartesian3.subtract(corner, v, corner); Cartesian3.subtract(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project fifth corner Cartesian3.subtract(center, u, corner); Cartesian3.add(corner, v, corner); Cartesian3.add(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project sixth corner Cartesian3.subtract(center, u, corner); Cartesian3.add(corner, v, corner); Cartesian3.subtract(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project seventh corner Cartesian3.subtract(center, u, corner); Cartesian3.subtract(corner, v, corner); Cartesian3.add(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); // project eighth corner Cartesian3.subtract(center, u, corner); Cartesian3.subtract(corner, v, corner); Cartesian3.subtract(corner, w, corner); Cartesian3.subtract(corner, position, toCenter); mag = Cartesian3.dot(direction, toCenter); minDist = Math.min(mag, minDist); maxDist = Math.max(mag, maxDist); result.start = minDist; result.stop = maxDist; return result; }; var scratchBoundingSphere$3 = new BoundingSphere(); /** * Determines whether or not a bounding box is hidden from view by the occluder. * * @param {OrientedBoundingBox} box The bounding box surrounding the occludee object. * @param {Occluder} occluder The occluder. * @returns {Boolean} true if the box is not visible; otherwise false. */ OrientedBoundingBox.isOccluded = function (box, occluder) { //>>includeStart('debug', pragmas.debug); if (!defined(box)) { throw new DeveloperError("box is required."); } if (!defined(occluder)) { throw new DeveloperError("occluder is required."); } //>>includeEnd('debug'); var sphere = BoundingSphere.fromOrientedBoundingBox( box, scratchBoundingSphere$3 ); return !occluder.isBoundingSphereVisible(sphere); }; /** * Determines which side of a plane the oriented bounding box is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ OrientedBoundingBox.prototype.intersectPlane = function (plane) { return OrientedBoundingBox.intersectPlane(this, plane); }; /** * Computes the estimated distance squared from the closest point on a bounding box to a point. * * @param {Cartesian3} cartesian The point * @returns {Number} The estimated distance squared from the bounding sphere to the point. * * @example * // Sort bounding boxes from back to front * boxes.sort(function(a, b) { * return b.distanceSquaredTo(camera.positionWC) - a.distanceSquaredTo(camera.positionWC); * }); */ OrientedBoundingBox.prototype.distanceSquaredTo = function (cartesian) { return OrientedBoundingBox.distanceSquaredTo(this, cartesian); }; /** * The distances calculated by the vector from the center of the bounding box to position projected onto direction. *
* 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 box. * * @param {Cartesian3} position The position to calculate the distance from. * @param {Cartesian3} direction The direction from position. * @param {Interval} [result] A Interval to store the nearest and farthest distances. * @returns {Interval} The nearest and farthest distances on the bounding box from position in direction. */ OrientedBoundingBox.prototype.computePlaneDistances = function ( position, direction, result ) { return OrientedBoundingBox.computePlaneDistances( this, position, direction, result ); }; /** * Determines whether or not a bounding box is hidden from view by the occluder. * * @param {Occluder} occluder The occluder. * @returns {Boolean} true if the sphere is not visible; otherwise false. */ OrientedBoundingBox.prototype.isOccluded = function (occluder) { return OrientedBoundingBox.isOccluded(this, occluder); }; /** * Compares the provided OrientedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {OrientedBoundingBox} left The first OrientedBoundingBox. * @param {OrientedBoundingBox} right The second OrientedBoundingBox. * @returns {Boolean} true if left and right are equal, false otherwise. */ OrientedBoundingBox.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && Cartesian3.equals(left.center, right.center) && Matrix3.equals(left.halfAxes, right.halfAxes)) ); }; /** * Duplicates this OrientedBoundingBox instance. * * @param {OrientedBoundingBox} [result] The object onto which to store the result. * @returns {OrientedBoundingBox} The modified result parameter or a new OrientedBoundingBox instance if one was not provided. */ OrientedBoundingBox.prototype.clone = function (result) { return OrientedBoundingBox.clone(this, result); }; /** * Compares this OrientedBoundingBox against the provided OrientedBoundingBox componentwise and returns * true if they are equal, false otherwise. * * @param {OrientedBoundingBox} [right] The right hand side OrientedBoundingBox. * @returns {Boolean} true if they are equal, false otherwise. */ OrientedBoundingBox.prototype.equals = function (right) { return OrientedBoundingBox.equals(this, right); }; var RIGHT_SHIFT = 1.0 / 256.0; var LEFT_SHIFT = 256.0; /** * Attribute compression and decompression functions. * * @namespace AttributeCompression * * @private */ var AttributeCompression = {}; /** * Encodes a normalized vector into 2 SNORM values in the range of [0-rangeMax] following the 'oct' encoding. * * Oct encoding is a compact representation of unit length vectors. * The 'oct' encoding is described in "A Survey of Efficient Representations of Independent Unit Vectors", * Cigolle et al 2014: {@link http://jcgt.org/published/0003/02/01/} * * @param {Cartesian3} vector The normalized vector to be compressed into 2 component 'oct' encoding. * @param {Cartesian2} result The 2 component oct-encoded unit length vector. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits. * @returns {Cartesian2} The 2 component oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octDecodeInRange */ AttributeCompression.octEncodeInRange = function (vector, rangeMax, result) { //>>includeStart('debug', pragmas.debug); Check.defined("vector", vector); Check.defined("result", result); var magSquared = Cartesian3.magnitudeSquared(vector); if (Math.abs(magSquared - 1.0) > CesiumMath.EPSILON6) { throw new DeveloperError("vector must be normalized."); } //>>includeEnd('debug'); result.x = vector.x / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z)); result.y = vector.y / (Math.abs(vector.x) + Math.abs(vector.y) + Math.abs(vector.z)); if (vector.z < 0) { var x = result.x; var y = result.y; result.x = (1.0 - Math.abs(y)) * CesiumMath.signNotZero(x); result.y = (1.0 - Math.abs(x)) * CesiumMath.signNotZero(y); } result.x = CesiumMath.toSNorm(result.x, rangeMax); result.y = CesiumMath.toSNorm(result.y, rangeMax); return result; }; /** * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding. * * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding. * @param {Cartesian2} result The 2 byte oct-encoded unit length vector. * @returns {Cartesian2} The 2 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octEncodeInRange * @see AttributeCompression.octDecode */ AttributeCompression.octEncode = function (vector, result) { return AttributeCompression.octEncodeInRange(vector, 255, result); }; var octEncodeScratch = new Cartesian2(); var uint8ForceArray = new Uint8Array(1); function forceUint8(value) { uint8ForceArray[0] = value; return uint8ForceArray[0]; } /** * @param {Cartesian3} vector The normalized vector to be compressed into 4 byte 'oct' encoding. * @param {Cartesian4} result The 4 byte oct-encoded unit length vector. * @returns {Cartesian4} The 4 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. * * @see AttributeCompression.octEncodeInRange * @see AttributeCompression.octDecodeFromCartesian4 */ AttributeCompression.octEncodeToCartesian4 = function (vector, result) { AttributeCompression.octEncodeInRange(vector, 65535, octEncodeScratch); result.x = forceUint8(octEncodeScratch.x * RIGHT_SHIFT); result.y = forceUint8(octEncodeScratch.x); result.z = forceUint8(octEncodeScratch.y * RIGHT_SHIFT); result.w = forceUint8(octEncodeScratch.y); return result; }; /** * Decodes a unit-length vector in 'oct' encoding to a normalized 3-component vector. * * @param {Number} x The x component of the oct-encoded unit length vector. * @param {Number} y The y component of the oct-encoded unit length vector. * @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits. * @param {Cartesian3} result The decoded and normalized vector * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x and y must be unsigned normalized integers between 0 and rangeMax. * * @see AttributeCompression.octEncodeInRange */ AttributeCompression.octDecodeInRange = function (x, y, rangeMax, result) { //>>includeStart('debug', pragmas.debug); Check.defined("result", result); if (x < 0 || x > rangeMax || y < 0 || y > rangeMax) { throw new DeveloperError( "x and y must be unsigned normalized integers between 0 and " + rangeMax ); } //>>includeEnd('debug'); result.x = CesiumMath.fromSNorm(x, rangeMax); result.y = CesiumMath.fromSNorm(y, rangeMax); result.z = 1.0 - (Math.abs(result.x) + Math.abs(result.y)); if (result.z < 0.0) { var oldVX = result.x; result.x = (1.0 - Math.abs(result.y)) * CesiumMath.signNotZero(oldVX); result.y = (1.0 - Math.abs(oldVX)) * CesiumMath.signNotZero(result.y); } return Cartesian3.normalize(result, result); }; /** * Decodes a unit-length vector in 2 byte 'oct' encoding to a normalized 3-component vector. * * @param {Number} x The x component of the oct-encoded unit length vector. * @param {Number} y The y component of the oct-encoded unit length vector. * @param {Cartesian3} result The decoded and normalized vector. * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and 255. * * @see AttributeCompression.octDecodeInRange */ AttributeCompression.octDecode = function (x, y, result) { return AttributeCompression.octDecodeInRange(x, y, 255, result); }; /** * Decodes a unit-length vector in 4 byte 'oct' encoding to a normalized 3-component vector. * * @param {Cartesian4} encoded The oct-encoded unit length vector. * @param {Cartesian3} result The decoded and normalized vector. * @returns {Cartesian3} The decoded and normalized vector. * * @exception {DeveloperError} x, y, z, and w must be unsigned normalized integers between 0 and 255. * * @see AttributeCompression.octDecodeInRange * @see AttributeCompression.octEncodeToCartesian4 */ AttributeCompression.octDecodeFromCartesian4 = function (encoded, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("encoded", encoded); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = encoded.x; var y = encoded.y; var z = encoded.z; var w = encoded.w; //>>includeStart('debug', pragmas.debug); if ( x < 0 || x > 255 || y < 0 || y > 255 || z < 0 || z > 255 || w < 0 || w > 255 ) { throw new DeveloperError( "x, y, z, and w must be unsigned normalized integers between 0 and 255" ); } //>>includeEnd('debug'); var xOct16 = x * LEFT_SHIFT + y; var yOct16 = z * LEFT_SHIFT + w; return AttributeCompression.octDecodeInRange(xOct16, yOct16, 65535, result); }; /** * Packs an oct encoded vector into a single floating-point number. * * @param {Cartesian2} encoded The oct encoded vector. * @returns {Number} The oct encoded vector packed into a single float. * */ AttributeCompression.octPackFloat = function (encoded) { //>>includeStart('debug', pragmas.debug); Check.defined("encoded", encoded); //>>includeEnd('debug'); return 256.0 * encoded.x + encoded.y; }; var scratchEncodeCart2 = new Cartesian2(); /** * Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding and * stores those values in a single float-point number. * * @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding. * @returns {Number} The 2 byte oct-encoded unit length vector. * * @exception {DeveloperError} vector must be normalized. */ AttributeCompression.octEncodeFloat = function (vector) { AttributeCompression.octEncode(vector, scratchEncodeCart2); return AttributeCompression.octPackFloat(scratchEncodeCart2); }; /** * Decodes a unit-length vector in 'oct' encoding packed in a floating-point number to a normalized 3-component vector. * * @param {Number} value The oct-encoded unit length vector stored as a single floating-point number. * @param {Cartesian3} result The decoded and normalized vector * @returns {Cartesian3} The decoded and normalized vector. * */ AttributeCompression.octDecodeFloat = function (value, result) { //>>includeStart('debug', pragmas.debug); Check.defined("value", value); //>>includeEnd('debug'); var temp = value / 256.0; var x = Math.floor(temp); var y = (temp - x) * 256.0; return AttributeCompression.octDecode(x, y, result); }; /** * Encodes three normalized vectors into 6 SNORM values in the range of [0-255] following the 'oct' encoding and * packs those into two floating-point numbers. * * @param {Cartesian3} v1 A normalized vector to be compressed. * @param {Cartesian3} v2 A normalized vector to be compressed. * @param {Cartesian3} v3 A normalized vector to be compressed. * @param {Cartesian2} result The 'oct' encoded vectors packed into two floating-point numbers. * @returns {Cartesian2} The 'oct' encoded vectors packed into two floating-point numbers. * */ AttributeCompression.octPack = function (v1, v2, v3, result) { //>>includeStart('debug', pragmas.debug); Check.defined("v1", v1); Check.defined("v2", v2); Check.defined("v3", v3); Check.defined("result", result); //>>includeEnd('debug'); var encoded1 = AttributeCompression.octEncodeFloat(v1); var encoded2 = AttributeCompression.octEncodeFloat(v2); var encoded3 = AttributeCompression.octEncode(v3, scratchEncodeCart2); result.x = 65536.0 * encoded3.x + encoded1; result.y = 65536.0 * encoded3.y + encoded2; return result; }; /** * Decodes three unit-length vectors in 'oct' encoding packed into a floating-point number to a normalized 3-component vector. * * @param {Cartesian2} packed The three oct-encoded unit length vectors stored as two floating-point number. * @param {Cartesian3} v1 One decoded and normalized vector. * @param {Cartesian3} v2 One decoded and normalized vector. * @param {Cartesian3} v3 One decoded and normalized vector. */ AttributeCompression.octUnpack = function (packed, v1, v2, v3) { //>>includeStart('debug', pragmas.debug); Check.defined("packed", packed); Check.defined("v1", v1); Check.defined("v2", v2); Check.defined("v3", v3); //>>includeEnd('debug'); var temp = packed.x / 65536.0; var x = Math.floor(temp); var encodedFloat1 = (temp - x) * 65536.0; temp = packed.y / 65536.0; var y = Math.floor(temp); var encodedFloat2 = (temp - y) * 65536.0; AttributeCompression.octDecodeFloat(encodedFloat1, v1); AttributeCompression.octDecodeFloat(encodedFloat2, v2); AttributeCompression.octDecode(x, y, v3); }; /** * Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision. * * @param {Cartesian2} textureCoordinates The texture coordinates to compress. Both coordinates must be in the range 0.0-1.0. * @returns {Number} The packed texture coordinates. * */ AttributeCompression.compressTextureCoordinates = function ( textureCoordinates ) { //>>includeStart('debug', pragmas.debug); Check.defined("textureCoordinates", textureCoordinates); //>>includeEnd('debug'); // Move x and y to the range 0-4095; var x = (textureCoordinates.x * 4095.0) | 0; var y = (textureCoordinates.y * 4095.0) | 0; return 4096.0 * x + y; }; /** * Decompresses texture coordinates that were packed into a single float. * * @param {Number} compressed The compressed texture coordinates. * @param {Cartesian2} result The decompressed texture coordinates. * @returns {Cartesian2} The modified result parameter. * */ AttributeCompression.decompressTextureCoordinates = function ( compressed, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("compressed", compressed); Check.defined("result", result); //>>includeEnd('debug'); var temp = compressed / 4096.0; var xZeroTo4095 = Math.floor(temp); result.x = xZeroTo4095 / 4095.0; result.y = (compressed - xZeroTo4095 * 4096) / 4095; return result; }; function zigZagDecode(value) { return (value >> 1) ^ -(value & 1); } /** * Decodes delta and ZigZag encoded vertices. This modifies the buffers in place. * * @param {Uint16Array} uBuffer The buffer view of u values. * @param {Uint16Array} vBuffer The buffer view of v values. * @param {Uint16Array} [heightBuffer] The buffer view of height values. * * @see {@link https://github.com/CesiumGS/quantized-mesh|quantized-mesh-1.0 terrain format} */ AttributeCompression.zigZagDeltaDecode = function ( uBuffer, vBuffer, heightBuffer ) { //>>includeStart('debug', pragmas.debug); Check.defined("uBuffer", uBuffer); Check.defined("vBuffer", vBuffer); Check.typeOf.number.equals( "uBuffer.length", "vBuffer.length", uBuffer.length, vBuffer.length ); if (defined(heightBuffer)) { Check.typeOf.number.equals( "uBuffer.length", "heightBuffer.length", uBuffer.length, heightBuffer.length ); } //>>includeEnd('debug'); var count = uBuffer.length; var u = 0; var v = 0; var height = 0; for (var i = 0; i < count; ++i) { u += zigZagDecode(uBuffer[i]); v += zigZagDecode(vBuffer[i]); uBuffer[i] = u; vBuffer[i] = v; if (defined(heightBuffer)) { height += zigZagDecode(heightBuffer[i]); heightBuffer[i] = height; } } }; /** * Enum containing WebGL Constant values by name. * for use without an active WebGL context, or in cases where certain constants are unavailable using the WebGL context * (For example, in [Safari 9]{@link https://github.com/CesiumGS/cesium/issues/2989}). * * These match the constants from the [WebGL 1.0]{@link https://www.khronos.org/registry/webgl/specs/latest/1.0/} * and [WebGL 2.0]{@link https://www.khronos.org/registry/webgl/specs/latest/2.0/} * specifications. * * @enum {Number} */ var WebGLConstants = { DEPTH_BUFFER_BIT: 0x00000100, STENCIL_BUFFER_BIT: 0x00000400, COLOR_BUFFER_BIT: 0x00004000, POINTS: 0x0000, LINES: 0x0001, LINE_LOOP: 0x0002, LINE_STRIP: 0x0003, TRIANGLES: 0x0004, TRIANGLE_STRIP: 0x0005, TRIANGLE_FAN: 0x0006, ZERO: 0, ONE: 1, SRC_COLOR: 0x0300, ONE_MINUS_SRC_COLOR: 0x0301, SRC_ALPHA: 0x0302, ONE_MINUS_SRC_ALPHA: 0x0303, DST_ALPHA: 0x0304, ONE_MINUS_DST_ALPHA: 0x0305, DST_COLOR: 0x0306, ONE_MINUS_DST_COLOR: 0x0307, SRC_ALPHA_SATURATE: 0x0308, FUNC_ADD: 0x8006, BLEND_EQUATION: 0x8009, BLEND_EQUATION_RGB: 0x8009, // same as BLEND_EQUATION BLEND_EQUATION_ALPHA: 0x883d, FUNC_SUBTRACT: 0x800a, FUNC_REVERSE_SUBTRACT: 0x800b, BLEND_DST_RGB: 0x80c8, BLEND_SRC_RGB: 0x80c9, BLEND_DST_ALPHA: 0x80ca, BLEND_SRC_ALPHA: 0x80cb, CONSTANT_COLOR: 0x8001, ONE_MINUS_CONSTANT_COLOR: 0x8002, CONSTANT_ALPHA: 0x8003, ONE_MINUS_CONSTANT_ALPHA: 0x8004, BLEND_COLOR: 0x8005, ARRAY_BUFFER: 0x8892, ELEMENT_ARRAY_BUFFER: 0x8893, ARRAY_BUFFER_BINDING: 0x8894, ELEMENT_ARRAY_BUFFER_BINDING: 0x8895, STREAM_DRAW: 0x88e0, STATIC_DRAW: 0x88e4, DYNAMIC_DRAW: 0x88e8, BUFFER_SIZE: 0x8764, BUFFER_USAGE: 0x8765, CURRENT_VERTEX_ATTRIB: 0x8626, FRONT: 0x0404, BACK: 0x0405, FRONT_AND_BACK: 0x0408, CULL_FACE: 0x0b44, BLEND: 0x0be2, DITHER: 0x0bd0, STENCIL_TEST: 0x0b90, DEPTH_TEST: 0x0b71, SCISSOR_TEST: 0x0c11, POLYGON_OFFSET_FILL: 0x8037, SAMPLE_ALPHA_TO_COVERAGE: 0x809e, SAMPLE_COVERAGE: 0x80a0, NO_ERROR: 0, INVALID_ENUM: 0x0500, INVALID_VALUE: 0x0501, INVALID_OPERATION: 0x0502, OUT_OF_MEMORY: 0x0505, CW: 0x0900, CCW: 0x0901, LINE_WIDTH: 0x0b21, ALIASED_POINT_SIZE_RANGE: 0x846d, ALIASED_LINE_WIDTH_RANGE: 0x846e, CULL_FACE_MODE: 0x0b45, FRONT_FACE: 0x0b46, DEPTH_RANGE: 0x0b70, DEPTH_WRITEMASK: 0x0b72, DEPTH_CLEAR_VALUE: 0x0b73, DEPTH_FUNC: 0x0b74, STENCIL_CLEAR_VALUE: 0x0b91, STENCIL_FUNC: 0x0b92, STENCIL_FAIL: 0x0b94, STENCIL_PASS_DEPTH_FAIL: 0x0b95, STENCIL_PASS_DEPTH_PASS: 0x0b96, STENCIL_REF: 0x0b97, STENCIL_VALUE_MASK: 0x0b93, STENCIL_WRITEMASK: 0x0b98, STENCIL_BACK_FUNC: 0x8800, STENCIL_BACK_FAIL: 0x8801, STENCIL_BACK_PASS_DEPTH_FAIL: 0x8802, STENCIL_BACK_PASS_DEPTH_PASS: 0x8803, STENCIL_BACK_REF: 0x8ca3, STENCIL_BACK_VALUE_MASK: 0x8ca4, STENCIL_BACK_WRITEMASK: 0x8ca5, VIEWPORT: 0x0ba2, SCISSOR_BOX: 0x0c10, COLOR_CLEAR_VALUE: 0x0c22, COLOR_WRITEMASK: 0x0c23, UNPACK_ALIGNMENT: 0x0cf5, PACK_ALIGNMENT: 0x0d05, MAX_TEXTURE_SIZE: 0x0d33, MAX_VIEWPORT_DIMS: 0x0d3a, SUBPIXEL_BITS: 0x0d50, RED_BITS: 0x0d52, GREEN_BITS: 0x0d53, BLUE_BITS: 0x0d54, ALPHA_BITS: 0x0d55, DEPTH_BITS: 0x0d56, STENCIL_BITS: 0x0d57, POLYGON_OFFSET_UNITS: 0x2a00, POLYGON_OFFSET_FACTOR: 0x8038, TEXTURE_BINDING_2D: 0x8069, SAMPLE_BUFFERS: 0x80a8, SAMPLES: 0x80a9, SAMPLE_COVERAGE_VALUE: 0x80aa, SAMPLE_COVERAGE_INVERT: 0x80ab, COMPRESSED_TEXTURE_FORMATS: 0x86a3, DONT_CARE: 0x1100, FASTEST: 0x1101, NICEST: 0x1102, GENERATE_MIPMAP_HINT: 0x8192, BYTE: 0x1400, UNSIGNED_BYTE: 0x1401, SHORT: 0x1402, UNSIGNED_SHORT: 0x1403, INT: 0x1404, UNSIGNED_INT: 0x1405, FLOAT: 0x1406, DEPTH_COMPONENT: 0x1902, ALPHA: 0x1906, RGB: 0x1907, RGBA: 0x1908, LUMINANCE: 0x1909, LUMINANCE_ALPHA: 0x190a, UNSIGNED_SHORT_4_4_4_4: 0x8033, UNSIGNED_SHORT_5_5_5_1: 0x8034, UNSIGNED_SHORT_5_6_5: 0x8363, FRAGMENT_SHADER: 0x8b30, VERTEX_SHADER: 0x8b31, MAX_VERTEX_ATTRIBS: 0x8869, MAX_VERTEX_UNIFORM_VECTORS: 0x8dfb, MAX_VARYING_VECTORS: 0x8dfc, MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0x8b4d, MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0x8b4c, MAX_TEXTURE_IMAGE_UNITS: 0x8872, MAX_FRAGMENT_UNIFORM_VECTORS: 0x8dfd, SHADER_TYPE: 0x8b4f, DELETE_STATUS: 0x8b80, LINK_STATUS: 0x8b82, VALIDATE_STATUS: 0x8b83, ATTACHED_SHADERS: 0x8b85, ACTIVE_UNIFORMS: 0x8b86, ACTIVE_ATTRIBUTES: 0x8b89, SHADING_LANGUAGE_VERSION: 0x8b8c, CURRENT_PROGRAM: 0x8b8d, NEVER: 0x0200, LESS: 0x0201, EQUAL: 0x0202, LEQUAL: 0x0203, GREATER: 0x0204, NOTEQUAL: 0x0205, GEQUAL: 0x0206, ALWAYS: 0x0207, KEEP: 0x1e00, REPLACE: 0x1e01, INCR: 0x1e02, DECR: 0x1e03, INVERT: 0x150a, INCR_WRAP: 0x8507, DECR_WRAP: 0x8508, VENDOR: 0x1f00, RENDERER: 0x1f01, VERSION: 0x1f02, NEAREST: 0x2600, LINEAR: 0x2601, NEAREST_MIPMAP_NEAREST: 0x2700, LINEAR_MIPMAP_NEAREST: 0x2701, NEAREST_MIPMAP_LINEAR: 0x2702, LINEAR_MIPMAP_LINEAR: 0x2703, TEXTURE_MAG_FILTER: 0x2800, TEXTURE_MIN_FILTER: 0x2801, TEXTURE_WRAP_S: 0x2802, TEXTURE_WRAP_T: 0x2803, TEXTURE_2D: 0x0de1, TEXTURE: 0x1702, TEXTURE_CUBE_MAP: 0x8513, TEXTURE_BINDING_CUBE_MAP: 0x8514, TEXTURE_CUBE_MAP_POSITIVE_X: 0x8515, TEXTURE_CUBE_MAP_NEGATIVE_X: 0x8516, TEXTURE_CUBE_MAP_POSITIVE_Y: 0x8517, TEXTURE_CUBE_MAP_NEGATIVE_Y: 0x8518, TEXTURE_CUBE_MAP_POSITIVE_Z: 0x8519, TEXTURE_CUBE_MAP_NEGATIVE_Z: 0x851a, MAX_CUBE_MAP_TEXTURE_SIZE: 0x851c, TEXTURE0: 0x84c0, TEXTURE1: 0x84c1, TEXTURE2: 0x84c2, TEXTURE3: 0x84c3, TEXTURE4: 0x84c4, TEXTURE5: 0x84c5, TEXTURE6: 0x84c6, TEXTURE7: 0x84c7, TEXTURE8: 0x84c8, TEXTURE9: 0x84c9, TEXTURE10: 0x84ca, TEXTURE11: 0x84cb, TEXTURE12: 0x84cc, TEXTURE13: 0x84cd, TEXTURE14: 0x84ce, TEXTURE15: 0x84cf, TEXTURE16: 0x84d0, TEXTURE17: 0x84d1, TEXTURE18: 0x84d2, TEXTURE19: 0x84d3, TEXTURE20: 0x84d4, TEXTURE21: 0x84d5, TEXTURE22: 0x84d6, TEXTURE23: 0x84d7, TEXTURE24: 0x84d8, TEXTURE25: 0x84d9, TEXTURE26: 0x84da, TEXTURE27: 0x84db, TEXTURE28: 0x84dc, TEXTURE29: 0x84dd, TEXTURE30: 0x84de, TEXTURE31: 0x84df, ACTIVE_TEXTURE: 0x84e0, REPEAT: 0x2901, CLAMP_TO_EDGE: 0x812f, MIRRORED_REPEAT: 0x8370, FLOAT_VEC2: 0x8b50, FLOAT_VEC3: 0x8b51, FLOAT_VEC4: 0x8b52, INT_VEC2: 0x8b53, INT_VEC3: 0x8b54, INT_VEC4: 0x8b55, BOOL: 0x8b56, BOOL_VEC2: 0x8b57, BOOL_VEC3: 0x8b58, BOOL_VEC4: 0x8b59, FLOAT_MAT2: 0x8b5a, FLOAT_MAT3: 0x8b5b, FLOAT_MAT4: 0x8b5c, SAMPLER_2D: 0x8b5e, SAMPLER_CUBE: 0x8b60, VERTEX_ATTRIB_ARRAY_ENABLED: 0x8622, VERTEX_ATTRIB_ARRAY_SIZE: 0x8623, VERTEX_ATTRIB_ARRAY_STRIDE: 0x8624, VERTEX_ATTRIB_ARRAY_TYPE: 0x8625, VERTEX_ATTRIB_ARRAY_NORMALIZED: 0x886a, VERTEX_ATTRIB_ARRAY_POINTER: 0x8645, VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 0x889f, IMPLEMENTATION_COLOR_READ_TYPE: 0x8b9a, IMPLEMENTATION_COLOR_READ_FORMAT: 0x8b9b, COMPILE_STATUS: 0x8b81, LOW_FLOAT: 0x8df0, MEDIUM_FLOAT: 0x8df1, HIGH_FLOAT: 0x8df2, LOW_INT: 0x8df3, MEDIUM_INT: 0x8df4, HIGH_INT: 0x8df5, FRAMEBUFFER: 0x8d40, RENDERBUFFER: 0x8d41, RGBA4: 0x8056, RGB5_A1: 0x8057, RGB565: 0x8d62, DEPTH_COMPONENT16: 0x81a5, STENCIL_INDEX: 0x1901, STENCIL_INDEX8: 0x8d48, DEPTH_STENCIL: 0x84f9, RENDERBUFFER_WIDTH: 0x8d42, RENDERBUFFER_HEIGHT: 0x8d43, RENDERBUFFER_INTERNAL_FORMAT: 0x8d44, RENDERBUFFER_RED_SIZE: 0x8d50, RENDERBUFFER_GREEN_SIZE: 0x8d51, RENDERBUFFER_BLUE_SIZE: 0x8d52, RENDERBUFFER_ALPHA_SIZE: 0x8d53, RENDERBUFFER_DEPTH_SIZE: 0x8d54, RENDERBUFFER_STENCIL_SIZE: 0x8d55, FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 0x8cd0, FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 0x8cd1, FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 0x8cd2, FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 0x8cd3, COLOR_ATTACHMENT0: 0x8ce0, DEPTH_ATTACHMENT: 0x8d00, STENCIL_ATTACHMENT: 0x8d20, DEPTH_STENCIL_ATTACHMENT: 0x821a, NONE: 0, FRAMEBUFFER_COMPLETE: 0x8cd5, FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0x8cd6, FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0x8cd7, FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 0x8cd9, FRAMEBUFFER_UNSUPPORTED: 0x8cdd, FRAMEBUFFER_BINDING: 0x8ca6, RENDERBUFFER_BINDING: 0x8ca7, MAX_RENDERBUFFER_SIZE: 0x84e8, INVALID_FRAMEBUFFER_OPERATION: 0x0506, UNPACK_FLIP_Y_WEBGL: 0x9240, UNPACK_PREMULTIPLY_ALPHA_WEBGL: 0x9241, CONTEXT_LOST_WEBGL: 0x9242, UNPACK_COLORSPACE_CONVERSION_WEBGL: 0x9243, BROWSER_DEFAULT_WEBGL: 0x9244, // WEBGL_compressed_texture_s3tc COMPRESSED_RGB_S3TC_DXT1_EXT: 0x83f0, COMPRESSED_RGBA_S3TC_DXT1_EXT: 0x83f1, COMPRESSED_RGBA_S3TC_DXT3_EXT: 0x83f2, COMPRESSED_RGBA_S3TC_DXT5_EXT: 0x83f3, // WEBGL_compressed_texture_pvrtc COMPRESSED_RGB_PVRTC_4BPPV1_IMG: 0x8c00, COMPRESSED_RGB_PVRTC_2BPPV1_IMG: 0x8c01, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: 0x8c02, COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: 0x8c03, // WEBGL_compressed_texture_etc1 COMPRESSED_RGB_ETC1_WEBGL: 0x8d64, // EXT_color_buffer_half_float HALF_FLOAT_OES: 0x8d61, // Desktop OpenGL DOUBLE: 0x140a, // WebGL 2 READ_BUFFER: 0x0c02, UNPACK_ROW_LENGTH: 0x0cf2, UNPACK_SKIP_ROWS: 0x0cf3, UNPACK_SKIP_PIXELS: 0x0cf4, PACK_ROW_LENGTH: 0x0d02, PACK_SKIP_ROWS: 0x0d03, PACK_SKIP_PIXELS: 0x0d04, COLOR: 0x1800, DEPTH: 0x1801, STENCIL: 0x1802, RED: 0x1903, RGB8: 0x8051, RGBA8: 0x8058, RGB10_A2: 0x8059, TEXTURE_BINDING_3D: 0x806a, UNPACK_SKIP_IMAGES: 0x806d, UNPACK_IMAGE_HEIGHT: 0x806e, TEXTURE_3D: 0x806f, TEXTURE_WRAP_R: 0x8072, MAX_3D_TEXTURE_SIZE: 0x8073, UNSIGNED_INT_2_10_10_10_REV: 0x8368, MAX_ELEMENTS_VERTICES: 0x80e8, MAX_ELEMENTS_INDICES: 0x80e9, TEXTURE_MIN_LOD: 0x813a, TEXTURE_MAX_LOD: 0x813b, TEXTURE_BASE_LEVEL: 0x813c, TEXTURE_MAX_LEVEL: 0x813d, MIN: 0x8007, MAX: 0x8008, DEPTH_COMPONENT24: 0x81a6, MAX_TEXTURE_LOD_BIAS: 0x84fd, TEXTURE_COMPARE_MODE: 0x884c, TEXTURE_COMPARE_FUNC: 0x884d, CURRENT_QUERY: 0x8865, QUERY_RESULT: 0x8866, QUERY_RESULT_AVAILABLE: 0x8867, STREAM_READ: 0x88e1, STREAM_COPY: 0x88e2, STATIC_READ: 0x88e5, STATIC_COPY: 0x88e6, DYNAMIC_READ: 0x88e9, DYNAMIC_COPY: 0x88ea, MAX_DRAW_BUFFERS: 0x8824, DRAW_BUFFER0: 0x8825, DRAW_BUFFER1: 0x8826, DRAW_BUFFER2: 0x8827, DRAW_BUFFER3: 0x8828, DRAW_BUFFER4: 0x8829, DRAW_BUFFER5: 0x882a, DRAW_BUFFER6: 0x882b, DRAW_BUFFER7: 0x882c, DRAW_BUFFER8: 0x882d, DRAW_BUFFER9: 0x882e, DRAW_BUFFER10: 0x882f, DRAW_BUFFER11: 0x8830, DRAW_BUFFER12: 0x8831, DRAW_BUFFER13: 0x8832, DRAW_BUFFER14: 0x8833, DRAW_BUFFER15: 0x8834, MAX_FRAGMENT_UNIFORM_COMPONENTS: 0x8b49, MAX_VERTEX_UNIFORM_COMPONENTS: 0x8b4a, SAMPLER_3D: 0x8b5f, SAMPLER_2D_SHADOW: 0x8b62, FRAGMENT_SHADER_DERIVATIVE_HINT: 0x8b8b, PIXEL_PACK_BUFFER: 0x88eb, PIXEL_UNPACK_BUFFER: 0x88ec, PIXEL_PACK_BUFFER_BINDING: 0x88ed, PIXEL_UNPACK_BUFFER_BINDING: 0x88ef, FLOAT_MAT2x3: 0x8b65, FLOAT_MAT2x4: 0x8b66, FLOAT_MAT3x2: 0x8b67, FLOAT_MAT3x4: 0x8b68, FLOAT_MAT4x2: 0x8b69, FLOAT_MAT4x3: 0x8b6a, SRGB: 0x8c40, SRGB8: 0x8c41, SRGB8_ALPHA8: 0x8c43, COMPARE_REF_TO_TEXTURE: 0x884e, RGBA32F: 0x8814, RGB32F: 0x8815, RGBA16F: 0x881a, RGB16F: 0x881b, VERTEX_ATTRIB_ARRAY_INTEGER: 0x88fd, MAX_ARRAY_TEXTURE_LAYERS: 0x88ff, MIN_PROGRAM_TEXEL_OFFSET: 0x8904, MAX_PROGRAM_TEXEL_OFFSET: 0x8905, MAX_VARYING_COMPONENTS: 0x8b4b, TEXTURE_2D_ARRAY: 0x8c1a, TEXTURE_BINDING_2D_ARRAY: 0x8c1d, R11F_G11F_B10F: 0x8c3a, UNSIGNED_INT_10F_11F_11F_REV: 0x8c3b, RGB9_E5: 0x8c3d, UNSIGNED_INT_5_9_9_9_REV: 0x8c3e, TRANSFORM_FEEDBACK_BUFFER_MODE: 0x8c7f, MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: 0x8c80, TRANSFORM_FEEDBACK_VARYINGS: 0x8c83, TRANSFORM_FEEDBACK_BUFFER_START: 0x8c84, TRANSFORM_FEEDBACK_BUFFER_SIZE: 0x8c85, TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 0x8c88, RASTERIZER_DISCARD: 0x8c89, MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: 0x8c8a, MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 0x8c8b, INTERLEAVED_ATTRIBS: 0x8c8c, SEPARATE_ATTRIBS: 0x8c8d, TRANSFORM_FEEDBACK_BUFFER: 0x8c8e, TRANSFORM_FEEDBACK_BUFFER_BINDING: 0x8c8f, RGBA32UI: 0x8d70, RGB32UI: 0x8d71, RGBA16UI: 0x8d76, RGB16UI: 0x8d77, RGBA8UI: 0x8d7c, RGB8UI: 0x8d7d, RGBA32I: 0x8d82, RGB32I: 0x8d83, RGBA16I: 0x8d88, RGB16I: 0x8d89, RGBA8I: 0x8d8e, RGB8I: 0x8d8f, RED_INTEGER: 0x8d94, RGB_INTEGER: 0x8d98, RGBA_INTEGER: 0x8d99, SAMPLER_2D_ARRAY: 0x8dc1, SAMPLER_2D_ARRAY_SHADOW: 0x8dc4, SAMPLER_CUBE_SHADOW: 0x8dc5, UNSIGNED_INT_VEC2: 0x8dc6, UNSIGNED_INT_VEC3: 0x8dc7, UNSIGNED_INT_VEC4: 0x8dc8, INT_SAMPLER_2D: 0x8dca, INT_SAMPLER_3D: 0x8dcb, INT_SAMPLER_CUBE: 0x8dcc, INT_SAMPLER_2D_ARRAY: 0x8dcf, UNSIGNED_INT_SAMPLER_2D: 0x8dd2, UNSIGNED_INT_SAMPLER_3D: 0x8dd3, UNSIGNED_INT_SAMPLER_CUBE: 0x8dd4, UNSIGNED_INT_SAMPLER_2D_ARRAY: 0x8dd7, DEPTH_COMPONENT32F: 0x8cac, DEPTH32F_STENCIL8: 0x8cad, FLOAT_32_UNSIGNED_INT_24_8_REV: 0x8dad, FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: 0x8210, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: 0x8211, FRAMEBUFFER_ATTACHMENT_RED_SIZE: 0x8212, FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: 0x8213, FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: 0x8214, FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: 0x8215, FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: 0x8216, FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: 0x8217, FRAMEBUFFER_DEFAULT: 0x8218, UNSIGNED_INT_24_8: 0x84fa, DEPTH24_STENCIL8: 0x88f0, UNSIGNED_NORMALIZED: 0x8c17, DRAW_FRAMEBUFFER_BINDING: 0x8ca6, // Same as FRAMEBUFFER_BINDING READ_FRAMEBUFFER: 0x8ca8, DRAW_FRAMEBUFFER: 0x8ca9, READ_FRAMEBUFFER_BINDING: 0x8caa, RENDERBUFFER_SAMPLES: 0x8cab, FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: 0x8cd4, MAX_COLOR_ATTACHMENTS: 0x8cdf, COLOR_ATTACHMENT1: 0x8ce1, COLOR_ATTACHMENT2: 0x8ce2, COLOR_ATTACHMENT3: 0x8ce3, COLOR_ATTACHMENT4: 0x8ce4, COLOR_ATTACHMENT5: 0x8ce5, COLOR_ATTACHMENT6: 0x8ce6, COLOR_ATTACHMENT7: 0x8ce7, COLOR_ATTACHMENT8: 0x8ce8, COLOR_ATTACHMENT9: 0x8ce9, COLOR_ATTACHMENT10: 0x8cea, COLOR_ATTACHMENT11: 0x8ceb, COLOR_ATTACHMENT12: 0x8cec, COLOR_ATTACHMENT13: 0x8ced, COLOR_ATTACHMENT14: 0x8cee, COLOR_ATTACHMENT15: 0x8cef, FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 0x8d56, MAX_SAMPLES: 0x8d57, HALF_FLOAT: 0x140b, RG: 0x8227, RG_INTEGER: 0x8228, R8: 0x8229, RG8: 0x822b, R16F: 0x822d, R32F: 0x822e, RG16F: 0x822f, RG32F: 0x8230, R8I: 0x8231, R8UI: 0x8232, R16I: 0x8233, R16UI: 0x8234, R32I: 0x8235, R32UI: 0x8236, RG8I: 0x8237, RG8UI: 0x8238, RG16I: 0x8239, RG16UI: 0x823a, RG32I: 0x823b, RG32UI: 0x823c, VERTEX_ARRAY_BINDING: 0x85b5, R8_SNORM: 0x8f94, RG8_SNORM: 0x8f95, RGB8_SNORM: 0x8f96, RGBA8_SNORM: 0x8f97, SIGNED_NORMALIZED: 0x8f9c, COPY_READ_BUFFER: 0x8f36, COPY_WRITE_BUFFER: 0x8f37, COPY_READ_BUFFER_BINDING: 0x8f36, // Same as COPY_READ_BUFFER COPY_WRITE_BUFFER_BINDING: 0x8f37, // Same as COPY_WRITE_BUFFER UNIFORM_BUFFER: 0x8a11, UNIFORM_BUFFER_BINDING: 0x8a28, UNIFORM_BUFFER_START: 0x8a29, UNIFORM_BUFFER_SIZE: 0x8a2a, MAX_VERTEX_UNIFORM_BLOCKS: 0x8a2b, MAX_FRAGMENT_UNIFORM_BLOCKS: 0x8a2d, MAX_COMBINED_UNIFORM_BLOCKS: 0x8a2e, MAX_UNIFORM_BUFFER_BINDINGS: 0x8a2f, MAX_UNIFORM_BLOCK_SIZE: 0x8a30, MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: 0x8a31, MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: 0x8a33, UNIFORM_BUFFER_OFFSET_ALIGNMENT: 0x8a34, ACTIVE_UNIFORM_BLOCKS: 0x8a36, UNIFORM_TYPE: 0x8a37, UNIFORM_SIZE: 0x8a38, UNIFORM_BLOCK_INDEX: 0x8a3a, UNIFORM_OFFSET: 0x8a3b, UNIFORM_ARRAY_STRIDE: 0x8a3c, UNIFORM_MATRIX_STRIDE: 0x8a3d, UNIFORM_IS_ROW_MAJOR: 0x8a3e, UNIFORM_BLOCK_BINDING: 0x8a3f, UNIFORM_BLOCK_DATA_SIZE: 0x8a40, UNIFORM_BLOCK_ACTIVE_UNIFORMS: 0x8a42, UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: 0x8a43, UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: 0x8a44, UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: 0x8a46, INVALID_INDEX: 0xffffffff, MAX_VERTEX_OUTPUT_COMPONENTS: 0x9122, MAX_FRAGMENT_INPUT_COMPONENTS: 0x9125, MAX_SERVER_WAIT_TIMEOUT: 0x9111, OBJECT_TYPE: 0x9112, SYNC_CONDITION: 0x9113, SYNC_STATUS: 0x9114, SYNC_FLAGS: 0x9115, SYNC_FENCE: 0x9116, SYNC_GPU_COMMANDS_COMPLETE: 0x9117, UNSIGNALED: 0x9118, SIGNALED: 0x9119, ALREADY_SIGNALED: 0x911a, TIMEOUT_EXPIRED: 0x911b, CONDITION_SATISFIED: 0x911c, WAIT_FAILED: 0x911d, SYNC_FLUSH_COMMANDS_BIT: 0x00000001, VERTEX_ATTRIB_ARRAY_DIVISOR: 0x88fe, ANY_SAMPLES_PASSED: 0x8c2f, ANY_SAMPLES_PASSED_CONSERVATIVE: 0x8d6a, SAMPLER_BINDING: 0x8919, RGB10_A2UI: 0x906f, INT_2_10_10_10_REV: 0x8d9f, TRANSFORM_FEEDBACK: 0x8e22, TRANSFORM_FEEDBACK_PAUSED: 0x8e23, TRANSFORM_FEEDBACK_ACTIVE: 0x8e24, TRANSFORM_FEEDBACK_BINDING: 0x8e25, COMPRESSED_R11_EAC: 0x9270, COMPRESSED_SIGNED_R11_EAC: 0x9271, COMPRESSED_RG11_EAC: 0x9272, COMPRESSED_SIGNED_RG11_EAC: 0x9273, COMPRESSED_RGB8_ETC2: 0x9274, COMPRESSED_SRGB8_ETC2: 0x9275, COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9276, COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9277, COMPRESSED_RGBA8_ETC2_EAC: 0x9278, COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: 0x9279, TEXTURE_IMMUTABLE_FORMAT: 0x912f, MAX_ELEMENT_INDEX: 0x8d6b, TEXTURE_IMMUTABLE_LEVELS: 0x82df, // Extensions MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84ff, }; var WebGLConstants$1 = Object.freeze(WebGLConstants); /** * WebGL component datatypes. Components are intrinsics, * which form attributes, which form vertices. * * @enum {Number} */ var ComponentDatatype = { /** * 8-bit signed byte corresponding to gl.BYTE and the type * of an element in Int8Array. * * @type {Number} * @constant */ BYTE: WebGLConstants$1.BYTE, /** * 8-bit unsigned byte corresponding to UNSIGNED_BYTE and the type * of an element in Uint8Array. * * @type {Number} * @constant */ UNSIGNED_BYTE: WebGLConstants$1.UNSIGNED_BYTE, /** * 16-bit signed short corresponding to SHORT and the type * of an element in Int16Array. * * @type {Number} * @constant */ SHORT: WebGLConstants$1.SHORT, /** * 16-bit unsigned short corresponding to UNSIGNED_SHORT and the type * of an element in Uint16Array. * * @type {Number} * @constant */ UNSIGNED_SHORT: WebGLConstants$1.UNSIGNED_SHORT, /** * 32-bit signed int corresponding to INT and the type * of an element in Int32Array. * * @memberOf ComponentDatatype * * @type {Number} * @constant */ INT: WebGLConstants$1.INT, /** * 32-bit unsigned int corresponding to UNSIGNED_INT and the type * of an element in Uint32Array. * * @memberOf ComponentDatatype * * @type {Number} * @constant */ UNSIGNED_INT: WebGLConstants$1.UNSIGNED_INT, /** * 32-bit floating-point corresponding to FLOAT and the type * of an element in Float32Array. * * @type {Number} * @constant */ FLOAT: WebGLConstants$1.FLOAT, /** * 64-bit floating-point corresponding to gl.DOUBLE (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 Float64Array. * * @memberOf ComponentDatatype * * @type {Number} * @constant * @default 0x140A */ DOUBLE: WebGLConstants$1.DOUBLE, }; /** * Returns the size, in bytes, of the corresponding datatype. * * @param {ComponentDatatype} componentDatatype The component datatype to get the size of. * @returns {Number} The size in bytes. * * @exception {DeveloperError} componentDatatype is not a valid value. * * @example * // Returns Int8Array.BYTES_PER_ELEMENT * var size = Cesium.ComponentDatatype.getSizeInBytes(Cesium.ComponentDatatype.BYTE); */ ComponentDatatype.getSizeInBytes = function (componentDatatype) { //>>includeStart('debug', pragmas.debug); if (!defined(componentDatatype)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); switch (componentDatatype) { case ComponentDatatype.BYTE: return Int8Array.BYTES_PER_ELEMENT; case ComponentDatatype.UNSIGNED_BYTE: return Uint8Array.BYTES_PER_ELEMENT; case ComponentDatatype.SHORT: return Int16Array.BYTES_PER_ELEMENT; case ComponentDatatype.UNSIGNED_SHORT: return Uint16Array.BYTES_PER_ELEMENT; case ComponentDatatype.INT: return Int32Array.BYTES_PER_ELEMENT; case ComponentDatatype.UNSIGNED_INT: return Uint32Array.BYTES_PER_ELEMENT; case ComponentDatatype.FLOAT: return Float32Array.BYTES_PER_ELEMENT; case ComponentDatatype.DOUBLE: return Float64Array.BYTES_PER_ELEMENT; //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError("componentDatatype is not a valid value."); //>>includeEnd('debug'); } }; /** * Gets the {@link ComponentDatatype} for the provided TypedArray instance. * * @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} array The typed array. * @returns {ComponentDatatype} The ComponentDatatype for the provided array, or undefined if the array is not a TypedArray. */ ComponentDatatype.fromTypedArray = function (array) { if (array instanceof Int8Array) { return ComponentDatatype.BYTE; } if (array instanceof Uint8Array) { return ComponentDatatype.UNSIGNED_BYTE; } if (array instanceof Int16Array) { return ComponentDatatype.SHORT; } if (array instanceof Uint16Array) { return ComponentDatatype.UNSIGNED_SHORT; } if (array instanceof Int32Array) { return ComponentDatatype.INT; } if (array instanceof Uint32Array) { return ComponentDatatype.UNSIGNED_INT; } if (array instanceof Float32Array) { return ComponentDatatype.FLOAT; } if (array instanceof Float64Array) { return ComponentDatatype.DOUBLE; } }; /** * Validates that the provided component datatype is a valid {@link ComponentDatatype} * * @param {ComponentDatatype} componentDatatype The component datatype to validate. * @returns {Boolean} true if the provided component datatype is a valid value; otherwise, false. * * @example * if (!Cesium.ComponentDatatype.validate(componentDatatype)) { * throw new Cesium.DeveloperError('componentDatatype must be a valid value.'); * } */ ComponentDatatype.validate = function (componentDatatype) { return ( defined(componentDatatype) && (componentDatatype === ComponentDatatype.BYTE || componentDatatype === ComponentDatatype.UNSIGNED_BYTE || componentDatatype === ComponentDatatype.SHORT || componentDatatype === ComponentDatatype.UNSIGNED_SHORT || componentDatatype === ComponentDatatype.INT || componentDatatype === ComponentDatatype.UNSIGNED_INT || componentDatatype === ComponentDatatype.FLOAT || componentDatatype === ComponentDatatype.DOUBLE) ); }; /** * Creates a typed array corresponding to component data type. * * @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|Int32Array|Uint32Array|Float32Array|Float64Array} A typed array. * * @exception {DeveloperError} componentDatatype is not a valid value. * * @example * // creates a Float32Array with length of 100 * var typedArray = Cesium.ComponentDatatype.createTypedArray(Cesium.ComponentDatatype.FLOAT, 100); */ ComponentDatatype.createTypedArray = function ( componentDatatype, valuesOrLength ) { //>>includeStart('debug', pragmas.debug); if (!defined(componentDatatype)) { throw new DeveloperError("componentDatatype is required."); } if (!defined(valuesOrLength)) { throw new DeveloperError("valuesOrLength is required."); } //>>includeEnd('debug'); switch (componentDatatype) { case ComponentDatatype.BYTE: return new Int8Array(valuesOrLength); case ComponentDatatype.UNSIGNED_BYTE: return new Uint8Array(valuesOrLength); case ComponentDatatype.SHORT: return new Int16Array(valuesOrLength); case ComponentDatatype.UNSIGNED_SHORT: return new Uint16Array(valuesOrLength); case ComponentDatatype.INT: return new Int32Array(valuesOrLength); case ComponentDatatype.UNSIGNED_INT: return new Uint32Array(valuesOrLength); case ComponentDatatype.FLOAT: return new Float32Array(valuesOrLength); case ComponentDatatype.DOUBLE: return new Float64Array(valuesOrLength); //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError("componentDatatype is not a valid value."); //>>includeEnd('debug'); } }; /** * Creates a typed view of an array of bytes. * * @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|Int32Array|Uint32Array|Float32Array|Float64Array} A typed array view of the buffer. * * @exception {DeveloperError} componentDatatype is not a valid value. */ ComponentDatatype.createArrayBufferView = function ( componentDatatype, buffer, byteOffset, length ) { //>>includeStart('debug', pragmas.debug); if (!defined(componentDatatype)) { throw new DeveloperError("componentDatatype is required."); } if (!defined(buffer)) { throw new DeveloperError("buffer is required."); } //>>includeEnd('debug'); byteOffset = defaultValue(byteOffset, 0); length = defaultValue( length, (buffer.byteLength - byteOffset) / ComponentDatatype.getSizeInBytes(componentDatatype) ); switch (componentDatatype) { case ComponentDatatype.BYTE: return new Int8Array(buffer, byteOffset, length); case ComponentDatatype.UNSIGNED_BYTE: return new Uint8Array(buffer, byteOffset, length); case ComponentDatatype.SHORT: return new Int16Array(buffer, byteOffset, length); case ComponentDatatype.UNSIGNED_SHORT: return new Uint16Array(buffer, byteOffset, length); case ComponentDatatype.INT: return new Int32Array(buffer, byteOffset, length); case ComponentDatatype.UNSIGNED_INT: return new Uint32Array(buffer, byteOffset, length); case ComponentDatatype.FLOAT: return new Float32Array(buffer, byteOffset, length); case ComponentDatatype.DOUBLE: return new Float64Array(buffer, byteOffset, length); //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError("componentDatatype is not a valid value."); //>>includeEnd('debug'); } }; /** * Get the ComponentDatatype from its name. * * @param {String} name The name of the ComponentDatatype. * @returns {ComponentDatatype} The ComponentDatatype. * * @exception {DeveloperError} name is not a valid value. */ ComponentDatatype.fromName = function (name) { switch (name) { case "BYTE": return ComponentDatatype.BYTE; case "UNSIGNED_BYTE": return ComponentDatatype.UNSIGNED_BYTE; case "SHORT": return ComponentDatatype.SHORT; case "UNSIGNED_SHORT": return ComponentDatatype.UNSIGNED_SHORT; case "INT": return ComponentDatatype.INT; case "UNSIGNED_INT": return ComponentDatatype.UNSIGNED_INT; case "FLOAT": return ComponentDatatype.FLOAT; case "DOUBLE": return ComponentDatatype.DOUBLE; //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError("name is not a valid value."); //>>includeEnd('debug'); } }; var ComponentDatatype$1 = Object.freeze(ComponentDatatype); /** * This enumerated type is used to determine how the vertices of the terrain mesh are compressed. * * @enum {Number} * * @private */ var TerrainQuantization = { /** * The vertices are not compressed. * * @type {Number} * @constant */ NONE: 0, /** * The vertices are compressed to 12 bits. * * @type {Number} * @constant */ BITS12: 1, }; var TerrainQuantization$1 = Object.freeze(TerrainQuantization); var cartesian3Scratch$4 = new Cartesian3(); var cartesian3DimScratch = new Cartesian3(); var cartesian2Scratch = new Cartesian2(); var matrix4Scratch$1 = new Matrix4(); var matrix4Scratch2 = new Matrix4(); var SHIFT_LEFT_12 = Math.pow(2.0, 12.0); /** * Data used to quantize and pack the terrain mesh. The position can be unpacked for picking and all attributes * are unpacked in the vertex shader. * * @alias TerrainEncoding * @constructor * * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center. * @param {Number} minimumHeight The minimum height. * @param {Number} maximumHeight The maximum height. * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh. * @param {Boolean} hasVertexNormals If the mesh has vertex normals. * @param {Boolean} [hasWebMercatorT=false] true if the terrain data includes a Web Mercator texture coordinate; otherwise, false. * * @private */ function TerrainEncoding( axisAlignedBoundingBox, minimumHeight, maximumHeight, fromENU, hasVertexNormals, hasWebMercatorT ) { var quantization = TerrainQuantization$1.NONE; var center; var toENU; var matrix; if ( defined(axisAlignedBoundingBox) && defined(minimumHeight) && defined(maximumHeight) && defined(fromENU) ) { var minimum = axisAlignedBoundingBox.minimum; var maximum = axisAlignedBoundingBox.maximum; var dimensions = Cartesian3.subtract( maximum, minimum, cartesian3DimScratch ); var hDim = maximumHeight - minimumHeight; var maxDim = Math.max(Cartesian3.maximumComponent(dimensions), hDim); if (maxDim < SHIFT_LEFT_12 - 1.0) { quantization = TerrainQuantization$1.BITS12; } else { quantization = TerrainQuantization$1.NONE; } center = axisAlignedBoundingBox.center; toENU = Matrix4.inverseTransformation(fromENU, new Matrix4()); var translation = Cartesian3.negate(minimum, cartesian3Scratch$4); Matrix4.multiply( Matrix4.fromTranslation(translation, matrix4Scratch$1), toENU, toENU ); var scale = cartesian3Scratch$4; scale.x = 1.0 / dimensions.x; scale.y = 1.0 / dimensions.y; scale.z = 1.0 / dimensions.z; Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch$1), toENU, toENU); matrix = Matrix4.clone(fromENU); Matrix4.setTranslation(matrix, Cartesian3.ZERO, matrix); fromENU = Matrix4.clone(fromENU, new Matrix4()); var translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch$1); var scaleMatrix = Matrix4.fromScale(dimensions, matrix4Scratch2); var st = Matrix4.multiply(translationMatrix, scaleMatrix, matrix4Scratch$1); Matrix4.multiply(fromENU, st, fromENU); Matrix4.multiply(matrix, st, matrix); } /** * How the vertices of the mesh were compressed. * @type {TerrainQuantization} */ this.quantization = quantization; /** * The minimum height of the tile including the skirts. * @type {Number} */ this.minimumHeight = minimumHeight; /** * The maximum height of the tile. * @type {Number} */ this.maximumHeight = maximumHeight; /** * The center of the tile. * @type {Cartesian3} */ this.center = center; /** * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales * it so each component is in the [0, 1] range. * @type {Matrix4} */ this.toScaledENU = toENU; /** * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame * @type {Matrix4} */ this.fromScaledENU = fromENU; /** * The matrix used to decompress the terrain vertices in the shader for RTE rendering. * @type {Matrix4} */ this.matrix = matrix; /** * The terrain mesh contains normals. * @type {Boolean} */ this.hasVertexNormals = hasVertexNormals; /** * The terrain mesh contains a vertical texture coordinate following the Web Mercator projection. * @type {Boolean} */ this.hasWebMercatorT = defaultValue(hasWebMercatorT, false); } TerrainEncoding.prototype.encode = function ( vertexBuffer, bufferIndex, position, uv, height, normalToPack, webMercatorT ) { var u = uv.x; var v = uv.y; if (this.quantization === TerrainQuantization$1.BITS12) { position = Matrix4.multiplyByPoint( this.toScaledENU, position, cartesian3Scratch$4 ); position.x = CesiumMath.clamp(position.x, 0.0, 1.0); position.y = CesiumMath.clamp(position.y, 0.0, 1.0); position.z = CesiumMath.clamp(position.z, 0.0, 1.0); var hDim = this.maximumHeight - this.minimumHeight; var h = CesiumMath.clamp((height - this.minimumHeight) / hDim, 0.0, 1.0); Cartesian2.fromElements(position.x, position.y, cartesian2Scratch); var compressed0 = AttributeCompression.compressTextureCoordinates( cartesian2Scratch ); Cartesian2.fromElements(position.z, h, cartesian2Scratch); var compressed1 = AttributeCompression.compressTextureCoordinates( cartesian2Scratch ); Cartesian2.fromElements(u, v, cartesian2Scratch); var compressed2 = AttributeCompression.compressTextureCoordinates( cartesian2Scratch ); vertexBuffer[bufferIndex++] = compressed0; vertexBuffer[bufferIndex++] = compressed1; vertexBuffer[bufferIndex++] = compressed2; if (this.hasWebMercatorT) { Cartesian2.fromElements(webMercatorT, 0.0, cartesian2Scratch); var compressed3 = AttributeCompression.compressTextureCoordinates( cartesian2Scratch ); vertexBuffer[bufferIndex++] = compressed3; } } else { Cartesian3.subtract(position, this.center, cartesian3Scratch$4); vertexBuffer[bufferIndex++] = cartesian3Scratch$4.x; vertexBuffer[bufferIndex++] = cartesian3Scratch$4.y; vertexBuffer[bufferIndex++] = cartesian3Scratch$4.z; vertexBuffer[bufferIndex++] = height; vertexBuffer[bufferIndex++] = u; vertexBuffer[bufferIndex++] = v; if (this.hasWebMercatorT) { vertexBuffer[bufferIndex++] = webMercatorT; } } if (this.hasVertexNormals) { vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat( normalToPack ); } return bufferIndex; }; TerrainEncoding.prototype.decodePosition = function (buffer, index, result) { if (!defined(result)) { result = new Cartesian3(); } index *= this.getStride(); if (this.quantization === TerrainQuantization$1.BITS12) { var xy = AttributeCompression.decompressTextureCoordinates( buffer[index], cartesian2Scratch ); result.x = xy.x; result.y = xy.y; var zh = AttributeCompression.decompressTextureCoordinates( buffer[index + 1], cartesian2Scratch ); result.z = zh.x; return Matrix4.multiplyByPoint(this.fromScaledENU, result, result); } result.x = buffer[index]; result.y = buffer[index + 1]; result.z = buffer[index + 2]; return Cartesian3.add(result, this.center, result); }; TerrainEncoding.prototype.decodeTextureCoordinates = function ( buffer, index, result ) { if (!defined(result)) { result = new Cartesian2(); } index *= this.getStride(); if (this.quantization === TerrainQuantization$1.BITS12) { return AttributeCompression.decompressTextureCoordinates( buffer[index + 2], result ); } return Cartesian2.fromElements(buffer[index + 4], buffer[index + 5], result); }; TerrainEncoding.prototype.decodeHeight = function (buffer, index) { index *= this.getStride(); if (this.quantization === TerrainQuantization$1.BITS12) { var zh = AttributeCompression.decompressTextureCoordinates( buffer[index + 1], cartesian2Scratch ); return ( zh.y * (this.maximumHeight - this.minimumHeight) + this.minimumHeight ); } return buffer[index + 3]; }; TerrainEncoding.prototype.decodeWebMercatorT = function (buffer, index) { index *= this.getStride(); if (this.quantization === TerrainQuantization$1.BITS12) { return AttributeCompression.decompressTextureCoordinates( buffer[index + 3], cartesian2Scratch ).x; } return buffer[index + 6]; }; TerrainEncoding.prototype.getOctEncodedNormal = function ( buffer, index, result ) { var stride = this.getStride(); index = (index + 1) * stride - 1; var temp = buffer[index] / 256.0; var x = Math.floor(temp); var y = (temp - x) * 256.0; return Cartesian2.fromElements(x, y, result); }; TerrainEncoding.prototype.getStride = function () { var vertexStride; switch (this.quantization) { case TerrainQuantization$1.BITS12: vertexStride = 3; break; default: vertexStride = 6; } if (this.hasWebMercatorT) { ++vertexStride; } if (this.hasVertexNormals) { ++vertexStride; } return vertexStride; }; var attributesNone = { position3DAndHeight: 0, textureCoordAndEncodedNormals: 1, }; var attributes = { compressed0: 0, compressed1: 1, }; TerrainEncoding.prototype.getAttributes = function (buffer) { var datatype = ComponentDatatype$1.FLOAT; var sizeInBytes = ComponentDatatype$1.getSizeInBytes(datatype); var stride; if (this.quantization === TerrainQuantization$1.NONE) { var position3DAndHeightLength = 4; var numTexCoordComponents = 2; if (this.hasWebMercatorT) { ++numTexCoordComponents; } if (this.hasVertexNormals) { ++numTexCoordComponents; } stride = (position3DAndHeightLength + numTexCoordComponents) * sizeInBytes; return [ { index: attributesNone.position3DAndHeight, vertexBuffer: buffer, componentDatatype: datatype, componentsPerAttribute: position3DAndHeightLength, offsetInBytes: 0, strideInBytes: stride, }, { index: attributesNone.textureCoordAndEncodedNormals, vertexBuffer: buffer, componentDatatype: datatype, componentsPerAttribute: numTexCoordComponents, offsetInBytes: position3DAndHeightLength * sizeInBytes, strideInBytes: stride, }, ]; } var numCompressed0 = 3; var numCompressed1 = 0; if (this.hasWebMercatorT || this.hasVertexNormals) { ++numCompressed0; } if (this.hasWebMercatorT && this.hasVertexNormals) { ++numCompressed1; stride = (numCompressed0 + numCompressed1) * sizeInBytes; return [ { index: attributes.compressed0, vertexBuffer: buffer, componentDatatype: datatype, componentsPerAttribute: numCompressed0, offsetInBytes: 0, strideInBytes: stride, }, { index: attributes.compressed1, vertexBuffer: buffer, componentDatatype: datatype, componentsPerAttribute: numCompressed1, offsetInBytes: numCompressed0 * sizeInBytes, strideInBytes: stride, }, ]; } return [ { index: attributes.compressed0, vertexBuffer: buffer, componentDatatype: datatype, componentsPerAttribute: numCompressed0, }, ]; }; TerrainEncoding.prototype.getAttributeLocations = function () { if (this.quantization === TerrainQuantization$1.NONE) { return attributesNone; } return attributes; }; TerrainEncoding.clone = function (encoding, result) { if (!defined(result)) { result = new TerrainEncoding(); } result.quantization = encoding.quantization; result.minimumHeight = encoding.minimumHeight; result.maximumHeight = encoding.maximumHeight; result.center = Cartesian3.clone(encoding.center); result.toScaledENU = Matrix4.clone(encoding.toScaledENU); result.fromScaledENU = Matrix4.clone(encoding.fromScaledENU); result.matrix = Matrix4.clone(encoding.matrix); result.hasVertexNormals = encoding.hasVertexNormals; result.hasWebMercatorT = encoding.hasWebMercatorT; return result; }; /** * The map projection used by Google Maps, Bing Maps, and most of ArcGIS Online, EPSG:3857. This * projection use longitude and latitude expressed with the WGS84 and transforms them to Mercator using * the spherical (rather than ellipsoidal) equations. * * @alias WebMercatorProjection * @constructor * * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid. * * @see GeographicProjection */ function WebMercatorProjection(ellipsoid) { this._ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); this._semimajorAxis = this._ellipsoid.maximumRadius; this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis; } Object.defineProperties(WebMercatorProjection.prototype, { /** * Gets the {@link Ellipsoid}. * * @memberof WebMercatorProjection.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, }); /** * Converts a Mercator angle, in the range -PI to PI, to a geodetic latitude * in the range -PI/2 to PI/2. * * @param {Number} mercatorAngle The angle to convert. * @returns {Number} The geodetic latitude in radians. */ WebMercatorProjection.mercatorAngleToGeodeticLatitude = function ( mercatorAngle ) { return CesiumMath.PI_OVER_TWO - 2.0 * Math.atan(Math.exp(-mercatorAngle)); }; /** * Converts a geodetic latitude in radians, in the range -PI/2 to PI/2, to a Mercator * angle in the range -PI to PI. * * @param {Number} latitude The geodetic latitude in radians. * @returns {Number} The Mercator angle. */ WebMercatorProjection.geodeticLatitudeToMercatorAngle = function (latitude) { // Clamp the latitude coordinate to the valid Mercator bounds. if (latitude > WebMercatorProjection.MaximumLatitude) { latitude = WebMercatorProjection.MaximumLatitude; } else if (latitude < -WebMercatorProjection.MaximumLatitude) { latitude = -WebMercatorProjection.MaximumLatitude; } var sinLatitude = Math.sin(latitude); return 0.5 * Math.log((1.0 + sinLatitude) / (1.0 - sinLatitude)); }; /** * The maximum latitude (both North and South) supported by a Web Mercator * (EPSG:3857) projection. Technically, the Mercator projection is defined * for any latitude up to (but not including) 90 degrees, but it makes sense * to cut it off sooner because it grows exponentially with increasing latitude. * The logic behind this particular cutoff value, which is the one used by * Google Maps, Bing Maps, and Esri, is that it makes the projection * square. That is, the rectangle is equal in the X and Y directions. * * The constant value is computed by calling: * WebMercatorProjection.mercatorAngleToGeodeticLatitude(Math.PI) * * @type {Number} */ WebMercatorProjection.MaximumLatitude = WebMercatorProjection.mercatorAngleToGeodeticLatitude( Math.PI ); /** * Converts geodetic ellipsoid coordinates, in radians, to the equivalent Web Mercator * X, Y, Z coordinates expressed in meters and returned in a {@link Cartesian3}. The height * is copied unmodified to the Z coordinate. * * @param {Cartographic} cartographic The cartographic coordinates in radians. * @param {Cartesian3} [result] The instance to which to copy the result, or undefined if a * new instance should be created. * @returns {Cartesian3} The equivalent web mercator X, Y, Z coordinates, in meters. */ WebMercatorProjection.prototype.project = function (cartographic, result) { var semimajorAxis = this._semimajorAxis; var x = cartographic.longitude * semimajorAxis; var y = WebMercatorProjection.geodeticLatitudeToMercatorAngle( 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; }; /** * Converts Web Mercator X, Y coordinates, expressed in meters, to a {@link Cartographic} * containing geodetic ellipsoid coordinates. The Z coordinate is copied unmodified to the * height. * * @param {Cartesian3} cartesian The web mercator Cartesian position to unrproject with height (z) in meters. * @param {Cartographic} [result] The instance to which to copy the result, or undefined if a * new instance should be created. * @returns {Cartographic} The equivalent cartographic coordinates. */ WebMercatorProjection.prototype.unproject = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); if (!defined(cartesian)) { throw new DeveloperError("cartesian is required"); } //>>includeEnd('debug'); var oneOverEarthSemimajorAxis = this._oneOverSemimajorAxis; var longitude = cartesian.x * oneOverEarthSemimajorAxis; var latitude = WebMercatorProjection.mercatorAngleToGeodeticLatitude( 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; }; /** * Contains functions to create a mesh from a heightmap image. * * @namespace HeightmapTessellator * * @private */ var HeightmapTessellator = {}; /** * The default structure of a heightmap, as given to {@link HeightmapTessellator.computeVertices}. * * @constant */ HeightmapTessellator.DEFAULT_STRUCTURE = Object.freeze({ heightScale: 1.0, heightOffset: 0.0, elementsPerHeight: 1, stride: 1, elementMultiplier: 256.0, isBigEndian: false, }); var cartesian3Scratch$3 = new Cartesian3(); var matrix4Scratch = new Matrix4(); var minimumScratch = new Cartesian3(); var maximumScratch = new Cartesian3(); /** * Fills an array of vertices from a heightmap image. * * @param {Object} options Object with the following properties: * @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} options.heightmap The heightmap to tessellate. * @param {Number} options.width The width of the heightmap, in height samples. * @param {Number} options.height The height of the heightmap, in height samples. * @param {Number} options.skirtHeight The height of skirts to drape at the edges of the heightmap. * @param {Rectangle} options.nativeRectangle A rectangle in the native coordinates of the heightmap's projection. For * a heightmap with a geographic projection, this is degrees. For the web mercator * projection, this is meters. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * @param {Rectangle} [options.rectangle] The rectangle covered by the heightmap, in geodetic coordinates with north, south, east and * west properties in radians. Either rectangle or nativeRectangle must be provided. If both * are provided, they're assumed to be consistent. * @param {Boolean} [options.isGeographic=true] True if the heightmap uses a {@link GeographicProjection}, or false if it uses * a {@link WebMercatorProjection}. * @param {Cartesian3} [options.relativeToCenter=Cartesian3.ZERO] The positions will be computed as Cartesian3.subtract(worldPosition, relativeToCenter). * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to which the heightmap applies. * @param {Object} [options.structure] An object describing the structure of the height data. * @param {Number} [options.structure.heightScale=1.0] The factor by which to multiply height samples in order to obtain * the height above the heightOffset, in meters. The heightOffset is added to the resulting * height after multiplying by the scale. * @param {Number} [options.structure.heightOffset=0.0] The offset to add to the scaled height to obtain the final * height in meters. The offset is added after the height sample is multiplied by the * heightScale. * @param {Number} [options.structure.elementsPerHeight=1] The number of elements in the buffer that make up a single height * sample. This is usually 1, indicating that each element is a separate height sample. If * it is greater than 1, that number of elements together form the height sample, which is * computed according to the structure.elementMultiplier and structure.isBigEndian properties. * @param {Number} [options.structure.stride=1] The number of elements to skip to get from the first element of * one height to the first element of the next height. * @param {Number} [options.structure.elementMultiplier=256.0] The multiplier used to compute the height value when the * stride property is greater than 1. For example, if the stride is 4 and the strideMultiplier * is 256, the height is computed as follows: * `height = buffer[index] + buffer[index + 1] * 256 + buffer[index + 2] * 256 * 256 + buffer[index + 3] * 256 * 256 * 256` * This is assuming that the isBigEndian property is false. If it is true, the order of the * elements is reversed. * @param {Number} [options.structure.lowestEncodedHeight] The lowest value that can be stored in the height buffer. Any heights that are lower * than this value after encoding with the `heightScale` and `heightOffset` are clamped to this value. For example, if the height * buffer is a `Uint16Array`, this value should be 0 because a `Uint16Array` cannot store negative numbers. If this parameter is * not specified, no minimum value is enforced. * @param {Number} [options.structure.highestEncodedHeight] The highest value that can be stored in the height buffer. Any heights that are higher * than this value after encoding with the `heightScale` and `heightOffset` are clamped to this value. For example, if the height * buffer is a `Uint16Array`, this value should be `256 * 256 - 1` or 65535 because a `Uint16Array` cannot store numbers larger * than 65535. If this parameter is not specified, no maximum value is enforced. * @param {Boolean} [options.structure.isBigEndian=false] Indicates endianness of the elements in the buffer when the * stride property is greater than 1. If this property is false, the first element is the * low-order element. If it is true, the first element is the high-order element. * * @example * var width = 5; * var height = 5; * var statistics = Cesium.HeightmapTessellator.computeVertices({ * heightmap : [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], * width : width, * height : height, * skirtHeight : 0.0, * nativeRectangle : { * west : 10.0, * east : 20.0, * south : 30.0, * north : 40.0 * } * }); * * var encoding = statistics.encoding; * var position = encoding.decodePosition(statistics.vertices, index * encoding.getStride()); */ HeightmapTessellator.computeVertices = function (options) { //>>includeStart('debug', pragmas.debug); if (!defined(options) || !defined(options.heightmap)) { throw new DeveloperError("options.heightmap is required."); } if (!defined(options.width) || !defined(options.height)) { throw new DeveloperError("options.width and options.height are required."); } if (!defined(options.nativeRectangle)) { throw new DeveloperError("options.nativeRectangle is required."); } if (!defined(options.skirtHeight)) { throw new DeveloperError("options.skirtHeight is required."); } //>>includeEnd('debug'); // This function tends to be a performance hotspot for terrain rendering, // so it employs a lot of inlining and unrolling as an optimization. // In particular, the functionality of Ellipsoid.cartographicToCartesian // is inlined. var cos = Math.cos; var sin = Math.sin; var sqrt = Math.sqrt; var atan = Math.atan; var exp = Math.exp; var piOverTwo = CesiumMath.PI_OVER_TWO; var toRadians = CesiumMath.toRadians; var heightmap = options.heightmap; var width = options.width; var height = options.height; var skirtHeight = options.skirtHeight; var isGeographic = defaultValue(options.isGeographic, true); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var oneOverGlobeSemimajorAxis = 1.0 / ellipsoid.maximumRadius; var nativeRectangle = options.nativeRectangle; var geographicWest; var geographicSouth; var geographicEast; var geographicNorth; var rectangle = options.rectangle; if (!defined(rectangle)) { if (isGeographic) { geographicWest = toRadians(nativeRectangle.west); geographicSouth = toRadians(nativeRectangle.south); geographicEast = toRadians(nativeRectangle.east); geographicNorth = toRadians(nativeRectangle.north); } else { geographicWest = nativeRectangle.west * oneOverGlobeSemimajorAxis; geographicSouth = piOverTwo - 2.0 * atan(exp(-nativeRectangle.south * oneOverGlobeSemimajorAxis)); geographicEast = nativeRectangle.east * oneOverGlobeSemimajorAxis; geographicNorth = piOverTwo - 2.0 * atan(exp(-nativeRectangle.north * oneOverGlobeSemimajorAxis)); } } else { geographicWest = rectangle.west; geographicSouth = rectangle.south; geographicEast = rectangle.east; geographicNorth = rectangle.north; } var relativeToCenter = options.relativeToCenter; var hasRelativeToCenter = defined(relativeToCenter); relativeToCenter = hasRelativeToCenter ? relativeToCenter : Cartesian3.ZERO; var exaggeration = defaultValue(options.exaggeration, 1.0); var includeWebMercatorT = defaultValue(options.includeWebMercatorT, false); var structure = defaultValue( options.structure, HeightmapTessellator.DEFAULT_STRUCTURE ); var heightScale = defaultValue( structure.heightScale, HeightmapTessellator.DEFAULT_STRUCTURE.heightScale ); var heightOffset = defaultValue( structure.heightOffset, HeightmapTessellator.DEFAULT_STRUCTURE.heightOffset ); var elementsPerHeight = defaultValue( structure.elementsPerHeight, HeightmapTessellator.DEFAULT_STRUCTURE.elementsPerHeight ); var stride = defaultValue( structure.stride, HeightmapTessellator.DEFAULT_STRUCTURE.stride ); var elementMultiplier = defaultValue( structure.elementMultiplier, HeightmapTessellator.DEFAULT_STRUCTURE.elementMultiplier ); var isBigEndian = defaultValue( structure.isBigEndian, HeightmapTessellator.DEFAULT_STRUCTURE.isBigEndian ); var rectangleWidth = Rectangle.computeWidth(nativeRectangle); var rectangleHeight = Rectangle.computeHeight(nativeRectangle); var granularityX = rectangleWidth / (width - 1); var granularityY = rectangleHeight / (height - 1); if (!isGeographic) { rectangleWidth *= oneOverGlobeSemimajorAxis; rectangleHeight *= oneOverGlobeSemimajorAxis; } var radiiSquared = ellipsoid.radiiSquared; var radiiSquaredX = radiiSquared.x; var radiiSquaredY = radiiSquared.y; var radiiSquaredZ = radiiSquared.z; var minimumHeight = 65536.0; var maximumHeight = -65536.0; var fromENU = Transforms.eastNorthUpToFixedFrame(relativeToCenter, ellipsoid); var toENU = Matrix4.inverseTransformation(fromENU, matrix4Scratch); var southMercatorY; var oneOverMercatorHeight; if (includeWebMercatorT) { southMercatorY = WebMercatorProjection.geodeticLatitudeToMercatorAngle( geographicSouth ); oneOverMercatorHeight = 1.0 / (WebMercatorProjection.geodeticLatitudeToMercatorAngle(geographicNorth) - southMercatorY); } var minimum = minimumScratch; minimum.x = Number.POSITIVE_INFINITY; minimum.y = Number.POSITIVE_INFINITY; minimum.z = Number.POSITIVE_INFINITY; var maximum = maximumScratch; maximum.x = Number.NEGATIVE_INFINITY; maximum.y = Number.NEGATIVE_INFINITY; maximum.z = Number.NEGATIVE_INFINITY; var hMin = Number.POSITIVE_INFINITY; var gridVertexCount = width * height; var edgeVertexCount = skirtHeight > 0.0 ? width * 2 + height * 2 : 0; var vertexCount = gridVertexCount + edgeVertexCount; var positions = new Array(vertexCount); var heights = new Array(vertexCount); var uvs = new Array(vertexCount); var webMercatorTs = includeWebMercatorT ? new Array(vertexCount) : []; var startRow = 0; var endRow = height; var startCol = 0; var endCol = width; if (skirtHeight > 0.0) { --startRow; ++endRow; --startCol; ++endCol; } var skirtOffsetPercentage = 0.00001; for (var rowIndex = startRow; rowIndex < endRow; ++rowIndex) { var row = rowIndex; if (row < 0) { row = 0; } if (row >= height) { row = height - 1; } var latitude = nativeRectangle.north - granularityY * row; if (!isGeographic) { latitude = piOverTwo - 2.0 * atan(exp(-latitude * oneOverGlobeSemimajorAxis)); } else { latitude = toRadians(latitude); } var v = (latitude - geographicSouth) / (geographicNorth - geographicSouth); v = CesiumMath.clamp(v, 0.0, 1.0); var isNorthEdge = rowIndex === startRow; var isSouthEdge = rowIndex === endRow - 1; if (skirtHeight > 0.0) { if (isNorthEdge) { latitude += skirtOffsetPercentage * rectangleHeight; } else if (isSouthEdge) { latitude -= skirtOffsetPercentage * rectangleHeight; } } var cosLatitude = cos(latitude); var nZ = sin(latitude); var kZ = radiiSquaredZ * nZ; var webMercatorT; if (includeWebMercatorT) { webMercatorT = (WebMercatorProjection.geodeticLatitudeToMercatorAngle(latitude) - southMercatorY) * oneOverMercatorHeight; } for (var colIndex = startCol; colIndex < endCol; ++colIndex) { var col = colIndex; if (col < 0) { col = 0; } if (col >= width) { col = width - 1; } var terrainOffset = row * (width * stride) + col * stride; var heightSample; if (elementsPerHeight === 1) { heightSample = heightmap[terrainOffset]; } else { heightSample = 0; var elementOffset; if (isBigEndian) { for ( elementOffset = 0; elementOffset < elementsPerHeight; ++elementOffset ) { heightSample = heightSample * elementMultiplier + heightmap[terrainOffset + elementOffset]; } } else { for ( elementOffset = elementsPerHeight - 1; elementOffset >= 0; --elementOffset ) { heightSample = heightSample * elementMultiplier + heightmap[terrainOffset + elementOffset]; } } } heightSample = (heightSample * heightScale + heightOffset) * exaggeration; maximumHeight = Math.max(maximumHeight, heightSample); minimumHeight = Math.min(minimumHeight, heightSample); var longitude = nativeRectangle.west + granularityX * col; if (!isGeographic) { longitude = longitude * oneOverGlobeSemimajorAxis; } else { longitude = toRadians(longitude); } var u = (longitude - geographicWest) / (geographicEast - geographicWest); u = CesiumMath.clamp(u, 0.0, 1.0); var index = row * width + col; if (skirtHeight > 0.0) { var isWestEdge = colIndex === startCol; var isEastEdge = colIndex === endCol - 1; var isEdge = isNorthEdge || isSouthEdge || isWestEdge || isEastEdge; var isCorner = (isNorthEdge || isSouthEdge) && (isWestEdge || isEastEdge); if (isCorner) { // Don't generate skirts on the corners. continue; } else if (isEdge) { heightSample -= skirtHeight; if (isWestEdge) { // The outer loop iterates north to south but the indices are ordered south to north, hence the index flip below index = gridVertexCount + (height - row - 1); longitude -= skirtOffsetPercentage * rectangleWidth; } else if (isSouthEdge) { // Add after west indices. South indices are ordered east to west. index = gridVertexCount + height + (width - col - 1); } else if (isEastEdge) { // Add after west and south indices. East indices are ordered north to south. The index is flipped like above. index = gridVertexCount + height + width + row; longitude += skirtOffsetPercentage * rectangleWidth; } else if (isNorthEdge) { // Add after west, south, and east indices. North indices are ordered west to east. index = gridVertexCount + height + width + height + col; } } } var nX = cosLatitude * cos(longitude); var nY = cosLatitude * sin(longitude); var kX = radiiSquaredX * nX; var kY = radiiSquaredY * nY; var gamma = sqrt(kX * nX + kY * nY + kZ * nZ); var oneOverGamma = 1.0 / gamma; var rSurfaceX = kX * oneOverGamma; var rSurfaceY = kY * oneOverGamma; var rSurfaceZ = kZ * oneOverGamma; var position = new Cartesian3(); position.x = rSurfaceX + nX * heightSample; position.y = rSurfaceY + nY * heightSample; position.z = rSurfaceZ + nZ * heightSample; positions[index] = position; heights[index] = heightSample; uvs[index] = new Cartesian2(u, v); if (includeWebMercatorT) { webMercatorTs[index] = webMercatorT; } Matrix4.multiplyByPoint(toENU, position, cartesian3Scratch$3); Cartesian3.minimumByComponent(cartesian3Scratch$3, minimum, minimum); Cartesian3.maximumByComponent(cartesian3Scratch$3, maximum, maximum); hMin = Math.min(hMin, heightSample); } } var boundingSphere3D = BoundingSphere.fromPoints(positions); var orientedBoundingBox; if (defined(rectangle)) { orientedBoundingBox = OrientedBoundingBox.fromRectangle( rectangle, minimumHeight, maximumHeight, ellipsoid ); } var occludeePointInScaledSpace; if (hasRelativeToCenter) { var occluder = new EllipsoidalOccluder(ellipsoid); occludeePointInScaledSpace = occluder.computeHorizonCullingPointPossiblyUnderEllipsoid( relativeToCenter, positions, minimumHeight ); } var aaBox = new AxisAlignedBoundingBox(minimum, maximum, relativeToCenter); var encoding = new TerrainEncoding( aaBox, hMin, maximumHeight, fromENU, false, includeWebMercatorT ); var vertices = new Float32Array(vertexCount * encoding.getStride()); var bufferIndex = 0; for (var j = 0; j < vertexCount; ++j) { bufferIndex = encoding.encode( vertices, bufferIndex, positions[j], uvs[j], heights[j], undefined, webMercatorTs[j] ); } return { vertices: vertices, maximumHeight: maximumHeight, minimumHeight: minimumHeight, encoding: encoding, boundingSphere3D: boundingSphere3D, orientedBoundingBox: orientedBoundingBox, occludeePointInScaledSpace: occludeePointInScaledSpace, }; }; function returnTrue() { return true; } /** * Destroys an object. Each of the object's functions, including functions in its prototype, * is replaced with a function that throws a {@link DeveloperError}, except for the object's * isDestroyed function, which is set to a function that returns true. * The object's properties are removed with delete. *

* This function is used by objects that hold native resources, e.g., WebGL resources, which * need to be explicitly released. Client code calls an object's destroy function, * which then releases the native resource and calls destroyObject to put itself * in a destroyed state. * * @function * * @param {Object} object The object to destroy. * @param {String} [message] The message to include in the exception that is thrown if * a destroyed object's function is called. * * * @example * // How a texture would destroy itself. * this.destroy = function () { * _gl.deleteTexture(_texture); * return Cesium.destroyObject(this); * }; * * @see DeveloperError */ function destroyObject(object, message) { message = defaultValue( message, "This object was destroyed, i.e., destroy() was called." ); function throwOnDestroyed() { //>>includeStart('debug', pragmas.debug); throw new DeveloperError(message); //>>includeEnd('debug'); } for (var key in object) { if (typeof object[key] === "function") { object[key] = throwOnDestroyed; } } object.isDestroyed = returnTrue; return undefined; } function canTransferArrayBuffer() { if (!defined(TaskProcessor._canTransferArrayBuffer)) { var worker = new Worker(getWorkerUrl("Workers/transferTypedArrayTest.js")); worker.postMessage = defaultValue( worker.webkitPostMessage, worker.postMessage ); var value = 99; var array = new Int8Array([value]); try { // postMessage might fail with a DataCloneError // if transferring array buffers is not supported. worker.postMessage( { array: array, }, [array.buffer] ); } catch (e) { TaskProcessor._canTransferArrayBuffer = false; return TaskProcessor._canTransferArrayBuffer; } var deferred = when.defer(); worker.onmessage = function (event) { var array = event.data.array; // some versions of Firefox silently fail to transfer typed arrays. // https://bugzilla.mozilla.org/show_bug.cgi?id=841904 // Check to make sure the value round-trips successfully. var result = defined(array) && array[0] === value; deferred.resolve(result); worker.terminate(); TaskProcessor._canTransferArrayBuffer = result; }; TaskProcessor._canTransferArrayBuffer = deferred.promise; } return TaskProcessor._canTransferArrayBuffer; } var taskCompletedEvent = new Event(); function completeTask(processor, data) { --processor._activeTasks; var id = data.id; if (!defined(id)) { // This is not one of ours. return; } var deferreds = processor._deferreds; var deferred = deferreds[id]; if (defined(data.error)) { var error = data.error; if (error.name === "RuntimeError") { error = new RuntimeError(data.error.message); error.stack = data.error.stack; } else if (error.name === "DeveloperError") { error = new DeveloperError(data.error.message); error.stack = data.error.stack; } taskCompletedEvent.raiseEvent(error); deferred.reject(error); } else { taskCompletedEvent.raiseEvent(); deferred.resolve(data.result); } delete deferreds[id]; } function getWorkerUrl(moduleID) { var url = buildModuleUrl(moduleID); if (isCrossOriginUrl(url)) { //to load cross-origin, create a shim worker from a blob URL var script = 'importScripts("' + url + '");'; var blob; try { blob = new Blob([script], { type: "application/javascript", }); } catch (e) { var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; var blobBuilder = new BlobBuilder(); blobBuilder.append(script); blob = blobBuilder.getBlob("application/javascript"); } var URL = window.URL || window.webkitURL; url = URL.createObjectURL(blob); } return url; } var bootstrapperUrlResult; function getBootstrapperUrl() { if (!defined(bootstrapperUrlResult)) { bootstrapperUrlResult = getWorkerUrl("Workers/cesiumWorkerBootstrapper.js"); } return bootstrapperUrlResult; } function createWorker(processor) { var worker = new Worker(getBootstrapperUrl()); worker.postMessage = defaultValue( worker.webkitPostMessage, worker.postMessage ); var bootstrapMessage = { loaderConfig: { paths: { Workers: buildModuleUrl("Workers"), }, baseUrl: buildModuleUrl.getCesiumBaseUrl().url, }, workerModule: processor._workerPath, }; worker.postMessage(bootstrapMessage); worker.onmessage = function (event) { completeTask(processor, event.data); }; return worker; } function getWebAssemblyLoaderConfig(processor, wasmOptions) { var config = { modulePath: undefined, wasmBinaryFile: undefined, wasmBinary: undefined, }; // Web assembly not supported, use fallback js module if provided if (!FeatureDetection.supportsWebAssembly()) { if (!defined(wasmOptions.fallbackModulePath)) { throw new RuntimeError( "This browser does not support Web Assembly, and no backup module was provided for " + processor._workerPath ); } config.modulePath = buildModuleUrl(wasmOptions.fallbackModulePath); return when.resolve(config); } config.modulePath = buildModuleUrl(wasmOptions.modulePath); config.wasmBinaryFile = buildModuleUrl(wasmOptions.wasmBinaryFile); return Resource.fetchArrayBuffer({ url: config.wasmBinaryFile, }).then(function (arrayBuffer) { config.wasmBinary = arrayBuffer; return config; }); } /** * A wrapper around a web worker that allows scheduling tasks for a given worker, * returning results asynchronously via a promise. * * The Worker is not constructed until a task is scheduled. * * @alias TaskProcessor * @constructor * * @param {String} workerPath The Url to the worker. This can either be an absolute path or relative to the Cesium Workers folder. * @param {Number} [maximumActiveTasks=Number.POSITIVE_INFINITY] The maximum number of active tasks. Once exceeded, * scheduleTask will not queue any more tasks, allowing * work to be rescheduled in future frames. */ function TaskProcessor(workerPath, maximumActiveTasks) { this._workerPath = new URI(workerPath).isAbsolute() ? workerPath : TaskProcessor._workerModulePrefix + workerPath; this._maximumActiveTasks = defaultValue( maximumActiveTasks, Number.POSITIVE_INFINITY ); this._activeTasks = 0; this._deferreds = {}; this._nextID = 0; } var emptyTransferableObjectArray = []; /** * Schedule a task to be processed by the web worker asynchronously. If there are currently more * tasks active than the maximum set by the constructor, will immediately return undefined. * Otherwise, returns a promise that will resolve to the result posted back by the worker when * finished. * * @param {Object} parameters Any input data that will be posted to the worker. * @param {Object[]} [transferableObjects] An array of objects contained in parameters that should be * transferred to the worker instead of copied. * @returns {Promise.|undefined} Either a promise that will resolve to the result when available, or undefined * if there are too many active tasks, * * @example * var taskProcessor = new Cesium.TaskProcessor('myWorkerPath'); * var promise = taskProcessor.scheduleTask({ * someParameter : true, * another : 'hello' * }); * if (!Cesium.defined(promise)) { * // too many active tasks - try again later * } else { * Cesium.when(promise, function(result) { * // use the result of the task * }); * } */ TaskProcessor.prototype.scheduleTask = function ( parameters, transferableObjects ) { if (!defined(this._worker)) { this._worker = createWorker(this); } if (this._activeTasks >= this._maximumActiveTasks) { return undefined; } ++this._activeTasks; var processor = this; return when(canTransferArrayBuffer(), function (canTransferArrayBuffer) { if (!defined(transferableObjects)) { transferableObjects = emptyTransferableObjectArray; } else if (!canTransferArrayBuffer) { transferableObjects.length = 0; } var id = processor._nextID++; var deferred = when.defer(); processor._deferreds[id] = deferred; processor._worker.postMessage( { id: id, parameters: parameters, canTransferArrayBuffer: canTransferArrayBuffer, }, transferableObjects ); return deferred.promise; }); }; /** * Posts a message to a web worker with configuration to initialize loading * and compiling a web assembly module asychronously, as well as an optional * fallback JavaScript module to use if Web Assembly is not supported. * * @param {Object} [webAssemblyOptions] An object with the following properties: * @param {String} [webAssemblyOptions.modulePath] The path of the web assembly JavaScript wrapper module. * @param {String} [webAssemblyOptions.wasmBinaryFile] The path of the web assembly binary file. * @param {String} [webAssemblyOptions.fallbackModulePath] The path of the fallback JavaScript module to use if web assembly is not supported. * @returns {Promise.} A promise that resolves to the result when the web worker has loaded and compiled the web assembly module and is ready to process tasks. */ TaskProcessor.prototype.initWebAssemblyModule = function (webAssemblyOptions) { if (!defined(this._worker)) { this._worker = createWorker(this); } var deferred = when.defer(); var processor = this; var worker = this._worker; getWebAssemblyLoaderConfig(this, webAssemblyOptions).then(function ( wasmConfig ) { return when(canTransferArrayBuffer(), function (canTransferArrayBuffer) { var transferableObjects; var binary = wasmConfig.wasmBinary; if (defined(binary) && canTransferArrayBuffer) { transferableObjects = [binary]; } worker.onmessage = function (event) { worker.onmessage = function (event) { completeTask(processor, event.data); }; deferred.resolve(event.data); }; worker.postMessage( { webAssemblyConfig: wasmConfig }, transferableObjects ); }); }); return deferred; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see TaskProcessor#destroy */ TaskProcessor.prototype.isDestroyed = function () { return false; }; /** * Destroys this object. This will immediately terminate the Worker. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. */ TaskProcessor.prototype.destroy = function () { if (defined(this._worker)) { this._worker.terminate(); } return destroyObject(this); }; /** * An event that's raised when a task is completed successfully. Event handlers are passed * the error object is a task fails. * * @type {Event} * * @private */ TaskProcessor.taskCompletedEvent = taskCompletedEvent; // exposed for testing purposes TaskProcessor._defaultWorkerModulePrefix = "Workers/"; TaskProcessor._workerModulePrefix = TaskProcessor._defaultWorkerModulePrefix; TaskProcessor._canTransferArrayBuffer = undefined; /** * Terrain data for a single tile. This type describes an * interface and is not intended to be instantiated directly. * * @alias TerrainData * @constructor * * @see HeightmapTerrainData * @see QuantizedMeshTerrainData * @see GoogleEarthEnterpriseTerrainData */ function TerrainData() { DeveloperError.throwInstantiationError(); } Object.defineProperties(TerrainData.prototype, { /** * An array of credits for this tile. * @memberof TerrainData.prototype * @type {Credit[]} */ credits: { get: DeveloperError.throwInstantiationError, }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. * @memberof TerrainData.prototype * @type {Uint8Array|HTMLImageElement|HTMLCanvasElement} */ waterMask: { get: DeveloperError.throwInstantiationError, }, }); /** * Computes the terrain height at a specified longitude and latitude. * @function * * @param {Rectangle} rectangle The rectangle covered by this terrain data. * @param {Number} longitude The longitude in radians. * @param {Number} latitude The latitude in radians. * @returns {Number} The terrain height at the specified position. If the position * is outside the rectangle, this method will extrapolate the height, which is likely to be wildly * incorrect for positions far outside the rectangle. */ TerrainData.prototype.interpolateHeight = DeveloperError.throwInstantiationError; /** * Determines if a given child tile is available, based on the * {@link TerrainData#childTileMask}. The given child tile coordinates are assumed * to be one of the four children of this tile. If non-child tile coordinates are * given, the availability of the southeast child tile is returned. * @function * * @param {Number} thisX The tile X coordinate of this (the parent) tile. * @param {Number} thisY The tile Y coordinate of this (the parent) tile. * @param {Number} childX The tile X coordinate of the child tile to check for availability. * @param {Number} childY The tile Y coordinate of the child tile to check for availability. * @returns {Boolean} True if the child tile is available; otherwise, false. */ TerrainData.prototype.isChildAvailable = DeveloperError.throwInstantiationError; /** * Creates a {@link TerrainMesh} from this terrain data. * @function * * @private * * @param {Object} options Object with the following properties: * @param {TilingScheme} options.tilingScheme The tiling scheme to which this tile belongs. * @param {Number} options.x The X coordinate of the tile for which to create the terrain data. * @param {Number} options.y The Y coordinate of the tile for which to create the terrain data. * @param {Number} options.level The level of the tile for which to create the terrain data. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * @param {Boolean} [options.throttle=true] If true, indicates that this operation will need to be retried if too many asynchronous mesh creations are already in progress. * @returns {Promise.|undefined} A promise for the terrain mesh, or undefined if too many * asynchronous mesh creations are already in progress and the operation should * be retried later. */ TerrainData.prototype.createMesh = DeveloperError.throwInstantiationError; /** * Upsamples this terrain data for use by a descendant tile. * @function * * @param {TilingScheme} tilingScheme The tiling scheme of this terrain data. * @param {Number} thisX The X coordinate of this tile in the tiling scheme. * @param {Number} thisY The Y coordinate of this tile in the tiling scheme. * @param {Number} thisLevel The level of this tile in the tiling scheme. * @param {Number} descendantX The X coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantY The Y coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantLevel The level within the tiling scheme of the descendant tile for which we are upsampling. * @returns {Promise.|undefined} A promise for upsampled terrain data for the descendant tile, * or undefined if too many asynchronous upsample operations are in progress and the request has been * deferred. */ TerrainData.prototype.upsample = DeveloperError.throwInstantiationError; /** * Gets a value indicating whether or not this terrain data was created by upsampling lower resolution * terrain data. If this value is false, the data was obtained from some other source, such * as by downloading it from a remote server. This method should return true for instances * returned from a call to {@link TerrainData#upsample}. * @function * * @returns {Boolean} True if this instance was created by upsampling; otherwise, false. */ TerrainData.prototype.wasCreatedByUpsampling = DeveloperError.throwInstantiationError; /** * The maximum number of asynchronous tasks used for terrain processing. * * @type {Number} * @private */ TerrainData.maximumAsynchronousTasks = 5; /** * A mesh plus related metadata for a single tile of terrain. Instances of this type are * usually created from raw {@link TerrainData}. * * @alias TerrainMesh * @constructor * * @param {Cartesian3} center The center of the tile. Vertex positions are specified relative to this center. * @param {Float32Array} vertices The vertex data, including positions, texture coordinates, and heights. * The vertex data is in the order [X, Y, Z, H, U, V], where X, Y, and Z represent * the Cartesian position of the vertex, H is the height above the ellipsoid, and * U and V are the texture coordinates. * @param {Uint8Array|Uint16Array|Uint32Array} indices The indices describing how the vertices are connected to form triangles. * @param {Number} indexCountWithoutSkirts The index count of the mesh not including skirts. * @param {Number} vertexCountWithoutSkirts The vertex count of the mesh not including skirts. * @param {Number} minimumHeight The lowest height in the tile, in meters above the ellipsoid. * @param {Number} maximumHeight The highest height in the tile, in meters above the ellipsoid. * @param {BoundingSphere} boundingSphere3D A bounding sphere that completely contains the tile. * @param {Cartesian3} occludeePointInScaledSpace The occludee point of the tile, represented in ellipsoid- * scaled space, and used for horizon culling. If this point is below the horizon, * the tile is considered to be entirely below the horizon. * @param {Number} [vertexStride=6] The number of components in each vertex. * @param {OrientedBoundingBox} [orientedBoundingBox] A bounding box that completely contains the tile. * @param {TerrainEncoding} encoding Information used to decode the mesh. * @param {Number} exaggeration The amount that this mesh was exaggerated. * @param {Number[]} westIndicesSouthToNorth The indices of the vertices on the Western edge of the tile, ordered from South to North (clockwise). * @param {Number[]} southIndicesEastToWest The indices of the vertices on the Southern edge of the tile, ordered from East to West (clockwise). * @param {Number[]} eastIndicesNorthToSouth The indices of the vertices on the Eastern edge of the tile, ordered from North to South (clockwise). * @param {Number[]} northIndicesWestToEast The indices of the vertices on the Northern edge of the tile, ordered from West to East (clockwise). * * @private */ function TerrainMesh( center, vertices, indices, indexCountWithoutSkirts, vertexCountWithoutSkirts, minimumHeight, maximumHeight, boundingSphere3D, occludeePointInScaledSpace, vertexStride, orientedBoundingBox, encoding, exaggeration, westIndicesSouthToNorth, southIndicesEastToWest, eastIndicesNorthToSouth, northIndicesWestToEast ) { /** * The center of the tile. Vertex positions are specified relative to this center. * @type {Cartesian3} */ this.center = center; /** * The vertex data, including positions, texture coordinates, and heights. * The vertex data is in the order [X, Y, Z, H, U, V], where X, Y, and Z represent * the Cartesian position of the vertex, H is the height above the ellipsoid, and * U and V are the texture coordinates. The vertex data may have additional attributes after those * mentioned above when the {@link TerrainMesh#stride} is greater than 6. * @type {Float32Array} */ this.vertices = vertices; /** * The number of components in each vertex. Typically this is 6 for the 6 components * [X, Y, Z, H, U, V], but if each vertex has additional data (such as a vertex normal), this value * may be higher. * @type {Number} */ this.stride = defaultValue(vertexStride, 6); /** * The indices describing how the vertices are connected to form triangles. * @type {Uint8Array|Uint16Array|Uint32Array} */ this.indices = indices; /** * The index count of the mesh not including skirts. * @type {Number} */ this.indexCountWithoutSkirts = indexCountWithoutSkirts; /** * The vertex count of the mesh not including skirts. * @type {Number} */ this.vertexCountWithoutSkirts = vertexCountWithoutSkirts; /** * The lowest height in the tile, in meters above the ellipsoid. * @type {Number} */ this.minimumHeight = minimumHeight; /** * The highest height in the tile, in meters above the ellipsoid. * @type {Number} */ this.maximumHeight = maximumHeight; /** * A bounding sphere that completely contains the tile. * @type {BoundingSphere} */ this.boundingSphere3D = boundingSphere3D; /** * The occludee point of the tile, represented in ellipsoid- * scaled space, and used for horizon culling. If this point is below the horizon, * the tile is considered to be entirely below the horizon. * @type {Cartesian3} */ this.occludeePointInScaledSpace = occludeePointInScaledSpace; /** * A bounding box that completely contains the tile. * @type {OrientedBoundingBox} */ this.orientedBoundingBox = orientedBoundingBox; /** * Information for decoding the mesh vertices. * @type {TerrainEncoding} */ this.encoding = encoding; /** * The amount that this mesh was exaggerated. * @type {Number} */ this.exaggeration = exaggeration; /** * The indices of the vertices on the Western edge of the tile, ordered from South to North (clockwise). * @type {Number[]} */ this.westIndicesSouthToNorth = westIndicesSouthToNorth; /** * The indices of the vertices on the Southern edge of the tile, ordered from East to West (clockwise). * @type {Number[]} */ this.southIndicesEastToWest = southIndicesEastToWest; /** * The indices of the vertices on the Eastern edge of the tile, ordered from North to South (clockwise). * @type {Number[]} */ this.eastIndicesNorthToSouth = eastIndicesNorthToSouth; /** * The indices of the vertices on the Northern edge of the tile, ordered from West to East (clockwise). * @type {Number[]} */ this.northIndicesWestToEast = northIndicesWestToEast; } /** * Constants for WebGL index datatypes. These corresponds to the * type parameter of {@link http://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml|drawElements}. * * @enum {Number} */ var IndexDatatype = { /** * 8-bit unsigned byte corresponding to UNSIGNED_BYTE and the type * of an element in Uint8Array. * * @type {Number} * @constant */ UNSIGNED_BYTE: WebGLConstants$1.UNSIGNED_BYTE, /** * 16-bit unsigned short corresponding to UNSIGNED_SHORT and the type * of an element in Uint16Array. * * @type {Number} * @constant */ UNSIGNED_SHORT: WebGLConstants$1.UNSIGNED_SHORT, /** * 32-bit unsigned int corresponding to UNSIGNED_INT and the type * of an element in Uint32Array. * * @type {Number} * @constant */ UNSIGNED_INT: WebGLConstants$1.UNSIGNED_INT, }; /** * 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. * * @example * // Returns 2 * var size = Cesium.IndexDatatype.getSizeInBytes(Cesium.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; } //>>includeStart('debug', pragmas.debug); throw new DeveloperError( "indexDatatype is required and must be a valid IndexDatatype constant." ); //>>includeEnd('debug'); }; /** * Gets the datatype with a given size in bytes. * * @param {Number} sizeInBytes The size of a single index in bytes. * @returns {IndexDatatype} The index datatype with the given size. */ IndexDatatype.fromSizeInBytes = function (sizeInBytes) { switch (sizeInBytes) { case 2: return IndexDatatype.UNSIGNED_SHORT; case 4: return IndexDatatype.UNSIGNED_INT; case 1: return IndexDatatype.UNSIGNED_BYTE; //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError( "Size in bytes cannot be mapped to an IndexDatatype" ); //>>includeEnd('debug'); } }; /** * Validates that the provided index datatype is a valid {@link IndexDatatype}. * * @param {IndexDatatype} indexDatatype The index datatype to validate. * @returns {Boolean} true if the provided index datatype is a valid value; otherwise, false. * * @example * if (!Cesium.IndexDatatype.validate(indexDatatype)) { * throw new Cesium.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 * or Uint32Array depending on the number of vertices. * * @param {Number} numberOfVertices Number of vertices that the indices will reference. * @param {Number|Array} indicesLengthOrArray Passed through to the typed array constructor. * @returns {Uint16Array|Uint32Array} A Uint16Array or Uint32Array constructed with indicesLengthOrArray. * * @example * this.indices = Cesium.IndexDatatype.createTypedArray(positions.length / 3, numberOfIndices); */ IndexDatatype.createTypedArray = function ( numberOfVertices, indicesLengthOrArray ) { //>>includeStart('debug', pragmas.debug); if (!defined(numberOfVertices)) { throw new DeveloperError("numberOfVertices is required."); } //>>includeEnd('debug'); if (numberOfVertices >= CesiumMath.SIXTY_FOUR_KILOBYTES) { return new Uint32Array(indicesLengthOrArray); } return new Uint16Array(indicesLengthOrArray); }; /** * Creates a typed array from a source array buffer. The resulting typed array will store indices, using either * or Uint32Array depending on the number of vertices. * * @param {Number} numberOfVertices Number of vertices that the indices will reference. * @param {ArrayBuffer} sourceArray Passed through to the typed array constructor. * @param {Number} byteOffset Passed through to the typed array constructor. * @param {Number} length Passed through to the typed array constructor. * @returns {Uint16Array|Uint32Array} A Uint16Array or Uint32Array constructed with sourceArray, byteOffset, and length. * */ IndexDatatype.createTypedArrayFromArrayBuffer = function ( numberOfVertices, sourceArray, byteOffset, length ) { //>>includeStart('debug', pragmas.debug); if (!defined(numberOfVertices)) { throw new DeveloperError("numberOfVertices is required."); } if (!defined(sourceArray)) { throw new DeveloperError("sourceArray is required."); } if (!defined(byteOffset)) { throw new DeveloperError("byteOffset is required."); } //>>includeEnd('debug'); if (numberOfVertices >= CesiumMath.SIXTY_FOUR_KILOBYTES) { return new Uint32Array(sourceArray, byteOffset, length); } return new Uint16Array(sourceArray, byteOffset, length); }; var IndexDatatype$1 = Object.freeze(IndexDatatype); /** * Provides terrain or other geometry for the surface of an ellipsoid. The surface geometry is * organized into a pyramid of tiles according to a {@link TilingScheme}. This type describes an * interface and is not intended to be instantiated directly. * * @alias TerrainProvider * @constructor * * @see EllipsoidTerrainProvider * @see CesiumTerrainProvider * @see VRTheWorldTerrainProvider * @see GoogleEarthEnterpriseTerrainProvider */ function TerrainProvider() { DeveloperError.throwInstantiationError(); } Object.defineProperties(TerrainProvider.prototype, { /** * Gets an event that is raised when the terrain provider encounters an asynchronous error.. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof TerrainProvider.prototype * @type {Event} */ errorEvent: { get: DeveloperError.throwInstantiationError, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should * not be called before {@link TerrainProvider#ready} returns true. * @memberof TerrainProvider.prototype * @type {Credit} */ credit: { get: DeveloperError.throwInstantiationError, }, /** * Gets the tiling scheme used by the provider. This function should * not be called before {@link TerrainProvider#ready} returns true. * @memberof TerrainProvider.prototype * @type {TilingScheme} */ tilingScheme: { get: DeveloperError.throwInstantiationError, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof TerrainProvider.prototype * @type {Boolean} */ ready: { get: DeveloperError.throwInstantiationError, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof TerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: DeveloperError.throwInstantiationError, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link TerrainProvider#ready} returns true. * @memberof TerrainProvider.prototype * @type {Boolean} */ hasWaterMask: { get: DeveloperError.throwInstantiationError, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link TerrainProvider#ready} returns true. * @memberof TerrainProvider.prototype * @type {Boolean} */ hasVertexNormals: { get: DeveloperError.throwInstantiationError, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link TerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. * @memberof TerrainProvider.prototype * @type {TileAvailability} */ availability: { get: DeveloperError.throwInstantiationError, }, }); var regularGridIndicesCache = []; /** * Gets a list of indices for a triangle mesh representing a regular grid. Calling * this function multiple times with the same grid width and height returns the * same list of indices. The total number of vertices must be less than or equal * to 65536. * * @param {Number} width The number of vertices in the regular grid in the horizontal direction. * @param {Number} height The number of vertices in the regular grid in the vertical direction. * @returns {Uint16Array|Uint32Array} The list of indices. Uint16Array gets returned for 64KB or less and Uint32Array for 4GB or less. */ TerrainProvider.getRegularGridIndices = function (width, height) { //>>includeStart('debug', pragmas.debug); if (width * height >= CesiumMath.FOUR_GIGABYTES) { throw new DeveloperError( "The total number of vertices (width * height) must be less than 4,294,967,296." ); } //>>includeEnd('debug'); var byWidth = regularGridIndicesCache[width]; if (!defined(byWidth)) { regularGridIndicesCache[width] = byWidth = []; } var indices = byWidth[height]; if (!defined(indices)) { if (width * height < CesiumMath.SIXTY_FOUR_KILOBYTES) { indices = byWidth[height] = new Uint16Array( (width - 1) * (height - 1) * 6 ); } else { indices = byWidth[height] = new Uint32Array( (width - 1) * (height - 1) * 6 ); } addRegularGridIndices(width, height, indices, 0); } return indices; }; var regularGridAndEdgeIndicesCache = []; /** * @private */ TerrainProvider.getRegularGridIndicesAndEdgeIndices = function (width, height) { //>>includeStart('debug', pragmas.debug); if (width * height >= CesiumMath.FOUR_GIGABYTES) { throw new DeveloperError( "The total number of vertices (width * height) must be less than 4,294,967,296." ); } //>>includeEnd('debug'); var byWidth = regularGridAndEdgeIndicesCache[width]; if (!defined(byWidth)) { regularGridAndEdgeIndicesCache[width] = byWidth = []; } var indicesAndEdges = byWidth[height]; if (!defined(indicesAndEdges)) { var indices = TerrainProvider.getRegularGridIndices(width, height); var edgeIndices = getEdgeIndices(width, height); var westIndicesSouthToNorth = edgeIndices.westIndicesSouthToNorth; var southIndicesEastToWest = edgeIndices.southIndicesEastToWest; var eastIndicesNorthToSouth = edgeIndices.eastIndicesNorthToSouth; var northIndicesWestToEast = edgeIndices.northIndicesWestToEast; indicesAndEdges = byWidth[height] = { indices: indices, westIndicesSouthToNorth: westIndicesSouthToNorth, southIndicesEastToWest: southIndicesEastToWest, eastIndicesNorthToSouth: eastIndicesNorthToSouth, northIndicesWestToEast: northIndicesWestToEast, }; } return indicesAndEdges; }; var regularGridAndSkirtAndEdgeIndicesCache = []; /** * @private */ TerrainProvider.getRegularGridAndSkirtIndicesAndEdgeIndices = function ( width, height ) { //>>includeStart('debug', pragmas.debug); if (width * height >= CesiumMath.FOUR_GIGABYTES) { throw new DeveloperError( "The total number of vertices (width * height) must be less than 4,294,967,296." ); } //>>includeEnd('debug'); var byWidth = regularGridAndSkirtAndEdgeIndicesCache[width]; if (!defined(byWidth)) { regularGridAndSkirtAndEdgeIndicesCache[width] = byWidth = []; } var indicesAndEdges = byWidth[height]; if (!defined(indicesAndEdges)) { var gridVertexCount = width * height; var gridIndexCount = (width - 1) * (height - 1) * 6; var edgeVertexCount = width * 2 + height * 2; var edgeIndexCount = Math.max(0, edgeVertexCount - 4) * 6; var vertexCount = gridVertexCount + edgeVertexCount; var indexCount = gridIndexCount + edgeIndexCount; var edgeIndices = getEdgeIndices(width, height); var westIndicesSouthToNorth = edgeIndices.westIndicesSouthToNorth; var southIndicesEastToWest = edgeIndices.southIndicesEastToWest; var eastIndicesNorthToSouth = edgeIndices.eastIndicesNorthToSouth; var northIndicesWestToEast = edgeIndices.northIndicesWestToEast; var indices = IndexDatatype$1.createTypedArray(vertexCount, indexCount); addRegularGridIndices(width, height, indices, 0); TerrainProvider.addSkirtIndices( westIndicesSouthToNorth, southIndicesEastToWest, eastIndicesNorthToSouth, northIndicesWestToEast, gridVertexCount, indices, gridIndexCount ); indicesAndEdges = byWidth[height] = { indices: indices, westIndicesSouthToNorth: westIndicesSouthToNorth, southIndicesEastToWest: southIndicesEastToWest, eastIndicesNorthToSouth: eastIndicesNorthToSouth, northIndicesWestToEast: northIndicesWestToEast, indexCountWithoutSkirts: gridIndexCount, }; } return indicesAndEdges; }; /** * @private */ TerrainProvider.addSkirtIndices = function ( westIndicesSouthToNorth, southIndicesEastToWest, eastIndicesNorthToSouth, northIndicesWestToEast, vertexCount, indices, offset ) { var vertexIndex = vertexCount; offset = addSkirtIndices( westIndicesSouthToNorth, vertexIndex, indices, offset ); vertexIndex += westIndicesSouthToNorth.length; offset = addSkirtIndices( southIndicesEastToWest, vertexIndex, indices, offset ); vertexIndex += southIndicesEastToWest.length; offset = addSkirtIndices( eastIndicesNorthToSouth, vertexIndex, indices, offset ); vertexIndex += eastIndicesNorthToSouth.length; addSkirtIndices(northIndicesWestToEast, vertexIndex, indices, offset); }; function getEdgeIndices(width, height) { var westIndicesSouthToNorth = new Array(height); var southIndicesEastToWest = new Array(width); var eastIndicesNorthToSouth = new Array(height); var northIndicesWestToEast = new Array(width); var i; for (i = 0; i < width; ++i) { northIndicesWestToEast[i] = i; southIndicesEastToWest[i] = width * height - 1 - i; } for (i = 0; i < height; ++i) { eastIndicesNorthToSouth[i] = (i + 1) * width - 1; westIndicesSouthToNorth[i] = (height - i - 1) * width; } return { westIndicesSouthToNorth: westIndicesSouthToNorth, southIndicesEastToWest: southIndicesEastToWest, eastIndicesNorthToSouth: eastIndicesNorthToSouth, northIndicesWestToEast: northIndicesWestToEast, }; } function addRegularGridIndices(width, height, indices, offset) { var index = 0; for (var j = 0; j < height - 1; ++j) { for (var i = 0; i < width - 1; ++i) { var upperLeft = index; var lowerLeft = upperLeft + width; var lowerRight = lowerLeft + 1; var upperRight = upperLeft + 1; indices[offset++] = upperLeft; indices[offset++] = lowerLeft; indices[offset++] = upperRight; indices[offset++] = upperRight; indices[offset++] = lowerLeft; indices[offset++] = lowerRight; ++index; } ++index; } } function addSkirtIndices(edgeIndices, vertexIndex, indices, offset) { var previousIndex = edgeIndices[0]; var length = edgeIndices.length; for (var i = 1; i < length; ++i) { var index = edgeIndices[i]; indices[offset++] = previousIndex; indices[offset++] = index; indices[offset++] = vertexIndex; indices[offset++] = vertexIndex; indices[offset++] = index; indices[offset++] = vertexIndex + 1; previousIndex = index; ++vertexIndex; } return offset; } /** * Specifies the quality of terrain created from heightmaps. A value of 1.0 will * ensure that adjacent heightmap vertices are separated by no more than * {@link Globe.maximumScreenSpaceError} screen pixels and will probably go very slowly. * A value of 0.5 will cut the estimated level zero geometric error in half, allowing twice the * screen pixels between adjacent heightmap vertices and thus rendering more quickly. * @type {Number} */ TerrainProvider.heightmapTerrainQuality = 0.25; /** * Determines an appropriate geometric error estimate when the geometry comes from a heightmap. * * @param {Ellipsoid} ellipsoid The ellipsoid to which the terrain is attached. * @param {Number} tileImageWidth The width, in pixels, of the heightmap associated with a single tile. * @param {Number} numberOfTilesAtLevelZero The number of tiles in the horizontal direction at tile level zero. * @returns {Number} An estimated geometric error. */ TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap = function ( ellipsoid, tileImageWidth, numberOfTilesAtLevelZero ) { return ( (ellipsoid.maximumRadius * 2 * Math.PI * TerrainProvider.heightmapTerrainQuality) / (tileImageWidth * numberOfTilesAtLevelZero) ); }; /** * Requests the geometry for a given tile. This function should not be called before * {@link TerrainProvider#ready} returns true. The result must include terrain data and * may optionally include a water mask and an indication of which child tiles are available. * @function * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ TerrainProvider.prototype.requestTileGeometry = DeveloperError.throwInstantiationError; /** * Gets the maximum geometric error allowed in a tile at a given level. This function should not be * called before {@link TerrainProvider#ready} returns true. * @function * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ TerrainProvider.prototype.getLevelMaximumGeometricError = DeveloperError.throwInstantiationError; /** * Determines whether data for a tile is available to be loaded. * @function * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported by the terrain provider, otherwise true or false. */ TerrainProvider.prototype.getTileDataAvailable = DeveloperError.throwInstantiationError; /** * Makes sure we load availability data for a tile * @function * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ TerrainProvider.prototype.loadTileDataAvailability = DeveloperError.throwInstantiationError; /** * Terrain data for a single tile where the terrain data is represented as a heightmap. A heightmap * is a rectangular array of heights in row-major order from north to south and west to east. * * @alias HeightmapTerrainData * @constructor * * @param {Object} options Object with the following properties: * @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} options.buffer The buffer containing height data. * @param {Number} options.width The width (longitude direction) of the heightmap, in samples. * @param {Number} options.height The height (latitude direction) of the heightmap, in samples. * @param {Number} [options.childTileMask=15] A bit mask indicating which of this tile's four children exist. * If a child's bit is set, geometry will be requested for that tile as well when it * is needed. If the bit is cleared, the child tile is not requested and geometry is * instead upsampled from the parent. The bit values are as follows: * * * * * * *
Bit PositionBit ValueChild Tile
01Southwest
12Southeast
24Northwest
38Northeast
* @param {Uint8Array} [options.waterMask] The water mask included in this terrain data, if any. A water mask is a square * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. * @param {Object} [options.structure] An object describing the structure of the height data. * @param {Number} [options.structure.heightScale=1.0] The factor by which to multiply height samples in order to obtain * the height above the heightOffset, in meters. The heightOffset is added to the resulting * height after multiplying by the scale. * @param {Number} [options.structure.heightOffset=0.0] The offset to add to the scaled height to obtain the final * height in meters. The offset is added after the height sample is multiplied by the * heightScale. * @param {Number} [options.structure.elementsPerHeight=1] The number of elements in the buffer that make up a single height * sample. This is usually 1, indicating that each element is a separate height sample. If * it is greater than 1, that number of elements together form the height sample, which is * computed according to the structure.elementMultiplier and structure.isBigEndian properties. * @param {Number} [options.structure.stride=1] The number of elements to skip to get from the first element of * one height to the first element of the next height. * @param {Number} [options.structure.elementMultiplier=256.0] The multiplier used to compute the height value when the * stride property is greater than 1. For example, if the stride is 4 and the strideMultiplier * is 256, the height is computed as follows: * `height = buffer[index] + buffer[index + 1] * 256 + buffer[index + 2] * 256 * 256 + buffer[index + 3] * 256 * 256 * 256` * This is assuming that the isBigEndian property is false. If it is true, the order of the * elements is reversed. * @param {Boolean} [options.structure.isBigEndian=false] Indicates endianness of the elements in the buffer when the * stride property is greater than 1. If this property is false, the first element is the * low-order element. If it is true, the first element is the high-order element. * @param {Number} [options.structure.lowestEncodedHeight] The lowest value that can be stored in the height buffer. Any heights that are lower * than this value after encoding with the `heightScale` and `heightOffset` are clamped to this value. For example, if the height * buffer is a `Uint16Array`, this value should be 0 because a `Uint16Array` cannot store negative numbers. If this parameter is * not specified, no minimum value is enforced. * @param {Number} [options.structure.highestEncodedHeight] The highest value that can be stored in the height buffer. Any heights that are higher * than this value after encoding with the `heightScale` and `heightOffset` are clamped to this value. For example, if the height * buffer is a `Uint16Array`, this value should be `256 * 256 - 1` or 65535 because a `Uint16Array` cannot store numbers larger * than 65535. If this parameter is not specified, no maximum value is enforced. * @param {HeightmapEncoding} [options.encoding=HeightmapEncoding.NONE] The encoding that is used on the buffer. * @param {Boolean} [options.createdByUpsampling=false] True if this instance was created by upsampling another instance; * otherwise, false. * * * @example * var buffer = ... * var heightBuffer = new Uint16Array(buffer, 0, that._heightmapWidth * that._heightmapWidth); * var childTileMask = new Uint8Array(buffer, heightBuffer.byteLength, 1)[0]; * var waterMask = new Uint8Array(buffer, heightBuffer.byteLength + 1, buffer.byteLength - heightBuffer.byteLength - 1); * var terrainData = new Cesium.HeightmapTerrainData({ * buffer : heightBuffer, * width : 65, * height : 65, * childTileMask : childTileMask, * waterMask : waterMask * }); * * @see TerrainData * @see QuantizedMeshTerrainData * @see GoogleEarthEnterpriseTerrainData */ function HeightmapTerrainData(options) { //>>includeStart('debug', pragmas.debug); if (!defined(options) || !defined(options.buffer)) { throw new DeveloperError("options.buffer is required."); } if (!defined(options.width)) { throw new DeveloperError("options.width is required."); } if (!defined(options.height)) { throw new DeveloperError("options.height is required."); } //>>includeEnd('debug'); this._buffer = options.buffer; this._width = options.width; this._height = options.height; this._childTileMask = defaultValue(options.childTileMask, 15); this._encoding = defaultValue(options.encoding, HeightmapEncoding$1.NONE); var defaultStructure = HeightmapTessellator.DEFAULT_STRUCTURE; var structure = options.structure; if (!defined(structure)) { structure = defaultStructure; } else if (structure !== defaultStructure) { structure.heightScale = defaultValue( structure.heightScale, defaultStructure.heightScale ); structure.heightOffset = defaultValue( structure.heightOffset, defaultStructure.heightOffset ); structure.elementsPerHeight = defaultValue( structure.elementsPerHeight, defaultStructure.elementsPerHeight ); structure.stride = defaultValue(structure.stride, defaultStructure.stride); structure.elementMultiplier = defaultValue( structure.elementMultiplier, defaultStructure.elementMultiplier ); structure.isBigEndian = defaultValue( structure.isBigEndian, defaultStructure.isBigEndian ); } this._structure = structure; this._createdByUpsampling = defaultValue(options.createdByUpsampling, false); this._waterMask = options.waterMask; this._skirtHeight = undefined; this._bufferType = this._encoding === HeightmapEncoding$1.LERC ? Float32Array : this._buffer.constructor; this._mesh = undefined; } Object.defineProperties(HeightmapTerrainData.prototype, { /** * An array of credits for this tile. * @memberof HeightmapTerrainData.prototype * @type {Credit[]} */ credits: { get: function () { return undefined; }, }, /** * The water mask included in this terrain data, if any. A water mask is a square * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. * @memberof HeightmapTerrainData.prototype * @type {Uint8Array|HTMLImageElement|HTMLCanvasElement} */ waterMask: { get: function () { return this._waterMask; }, }, childTileMask: { get: function () { return this._childTileMask; }, }, }); var createMeshTaskName$2 = "createVerticesFromHeightmap"; var createMeshTaskProcessorNoThrottle$2 = new TaskProcessor(createMeshTaskName$2); var createMeshTaskProcessorThrottle$2 = new TaskProcessor( createMeshTaskName$2, TerrainData.maximumAsynchronousTasks ); /** * Creates a {@link TerrainMesh} from this terrain data. * * @private * * @param {Object} options Object with the following properties: * @param {TilingScheme} options.tilingScheme The tiling scheme to which this tile belongs. * @param {Number} options.x The X coordinate of the tile for which to create the terrain data. * @param {Number} options.y The Y coordinate of the tile for which to create the terrain data. * @param {Number} options.level The level of the tile for which to create the terrain data. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * @param {Boolean} [options.throttle=true] If true, indicates that this operation will need to be retried if too many asynchronous mesh creations are already in progress. * @returns {Promise.|undefined} A promise for the terrain mesh, or undefined if too many * asynchronous mesh creations are already in progress and the operation should * be retried later. */ HeightmapTerrainData.prototype.createMesh = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.tilingScheme", options.tilingScheme); Check.typeOf.number("options.x", options.x); Check.typeOf.number("options.y", options.y); Check.typeOf.number("options.level", options.level); //>>includeEnd('debug'); var tilingScheme = options.tilingScheme; var x = options.x; var y = options.y; var level = options.level; var exaggeration = defaultValue(options.exaggeration, 1.0); var throttle = defaultValue(options.throttle, true); var ellipsoid = tilingScheme.ellipsoid; var nativeRectangle = tilingScheme.tileXYToNativeRectangle(x, y, level); var rectangle = tilingScheme.tileXYToRectangle(x, y, level); // Compute the center of the tile for RTC rendering. var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); var structure = this._structure; var levelZeroMaxError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( ellipsoid, this._width, tilingScheme.getNumberOfXTilesAtLevel(0) ); var thisLevelMaxError = levelZeroMaxError / (1 << level); this._skirtHeight = Math.min(thisLevelMaxError * 4.0, 1000.0); var createMeshTaskProcessor = throttle ? createMeshTaskProcessorThrottle$2 : createMeshTaskProcessorNoThrottle$2; var verticesPromise = createMeshTaskProcessor.scheduleTask({ heightmap: this._buffer, structure: structure, includeWebMercatorT: true, width: this._width, height: this._height, nativeRectangle: nativeRectangle, rectangle: rectangle, relativeToCenter: center, ellipsoid: ellipsoid, skirtHeight: this._skirtHeight, isGeographic: tilingScheme.projection instanceof GeographicProjection, exaggeration: exaggeration, encoding: this._encoding, }); if (!defined(verticesPromise)) { // Postponed return undefined; } var that = this; return when(verticesPromise, function (result) { var indicesAndEdges; if (that._skirtHeight > 0.0) { indicesAndEdges = TerrainProvider.getRegularGridAndSkirtIndicesAndEdgeIndices( result.gridWidth, result.gridHeight ); } else { indicesAndEdges = TerrainProvider.getRegularGridIndicesAndEdgeIndices( result.gridWidth, result.gridHeight ); } var vertexCountWithoutSkirts = result.gridWidth * result.gridHeight; // Clone complex result objects because the transfer from the web worker // has stripped them down to JSON-style objects. that._mesh = new TerrainMesh( center, new Float32Array(result.vertices), indicesAndEdges.indices, indicesAndEdges.indexCountWithoutSkirts, vertexCountWithoutSkirts, result.minimumHeight, result.maximumHeight, BoundingSphere.clone(result.boundingSphere3D), Cartesian3.clone(result.occludeePointInScaledSpace), result.numberOfAttributes, OrientedBoundingBox.clone(result.orientedBoundingBox), TerrainEncoding.clone(result.encoding), exaggeration, indicesAndEdges.westIndicesSouthToNorth, indicesAndEdges.southIndicesEastToWest, indicesAndEdges.eastIndicesNorthToSouth, indicesAndEdges.northIndicesWestToEast ); // Free memory received from server after mesh is created. that._buffer = undefined; return that._mesh; }); }; /** * @param {Object} options Object with the following properties: * @param {TilingScheme} options.tilingScheme The tiling scheme to which this tile belongs. * @param {Number} options.x The X coordinate of the tile for which to create the terrain data. * @param {Number} options.y The Y coordinate of the tile for which to create the terrain data. * @param {Number} options.level The level of the tile for which to create the terrain data. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * * @private */ HeightmapTerrainData.prototype._createMeshSync = function (options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.tilingScheme", options.tilingScheme); Check.typeOf.number("options.x", options.x); Check.typeOf.number("options.y", options.y); Check.typeOf.number("options.level", options.level); //>>includeEnd('debug'); var tilingScheme = options.tilingScheme; var x = options.x; var y = options.y; var level = options.level; var exaggeration = defaultValue(options.exaggeration, 1.0); var ellipsoid = tilingScheme.ellipsoid; var nativeRectangle = tilingScheme.tileXYToNativeRectangle(x, y, level); var rectangle = tilingScheme.tileXYToRectangle(x, y, level); // Compute the center of the tile for RTC rendering. var center = ellipsoid.cartographicToCartesian(Rectangle.center(rectangle)); var structure = this._structure; var levelZeroMaxError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( ellipsoid, this._width, tilingScheme.getNumberOfXTilesAtLevel(0) ); var thisLevelMaxError = levelZeroMaxError / (1 << level); this._skirtHeight = Math.min(thisLevelMaxError * 4.0, 1000.0); var result = HeightmapTessellator.computeVertices({ heightmap: this._buffer, structure: structure, includeWebMercatorT: true, width: this._width, height: this._height, nativeRectangle: nativeRectangle, rectangle: rectangle, relativeToCenter: center, ellipsoid: ellipsoid, skirtHeight: this._skirtHeight, isGeographic: tilingScheme.projection instanceof GeographicProjection, exaggeration: exaggeration, }); // Free memory received from server after mesh is created. this._buffer = undefined; var indicesAndEdges; if (this._skirtHeight > 0.0) { indicesAndEdges = TerrainProvider.getRegularGridAndSkirtIndicesAndEdgeIndices( this._width, this._height ); } else { indicesAndEdges = TerrainProvider.getRegularGridIndicesAndEdgeIndices( this._width, this._height ); } var vertexCountWithoutSkirts = result.gridWidth * result.gridHeight; // No need to clone here (as we do in the async version) because the result // is not coming from a web worker. return new TerrainMesh( center, result.vertices, indicesAndEdges.indices, indicesAndEdges.indexCountWithoutSkirts, vertexCountWithoutSkirts, result.minimumHeight, result.maximumHeight, result.boundingSphere3D, result.occludeePointInScaledSpace, result.encoding.getStride(), result.orientedBoundingBox, result.encoding, exaggeration, indicesAndEdges.westIndicesSouthToNorth, indicesAndEdges.southIndicesEastToWest, indicesAndEdges.eastIndicesNorthToSouth, indicesAndEdges.northIndicesWestToEast ); }; /** * Computes the terrain height at a specified longitude and latitude. * * @param {Rectangle} rectangle The rectangle covered by this terrain data. * @param {Number} longitude The longitude in radians. * @param {Number} latitude The latitude in radians. * @returns {Number} The terrain height at the specified position. If the position * is outside the rectangle, this method will extrapolate the height, which is likely to be wildly * incorrect for positions far outside the rectangle. */ HeightmapTerrainData.prototype.interpolateHeight = function ( rectangle, longitude, latitude ) { var width = this._width; var height = this._height; var structure = this._structure; var stride = structure.stride; var elementsPerHeight = structure.elementsPerHeight; var elementMultiplier = structure.elementMultiplier; var isBigEndian = structure.isBigEndian; var heightOffset = structure.heightOffset; var heightScale = structure.heightScale; var isMeshCreated = defined(this._mesh); var isLERCEncoding = this._encoding === HeightmapEncoding$1.LERC; var isInterpolationImpossible = !isMeshCreated && isLERCEncoding; if (isInterpolationImpossible) { // We can't interpolate using the buffer because it's LERC encoded // so please call createMesh() first and interpolate using the mesh; // as mesh creation will decode the LERC buffer return undefined; } var heightSample; if (isMeshCreated) { var buffer = this._mesh.vertices; var encoding = this._mesh.encoding; var exaggeration = this._mesh.exaggeration; heightSample = interpolateMeshHeight$2( buffer, encoding, heightOffset, heightScale, rectangle, width, height, longitude, latitude, exaggeration ); } else { heightSample = interpolateHeight$2( this._buffer, elementsPerHeight, elementMultiplier, stride, isBigEndian, rectangle, width, height, longitude, latitude ); heightSample = heightSample * heightScale + heightOffset; } return heightSample; }; /** * Upsamples this terrain data for use by a descendant tile. The resulting instance will contain a subset of the * height samples in this instance, interpolated if necessary. * * @param {TilingScheme} tilingScheme The tiling scheme of this terrain data. * @param {Number} thisX The X coordinate of this tile in the tiling scheme. * @param {Number} thisY The Y coordinate of this tile in the tiling scheme. * @param {Number} thisLevel The level of this tile in the tiling scheme. * @param {Number} descendantX The X coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantY The Y coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantLevel The level within the tiling scheme of the descendant tile for which we are upsampling. * @returns {Promise.|undefined} A promise for upsampled heightmap terrain data for the descendant tile, * or undefined if too many asynchronous upsample operations are in progress and the request has been * deferred. */ HeightmapTerrainData.prototype.upsample = function ( tilingScheme, thisX, thisY, thisLevel, descendantX, descendantY, descendantLevel ) { //>>includeStart('debug', pragmas.debug); if (!defined(tilingScheme)) { throw new DeveloperError("tilingScheme is required."); } if (!defined(thisX)) { throw new DeveloperError("thisX is required."); } if (!defined(thisY)) { throw new DeveloperError("thisY is required."); } if (!defined(thisLevel)) { throw new DeveloperError("thisLevel is required."); } if (!defined(descendantX)) { throw new DeveloperError("descendantX is required."); } if (!defined(descendantY)) { throw new DeveloperError("descendantY is required."); } if (!defined(descendantLevel)) { throw new DeveloperError("descendantLevel is required."); } var levelDifference = descendantLevel - thisLevel; if (levelDifference > 1) { throw new DeveloperError( "Upsampling through more than one level at a time is not currently supported." ); } //>>includeEnd('debug'); var meshData = this._mesh; if (!defined(meshData)) { return undefined; } var width = this._width; var height = this._height; var structure = this._structure; var stride = structure.stride; var heights = new this._bufferType(width * height * stride); var buffer = meshData.vertices; var encoding = meshData.encoding; // PERFORMANCE_IDEA: don't recompute these rectangles - the caller already knows them. var sourceRectangle = tilingScheme.tileXYToRectangle(thisX, thisY, thisLevel); var destinationRectangle = tilingScheme.tileXYToRectangle( descendantX, descendantY, descendantLevel ); var heightOffset = structure.heightOffset; var heightScale = structure.heightScale; var exaggeration = meshData.exaggeration; var elementsPerHeight = structure.elementsPerHeight; var elementMultiplier = structure.elementMultiplier; var isBigEndian = structure.isBigEndian; var divisor = Math.pow(elementMultiplier, elementsPerHeight - 1); for (var j = 0; j < height; ++j) { var latitude = CesiumMath.lerp( destinationRectangle.north, destinationRectangle.south, j / (height - 1) ); for (var i = 0; i < width; ++i) { var longitude = CesiumMath.lerp( destinationRectangle.west, destinationRectangle.east, i / (width - 1) ); var heightSample = interpolateMeshHeight$2( buffer, encoding, heightOffset, heightScale, sourceRectangle, width, height, longitude, latitude, exaggeration ); // Use conditionals here instead of Math.min and Math.max so that an undefined // lowestEncodedHeight or highestEncodedHeight has no effect. heightSample = heightSample < structure.lowestEncodedHeight ? structure.lowestEncodedHeight : heightSample; heightSample = heightSample > structure.highestEncodedHeight ? structure.highestEncodedHeight : heightSample; setHeight( heights, elementsPerHeight, elementMultiplier, divisor, stride, isBigEndian, j * width + i, heightSample ); } } return new HeightmapTerrainData({ buffer: heights, width: width, height: height, childTileMask: 0, structure: this._structure, createdByUpsampling: true, }); }; /** * Determines if a given child tile is available, based on the * {@link HeightmapTerrainData.childTileMask}. The given child tile coordinates are assumed * to be one of the four children of this tile. If non-child tile coordinates are * given, the availability of the southeast child tile is returned. * * @param {Number} thisX The tile X coordinate of this (the parent) tile. * @param {Number} thisY The tile Y coordinate of this (the parent) tile. * @param {Number} childX The tile X coordinate of the child tile to check for availability. * @param {Number} childY The tile Y coordinate of the child tile to check for availability. * @returns {Boolean} True if the child tile is available; otherwise, false. */ HeightmapTerrainData.prototype.isChildAvailable = function ( thisX, thisY, childX, childY ) { //>>includeStart('debug', pragmas.debug); if (!defined(thisX)) { throw new DeveloperError("thisX is required."); } if (!defined(thisY)) { throw new DeveloperError("thisY is required."); } if (!defined(childX)) { throw new DeveloperError("childX is required."); } if (!defined(childY)) { throw new DeveloperError("childY is required."); } //>>includeEnd('debug'); var bitNumber = 2; // northwest child if (childX !== thisX * 2) { ++bitNumber; // east child } if (childY !== thisY * 2) { bitNumber -= 2; // south child } return (this._childTileMask & (1 << bitNumber)) !== 0; }; /** * Gets a value indicating whether or not this terrain data was created by upsampling lower resolution * terrain data. If this value is false, the data was obtained from some other source, such * as by downloading it from a remote server. This method should return true for instances * returned from a call to {@link HeightmapTerrainData#upsample}. * * @returns {Boolean} True if this instance was created by upsampling; otherwise, false. */ HeightmapTerrainData.prototype.wasCreatedByUpsampling = function () { return this._createdByUpsampling; }; function interpolateHeight$2( sourceHeights, elementsPerHeight, elementMultiplier, stride, isBigEndian, sourceRectangle, width, height, longitude, latitude ) { var fromWest = ((longitude - sourceRectangle.west) * (width - 1)) / (sourceRectangle.east - sourceRectangle.west); var fromSouth = ((latitude - sourceRectangle.south) * (height - 1)) / (sourceRectangle.north - sourceRectangle.south); var westInteger = fromWest | 0; var eastInteger = westInteger + 1; if (eastInteger >= width) { eastInteger = width - 1; westInteger = width - 2; } var southInteger = fromSouth | 0; var northInteger = southInteger + 1; if (northInteger >= height) { northInteger = height - 1; southInteger = height - 2; } var dx = fromWest - westInteger; var dy = fromSouth - southInteger; southInteger = height - 1 - southInteger; northInteger = height - 1 - northInteger; var southwestHeight = getHeight( sourceHeights, elementsPerHeight, elementMultiplier, stride, isBigEndian, southInteger * width + westInteger ); var southeastHeight = getHeight( sourceHeights, elementsPerHeight, elementMultiplier, stride, isBigEndian, southInteger * width + eastInteger ); var northwestHeight = getHeight( sourceHeights, elementsPerHeight, elementMultiplier, stride, isBigEndian, northInteger * width + westInteger ); var northeastHeight = getHeight( sourceHeights, elementsPerHeight, elementMultiplier, stride, isBigEndian, northInteger * width + eastInteger ); return triangleInterpolateHeight( dx, dy, southwestHeight, southeastHeight, northwestHeight, northeastHeight ); } function interpolateMeshHeight$2( buffer, encoding, heightOffset, heightScale, sourceRectangle, width, height, longitude, latitude, exaggeration ) { // returns a height encoded according to the structure's heightScale and heightOffset. var fromWest = ((longitude - sourceRectangle.west) * (width - 1)) / (sourceRectangle.east - sourceRectangle.west); var fromSouth = ((latitude - sourceRectangle.south) * (height - 1)) / (sourceRectangle.north - sourceRectangle.south); var westInteger = fromWest | 0; var eastInteger = westInteger + 1; if (eastInteger >= width) { eastInteger = width - 1; westInteger = width - 2; } var southInteger = fromSouth | 0; var northInteger = southInteger + 1; if (northInteger >= height) { northInteger = height - 1; southInteger = height - 2; } var dx = fromWest - westInteger; var dy = fromSouth - southInteger; southInteger = height - 1 - southInteger; northInteger = height - 1 - northInteger; var southwestHeight = (encoding.decodeHeight(buffer, southInteger * width + westInteger) / exaggeration - heightOffset) / heightScale; var southeastHeight = (encoding.decodeHeight(buffer, southInteger * width + eastInteger) / exaggeration - heightOffset) / heightScale; var northwestHeight = (encoding.decodeHeight(buffer, northInteger * width + westInteger) / exaggeration - heightOffset) / heightScale; var northeastHeight = (encoding.decodeHeight(buffer, northInteger * width + eastInteger) / exaggeration - heightOffset) / heightScale; return triangleInterpolateHeight( dx, dy, southwestHeight, southeastHeight, northwestHeight, northeastHeight ); } function triangleInterpolateHeight( dX, dY, southwestHeight, southeastHeight, northwestHeight, northeastHeight ) { // The HeightmapTessellator bisects the quad from southwest to northeast. if (dY < dX) { // Lower right triangle return ( southwestHeight + dX * (southeastHeight - southwestHeight) + dY * (northeastHeight - southeastHeight) ); } // Upper left triangle return ( southwestHeight + dX * (northeastHeight - northwestHeight) + dY * (northwestHeight - southwestHeight) ); } function getHeight( heights, elementsPerHeight, elementMultiplier, stride, isBigEndian, index ) { index *= stride; var height = 0; var i; if (isBigEndian) { for (i = 0; i < elementsPerHeight; ++i) { height = height * elementMultiplier + heights[index + i]; } } else { for (i = elementsPerHeight - 1; i >= 0; --i) { height = height * elementMultiplier + heights[index + i]; } } return height; } function setHeight( heights, elementsPerHeight, elementMultiplier, divisor, stride, isBigEndian, index, height ) { index *= stride; var i; if (isBigEndian) { for (i = 0; i < elementsPerHeight - 1; ++i) { heights[index + i] = (height / divisor) | 0; height -= heights[index + i] * divisor; divisor /= elementMultiplier; } } else { for (i = elementsPerHeight - 1; i > 0; --i) { heights[index + i] = (height / divisor) | 0; height -= heights[index + i] * divisor; divisor /= elementMultiplier; } } heights[index + i] = height; } /** * Reports the availability of tiles in a {@link TilingScheme}. * * @alias TileAvailability * @constructor * * @param {TilingScheme} tilingScheme The tiling scheme in which to report availability. * @param {Number} maximumLevel The maximum tile level that is potentially available. */ function TileAvailability(tilingScheme, maximumLevel) { this._tilingScheme = tilingScheme; this._maximumLevel = maximumLevel; this._rootNodes = []; } var rectangleScratch$6 = new Rectangle(); function findNode$1(level, x, y, nodes) { var count = nodes.length; for (var i = 0; i < count; ++i) { var node = nodes[i]; if (node.x === x && node.y === y && node.level === level) { return true; } } return false; } /** * Marks a rectangular range of tiles in a particular level as being available. For best performance, * add your ranges in order of increasing level. * * @param {Number} level The level. * @param {Number} startX The X coordinate of the first available tiles at the level. * @param {Number} startY The Y coordinate of the first available tiles at the level. * @param {Number} endX The X coordinate of the last available tiles at the level. * @param {Number} endY The Y coordinate of the last available tiles at the level. */ TileAvailability.prototype.addAvailableTileRange = function ( level, startX, startY, endX, endY ) { var tilingScheme = this._tilingScheme; var rootNodes = this._rootNodes; if (level === 0) { for (var y = startY; y <= endY; ++y) { for (var x = startX; x <= endX; ++x) { if (!findNode$1(level, x, y, rootNodes)) { rootNodes.push(new QuadtreeNode(tilingScheme, undefined, 0, x, y)); } } } } tilingScheme.tileXYToRectangle(startX, startY, level, rectangleScratch$6); var west = rectangleScratch$6.west; var north = rectangleScratch$6.north; tilingScheme.tileXYToRectangle(endX, endY, level, rectangleScratch$6); var east = rectangleScratch$6.east; var south = rectangleScratch$6.south; var rectangleWithLevel = new RectangleWithLevel( level, west, south, east, north ); for (var i = 0; i < rootNodes.length; ++i) { var rootNode = rootNodes[i]; if (rectanglesOverlap(rootNode.extent, rectangleWithLevel)) { putRectangleInQuadtree(this._maximumLevel, rootNode, rectangleWithLevel); } } }; /** * Determines the level of the most detailed tile covering the position. This function * usually completes in time logarithmic to the number of rectangles added with * {@link TileAvailability#addAvailableTileRange}. * * @param {Cartographic} position The position for which to determine the maximum available level. The height component is ignored. * @return {Number} The level of the most detailed tile covering the position. * @throws {DeveloperError} If position is outside any tile according to the tiling scheme. */ TileAvailability.prototype.computeMaximumLevelAtPosition = function (position) { // Find the root node that contains this position. var node; for (var nodeIndex = 0; nodeIndex < this._rootNodes.length; ++nodeIndex) { var rootNode = this._rootNodes[nodeIndex]; if (rectangleContainsPosition(rootNode.extent, position)) { node = rootNode; break; } } if (!defined(node)) { return -1; } return findMaxLevelFromNode(undefined, node, position); }; var rectanglesScratch = []; var remainingToCoverByLevelScratch = []; var westScratch$1 = new Rectangle(); var eastScratch = new Rectangle(); /** * Finds the most detailed level that is available _everywhere_ within a given rectangle. More detailed * tiles may be available in parts of the rectangle, but not the whole thing. The return value of this * function may be safely passed to {@link sampleTerrain} for any position within the rectangle. This function * usually completes in time logarithmic to the number of rectangles added with * {@link TileAvailability#addAvailableTileRange}. * * @param {Rectangle} rectangle The rectangle. * @return {Number} The best available level for the entire rectangle. */ TileAvailability.prototype.computeBestAvailableLevelOverRectangle = function ( rectangle ) { var rectangles = rectanglesScratch; rectangles.length = 0; if (rectangle.east < rectangle.west) { // Rectangle crosses the IDL, make it two rectangles. rectangles.push( Rectangle.fromRadians( -Math.PI, rectangle.south, rectangle.east, rectangle.north, westScratch$1 ) ); rectangles.push( Rectangle.fromRadians( rectangle.west, rectangle.south, Math.PI, rectangle.north, eastScratch ) ); } else { rectangles.push(rectangle); } var remainingToCoverByLevel = remainingToCoverByLevelScratch; remainingToCoverByLevel.length = 0; var i; for (i = 0; i < this._rootNodes.length; ++i) { updateCoverageWithNode( remainingToCoverByLevel, this._rootNodes[i], rectangles ); } for (i = remainingToCoverByLevel.length - 1; i >= 0; --i) { if ( defined(remainingToCoverByLevel[i]) && remainingToCoverByLevel[i].length === 0 ) { return i; } } return 0; }; var cartographicScratch$4 = new Cartographic(); /** * Determines if a particular tile is available. * @param {Number} level The tile level to check. * @param {Number} x The X coordinate of the tile to check. * @param {Number} y The Y coordinate of the tile to check. * @return {Boolean} True if the tile is available; otherwise, false. */ TileAvailability.prototype.isTileAvailable = function (level, x, y) { // Get the center of the tile and find the maximum level at that position. // Because availability is by tile, if the level is available at that point, it // is sure to be available for the whole tile. We assume that if a tile at level n exists, // then all its parent tiles back to level 0 exist too. This isn't really enforced // anywhere, but Cesium would never load a tile for which this is not true. var rectangle = this._tilingScheme.tileXYToRectangle( x, y, level, rectangleScratch$6 ); Rectangle.center(rectangle, cartographicScratch$4); return this.computeMaximumLevelAtPosition(cartographicScratch$4) >= level; }; /** * Computes a bit mask indicating which of a tile's four children exist. * If a child's bit is set, a tile is available for that child. If it is cleared, * the tile is not available. The bit values are as follows: * * * * * * *
Bit PositionBit ValueChild Tile
01Southwest
12Southeast
24Northwest
38Northeast
* * @param {Number} level The level of the parent tile. * @param {Number} x The X coordinate of the parent tile. * @param {Number} y The Y coordinate of the parent tile. * @return {Number} The bit mask indicating child availability. */ TileAvailability.prototype.computeChildMaskForTile = function (level, x, y) { var childLevel = level + 1; if (childLevel >= this._maximumLevel) { return 0; } var mask = 0; mask |= this.isTileAvailable(childLevel, 2 * x, 2 * y + 1) ? 1 : 0; mask |= this.isTileAvailable(childLevel, 2 * x + 1, 2 * y + 1) ? 2 : 0; mask |= this.isTileAvailable(childLevel, 2 * x, 2 * y) ? 4 : 0; mask |= this.isTileAvailable(childLevel, 2 * x + 1, 2 * y) ? 8 : 0; return mask; }; function QuadtreeNode(tilingScheme, parent, level, x, y) { this.tilingScheme = tilingScheme; this.parent = parent; this.level = level; this.x = x; this.y = y; this.extent = tilingScheme.tileXYToRectangle(x, y, level); this.rectangles = []; this._sw = undefined; this._se = undefined; this._nw = undefined; this._ne = undefined; } Object.defineProperties(QuadtreeNode.prototype, { nw: { get: function () { if (!this._nw) { this._nw = new QuadtreeNode( this.tilingScheme, this, this.level + 1, this.x * 2, this.y * 2 ); } return this._nw; }, }, ne: { get: function () { if (!this._ne) { this._ne = new QuadtreeNode( this.tilingScheme, this, this.level + 1, this.x * 2 + 1, this.y * 2 ); } return this._ne; }, }, sw: { get: function () { if (!this._sw) { this._sw = new QuadtreeNode( this.tilingScheme, this, this.level + 1, this.x * 2, this.y * 2 + 1 ); } return this._sw; }, }, se: { get: function () { if (!this._se) { this._se = new QuadtreeNode( this.tilingScheme, this, this.level + 1, this.x * 2 + 1, this.y * 2 + 1 ); } return this._se; }, }, }); function RectangleWithLevel(level, west, south, east, north) { this.level = level; this.west = west; this.south = south; this.east = east; this.north = north; } function rectanglesOverlap(rectangle1, rectangle2) { var west = Math.max(rectangle1.west, rectangle2.west); var south = Math.max(rectangle1.south, rectangle2.south); var east = Math.min(rectangle1.east, rectangle2.east); var north = Math.min(rectangle1.north, rectangle2.north); return south < north && west < east; } function putRectangleInQuadtree(maxDepth, node, rectangle) { while (node.level < maxDepth) { if (rectangleFullyContainsRectangle(node.nw.extent, rectangle)) { node = node.nw; } else if (rectangleFullyContainsRectangle(node.ne.extent, rectangle)) { node = node.ne; } else if (rectangleFullyContainsRectangle(node.sw.extent, rectangle)) { node = node.sw; } else if (rectangleFullyContainsRectangle(node.se.extent, rectangle)) { node = node.se; } else { break; } } if ( node.rectangles.length === 0 || node.rectangles[node.rectangles.length - 1].level <= rectangle.level ) { node.rectangles.push(rectangle); } else { // Maintain ordering by level when inserting. var index = binarySearch( node.rectangles, rectangle.level, rectangleLevelComparator ); if (index < 0) { index = ~index; } node.rectangles.splice(index, 0, rectangle); } } function rectangleLevelComparator(a, b) { return a.level - b; } function rectangleFullyContainsRectangle(potentialContainer, rectangleToTest) { return ( rectangleToTest.west >= potentialContainer.west && rectangleToTest.east <= potentialContainer.east && rectangleToTest.south >= potentialContainer.south && rectangleToTest.north <= potentialContainer.north ); } function rectangleContainsPosition(potentialContainer, positionToTest) { return ( positionToTest.longitude >= potentialContainer.west && positionToTest.longitude <= potentialContainer.east && positionToTest.latitude >= potentialContainer.south && positionToTest.latitude <= potentialContainer.north ); } function findMaxLevelFromNode(stopNode, node, position) { var maxLevel = 0; // Find the deepest quadtree node containing this point. var found = false; while (!found) { var nw = node._nw && rectangleContainsPosition(node._nw.extent, position); var ne = node._ne && rectangleContainsPosition(node._ne.extent, position); var sw = node._sw && rectangleContainsPosition(node._sw.extent, position); var se = node._se && rectangleContainsPosition(node._se.extent, position); // The common scenario is that the point is in only one quadrant and we can simply // iterate down the tree. But if the point is on a boundary between tiles, it is // in multiple tiles and we need to check all of them, so use recursion. if (nw + ne + sw + se > 1) { if (nw) { maxLevel = Math.max( maxLevel, findMaxLevelFromNode(node, node._nw, position) ); } if (ne) { maxLevel = Math.max( maxLevel, findMaxLevelFromNode(node, node._ne, position) ); } if (sw) { maxLevel = Math.max( maxLevel, findMaxLevelFromNode(node, node._sw, position) ); } if (se) { maxLevel = Math.max( maxLevel, findMaxLevelFromNode(node, node._se, position) ); } break; } else if (nw) { node = node._nw; } else if (ne) { node = node._ne; } else if (sw) { node = node._sw; } else if (se) { node = node._se; } else { found = true; } } // Work up the tree until we find a rectangle that contains this point. while (node !== stopNode) { var rectangles = node.rectangles; // Rectangles are sorted by level, lowest first. for ( var i = rectangles.length - 1; i >= 0 && rectangles[i].level > maxLevel; --i ) { var rectangle = rectangles[i]; if (rectangleContainsPosition(rectangle, position)) { maxLevel = rectangle.level; } } node = node.parent; } return maxLevel; } function updateCoverageWithNode( remainingToCoverByLevel, node, rectanglesToCover ) { if (!node) { return; } var i; var anyOverlap = false; for (i = 0; i < rectanglesToCover.length; ++i) { anyOverlap = anyOverlap || rectanglesOverlap(node.extent, rectanglesToCover[i]); } if (!anyOverlap) { // This node is not applicable to the rectangle(s). return; } var rectangles = node.rectangles; for (i = 0; i < rectangles.length; ++i) { var rectangle = rectangles[i]; if (!remainingToCoverByLevel[rectangle.level]) { remainingToCoverByLevel[rectangle.level] = rectanglesToCover; } remainingToCoverByLevel[rectangle.level] = subtractRectangle( remainingToCoverByLevel[rectangle.level], rectangle ); } // Update with child nodes. updateCoverageWithNode(remainingToCoverByLevel, node._nw, rectanglesToCover); updateCoverageWithNode(remainingToCoverByLevel, node._ne, rectanglesToCover); updateCoverageWithNode(remainingToCoverByLevel, node._sw, rectanglesToCover); updateCoverageWithNode(remainingToCoverByLevel, node._se, rectanglesToCover); } function subtractRectangle(rectangleList, rectangleToSubtract) { var result = []; for (var i = 0; i < rectangleList.length; ++i) { var rectangle = rectangleList[i]; if (!rectanglesOverlap(rectangle, rectangleToSubtract)) { // Disjoint rectangles. Original rectangle is unmodified. result.push(rectangle); } else { // rectangleToSubtract partially or completely overlaps rectangle. if (rectangle.west < rectangleToSubtract.west) { result.push( new Rectangle( rectangle.west, rectangle.south, rectangleToSubtract.west, rectangle.north ) ); } if (rectangle.east > rectangleToSubtract.east) { result.push( new Rectangle( rectangleToSubtract.east, rectangle.south, rectangle.east, rectangle.north ) ); } if (rectangle.south < rectangleToSubtract.south) { result.push( new Rectangle( Math.max(rectangleToSubtract.west, rectangle.west), rectangle.south, Math.min(rectangleToSubtract.east, rectangle.east), rectangleToSubtract.south ) ); } if (rectangle.north > rectangleToSubtract.north) { result.push( new Rectangle( Math.max(rectangleToSubtract.west, rectangle.west), rectangleToSubtract.north, Math.min(rectangleToSubtract.east, rectangle.east), rectangle.north ) ); } } } return result; } /** * Formats an error object into a String. If available, uses name, message, and stack * properties, otherwise, falls back on toString(). * * @function * * @param {*} object The item to find in the array. * @returns {String} A string containing the formatted error. */ function formatError(object) { var result; var name = object.name; var message = object.message; if (defined(name) && defined(message)) { result = name + ": " + message; } else { result = object.toString(); } var stack = object.stack; if (defined(stack)) { result += "\n" + stack; } return result; } /** * Provides details about an error that occurred in an {@link ImageryProvider} or a {@link TerrainProvider}. * * @alias TileProviderError * @constructor * * @param {ImageryProvider|TerrainProvider} provider The imagery or terrain provider that experienced the error. * @param {String} message A message describing the error. * @param {Number} [x] The X coordinate of the tile that experienced the error, or undefined if the error * is not specific to a particular tile. * @param {Number} [y] The Y coordinate of the tile that experienced the error, or undefined if the error * is not specific to a particular tile. * @param {Number} [level] The level of the tile that experienced the error, or undefined if the error * is not specific to a particular tile. * @param {Number} [timesRetried=0] The number of times this operation has been retried. * @param {Error} [error] The error or exception that occurred, if any. */ function TileProviderError( provider, message, x, y, level, timesRetried, error ) { /** * The {@link ImageryProvider} or {@link TerrainProvider} that experienced the error. * @type {ImageryProvider|TerrainProvider} */ this.provider = provider; /** * The message describing the error. * @type {String} */ this.message = message; /** * The X coordinate of the tile that experienced the error. If the error is not specific * to a particular tile, this property will be undefined. * @type {Number} */ this.x = x; /** * The Y coordinate of the tile that experienced the error. If the error is not specific * to a particular tile, this property will be undefined. * @type {Number} */ this.y = y; /** * The level-of-detail of the tile that experienced the error. If the error is not specific * to a particular tile, this property will be undefined. * @type {Number} */ this.level = level; /** * The number of times this operation has been retried. * @type {Number} * @default 0 */ this.timesRetried = defaultValue(timesRetried, 0); /** * True if the failed operation should be retried; otherwise, false. The imagery or terrain provider * will set the initial value of this property before raising the event, but any listeners * can change it. The value after the last listener is invoked will be acted upon. * @type {Boolean} * @default false */ this.retry = false; /** * The error or exception that occurred, if any. * @type {Error} */ this.error = error; } /** * Handles an error in an {@link ImageryProvider} or {@link TerrainProvider} by raising an event if it has any listeners, or by * logging the error to the console if the event has no listeners. This method also tracks the number * of times the operation has been retried and will automatically retry if requested to do so by the * event listeners. * * @param {TileProviderError} previousError The error instance returned by this function the last * time it was called for this error, or undefined if this is the first time this error has * occurred. * @param {ImageryProvider|TerrainProvider} provider The imagery or terrain provider that encountered the error. * @param {Event} event The event to raise to inform listeners of the error. * @param {String} message The message describing the error. * @param {Number} x The X coordinate of the tile that experienced the error, or undefined if the * error is not specific to a particular tile. * @param {Number} y The Y coordinate of the tile that experienced the error, or undefined if the * error is not specific to a particular tile. * @param {Number} level The level-of-detail of the tile that experienced the error, or undefined if the * error is not specific to a particular tile. * @param {TileProviderError.RetryFunction} retryFunction The function to call to retry the operation. If undefined, the * operation will not be retried. * @param {Error} [errorDetails] The error or exception that occurred, if any. * @returns {TileProviderError} The error instance that was passed to the event listeners and that * should be passed to this function the next time it is called for the same error in order * to track retry counts. */ TileProviderError.handleError = function ( previousError, provider, event, message, x, y, level, retryFunction, errorDetails ) { var error = previousError; if (!defined(previousError)) { error = new TileProviderError( provider, message, x, y, level, 0, errorDetails ); } else { error.provider = provider; error.message = message; error.x = x; error.y = y; error.level = level; error.retry = false; error.error = errorDetails; ++error.timesRetried; } if (event.numberOfListeners > 0) { event.raiseEvent(error); } else { console.log( 'An error occurred in "' + provider.constructor.name + '": ' + formatError(message) ); } if (error.retry && defined(retryFunction)) { retryFunction(); } return error; }; /** * Handles success of an operation by resetting the retry count of a previous error, if any. This way, * if the error occurs again in the future, the listeners will be informed that it has not yet been retried. * * @param {TileProviderError} previousError The previous error, or undefined if this operation has * not previously resulted in an error. */ TileProviderError.handleSuccess = function (previousError) { if (defined(previousError)) { previousError.timesRetried = -1; } }; /** * A tiling scheme for geometry referenced to a {@link WebMercatorProjection}, EPSG:3857. This is * the tiling scheme used by Google Maps, Microsoft Bing Maps, and most of ESRI ArcGIS Online. * * @alias WebMercatorTilingScheme * @constructor * * @param {Object} [options] Object with the following properties: * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid whose surface is being tiled. Defaults to * the WGS84 ellipsoid. * @param {Number} [options.numberOfLevelZeroTilesX=1] The number of tiles in the X direction at level zero of * the tile tree. * @param {Number} [options.numberOfLevelZeroTilesY=1] The number of tiles in the Y direction at level zero of * the tile tree. * @param {Cartesian2} [options.rectangleSouthwestInMeters] The southwest corner of the rectangle covered by the * tiling scheme, in meters. If this parameter or rectangleNortheastInMeters is not specified, the entire * globe is covered in the longitude direction and an equal distance is covered in the latitude * direction, resulting in a square projection. * @param {Cartesian2} [options.rectangleNortheastInMeters] The northeast corner of the rectangle covered by the * tiling scheme, in meters. If this parameter or rectangleSouthwestInMeters is not specified, the entire * globe is covered in the longitude direction and an equal distance is covered in the latitude * direction, resulting in a square projection. */ function WebMercatorTilingScheme(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._numberOfLevelZeroTilesX = defaultValue( options.numberOfLevelZeroTilesX, 1 ); this._numberOfLevelZeroTilesY = defaultValue( options.numberOfLevelZeroTilesY, 1 ); this._projection = new WebMercatorProjection(this._ellipsoid); if ( defined(options.rectangleSouthwestInMeters) && defined(options.rectangleNortheastInMeters) ) { this._rectangleSouthwestInMeters = options.rectangleSouthwestInMeters; this._rectangleNortheastInMeters = options.rectangleNortheastInMeters; } else { var semimajorAxisTimesPi = this._ellipsoid.maximumRadius * Math.PI; this._rectangleSouthwestInMeters = new Cartesian2( -semimajorAxisTimesPi, -semimajorAxisTimesPi ); this._rectangleNortheastInMeters = new Cartesian2( semimajorAxisTimesPi, semimajorAxisTimesPi ); } var southwest = this._projection.unproject(this._rectangleSouthwestInMeters); var northeast = this._projection.unproject(this._rectangleNortheastInMeters); this._rectangle = new Rectangle( southwest.longitude, southwest.latitude, northeast.longitude, northeast.latitude ); } Object.defineProperties(WebMercatorTilingScheme.prototype, { /** * Gets the ellipsoid that is tiled by this tiling scheme. * @memberof WebMercatorTilingScheme.prototype * @type {Ellipsoid} */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets the rectangle, in radians, covered by this tiling scheme. * @memberof WebMercatorTilingScheme.prototype * @type {Rectangle} */ rectangle: { get: function () { return this._rectangle; }, }, /** * Gets the map projection used by this tiling scheme. * @memberof WebMercatorTilingScheme.prototype * @type {MapProjection} */ projection: { get: function () { return this._projection; }, }, }); /** * Gets the total number of tiles in the X direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the X direction at the given level. */ WebMercatorTilingScheme.prototype.getNumberOfXTilesAtLevel = function (level) { return this._numberOfLevelZeroTilesX << level; }; /** * Gets the total number of tiles in the Y direction at a specified level-of-detail. * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the Y direction at the given level. */ WebMercatorTilingScheme.prototype.getNumberOfYTilesAtLevel = function (level) { return this._numberOfLevelZeroTilesY << level; }; /** * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * * @param {Rectangle} rectangle The rectangle to transform. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result' * is undefined. */ WebMercatorTilingScheme.prototype.rectangleToNativeRectangle = function ( rectangle, result ) { var projection = this._projection; var southwest = projection.project(Rectangle.southwest(rectangle)); var northeast = projection.project(Rectangle.northeast(rectangle)); if (!defined(result)) { return new Rectangle(southwest.x, southwest.y, northeast.x, northeast.y); } result.west = southwest.x; result.south = southwest.y; result.east = northeast.x; result.north = northeast.y; return result; }; /** * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ WebMercatorTilingScheme.prototype.tileXYToNativeRectangle = function ( x, y, level, result ) { var xTiles = this.getNumberOfXTilesAtLevel(level); var yTiles = this.getNumberOfYTilesAtLevel(level); var xTileWidth = (this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x) / xTiles; var west = this._rectangleSouthwestInMeters.x + x * xTileWidth; var east = this._rectangleSouthwestInMeters.x + (x + 1) * xTileWidth; var yTileHeight = (this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y) / yTiles; var north = this._rectangleNortheastInMeters.y - y * yTileHeight; var south = this._rectangleNortheastInMeters.y - (y + 1) * yTileHeight; if (!defined(result)) { return new Rectangle(west, south, east, north); } result.west = west; result.south = south; result.east = east; result.north = north; return result; }; /** * Converts tile x, y coordinates and level to a cartographic rectangle in radians. * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ WebMercatorTilingScheme.prototype.tileXYToRectangle = function ( x, y, level, result ) { var nativeRectangle = this.tileXYToNativeRectangle(x, y, level, result); var projection = this._projection; var southwest = projection.unproject( new Cartesian2(nativeRectangle.west, nativeRectangle.south) ); var northeast = projection.unproject( new Cartesian2(nativeRectangle.east, nativeRectangle.north) ); nativeRectangle.west = southwest.longitude; nativeRectangle.south = southwest.latitude; nativeRectangle.east = northeast.longitude; nativeRectangle.north = northeast.latitude; return nativeRectangle; }; /** * Calculates the tile x, y coordinates of the tile containing * a given cartographic position. * * @param {Cartographic} position The position. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates * if 'result' is undefined. */ WebMercatorTilingScheme.prototype.positionToTileXY = function ( position, level, result ) { var rectangle = this._rectangle; if (!Rectangle.contains(rectangle, position)) { // outside the bounds of the tiling scheme return undefined; } var xTiles = this.getNumberOfXTilesAtLevel(level); var yTiles = this.getNumberOfYTilesAtLevel(level); var overallWidth = this._rectangleNortheastInMeters.x - this._rectangleSouthwestInMeters.x; var xTileWidth = overallWidth / xTiles; var overallHeight = this._rectangleNortheastInMeters.y - this._rectangleSouthwestInMeters.y; var yTileHeight = overallHeight / yTiles; var projection = this._projection; var webMercatorPosition = projection.project(position); var distanceFromWest = webMercatorPosition.x - this._rectangleSouthwestInMeters.x; var distanceFromNorth = this._rectangleNortheastInMeters.y - webMercatorPosition.y; var xTileCoordinate = (distanceFromWest / xTileWidth) | 0; if (xTileCoordinate >= xTiles) { xTileCoordinate = xTiles - 1; } var yTileCoordinate = (distanceFromNorth / yTileHeight) | 0; if (yTileCoordinate >= yTiles) { yTileCoordinate = yTiles - 1; } if (!defined(result)) { return new Cartesian2(xTileCoordinate, yTileCoordinate); } result.x = xTileCoordinate; result.y = yTileCoordinate; return result; }; var ALL_CHILDREN = 15; /** * A {@link TerrainProvider} that produces terrain geometry by tessellating height maps * retrieved from Elevation Tiles of an an ArcGIS ImageService. * * @alias ArcGISTiledElevationTerrainProvider * @constructor * * @param {Object} options Object with the following properties: * @param {Resource|String|Promise|Promise} options.url The URL of the ArcGIS ImageServer service. * @param {String} [options.token] The authorization token to use to connect to the service. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If the tilingScheme is specified, * this parameter is ignored and the tiling scheme's ellipsoid is used instead. * If neither parameter is specified, the WGS84 ellipsoid is used. * * @example * var terrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({ * url : 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer', * token : 'KED1aF_I4UzXOHy3BnhwyBHU4l5oY6rO6walkmHoYqGp4XyIWUd5YZUC1ZrLAzvV40pR6gBXQayh0eFA8m6vPg..' * }); * viewer.terrainProvider = terrainProvider; * * @see TerrainProvider */ function ArcGISTiledElevationTerrainProvider(options) { //>>includeStart('debug', pragmas.debug); if (!defined(options) || !defined(options.url)) { throw new DeveloperError("options.url is required."); } //>>includeEnd('debug'); this._resource = undefined; this._credit = undefined; this._tilingScheme = undefined; this._levelZeroMaximumGeometricError = undefined; this._maxLevel = undefined; this._terrainDataStructure = undefined; this._ready = false; this._width = undefined; this._height = undefined; this._encoding = undefined; var token = options.token; this._hasAvailability = false; this._tilesAvailable = undefined; this._tilesAvailablityLoaded = undefined; this._availableCache = {}; var that = this; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._readyPromise = when(options.url) .then(function (url) { var resource = Resource.createIfNeeded(url); resource.appendForwardSlash(); if (defined(token)) { resource = resource.getDerivedResource({ queryParameters: { token: token, }, }); } that._resource = resource; var metadataResource = resource.getDerivedResource({ queryParameters: { f: "pjson", }, }); return metadataResource.fetchJson(); }) .then(function (metadata) { var copyrightText = metadata.copyrightText; if (defined(copyrightText)) { that._credit = new Credit(copyrightText); } var spatialReference = metadata.spatialReference; var wkid = defaultValue( spatialReference.latestWkid, spatialReference.wkid ); var extent = metadata.extent; var tilingSchemeOptions = { ellipsoid: ellipsoid, }; if (wkid === 4326) { tilingSchemeOptions.rectangle = Rectangle.fromDegrees( extent.xmin, extent.ymin, extent.xmax, extent.ymax ); that._tilingScheme = new GeographicTilingScheme(tilingSchemeOptions); } else if (wkid === 3857) { tilingSchemeOptions.rectangleSouthwestInMeters = new Cartesian2( extent.xmin, extent.ymin ); tilingSchemeOptions.rectangleNortheastInMeters = new Cartesian2( extent.xmax, extent.ymax ); that._tilingScheme = new WebMercatorTilingScheme(tilingSchemeOptions); } else { return when.reject(new RuntimeError("Invalid spatial reference")); } var tileInfo = metadata.tileInfo; if (!defined(tileInfo)) { return when.reject(new RuntimeError("tileInfo is required")); } that._width = tileInfo.rows + 1; that._height = tileInfo.cols + 1; that._encoding = tileInfo.format === "LERC" ? HeightmapEncoding$1.LERC : HeightmapEncoding$1.NONE; that._lodCount = tileInfo.lods.length - 1; var hasAvailability = (that._hasAvailability = metadata.capabilities.indexOf("Tilemap") !== -1); if (hasAvailability) { that._tilesAvailable = new TileAvailability( that._tilingScheme, that._lodCount ); that._tilesAvailable.addAvailableTileRange( 0, 0, 0, that._tilingScheme.getNumberOfXTilesAtLevel(0), that._tilingScheme.getNumberOfYTilesAtLevel(0) ); that._tilesAvailablityLoaded = new TileAvailability( that._tilingScheme, that._lodCount ); } that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( that._tilingScheme.ellipsoid, that._width, that._tilingScheme.getNumberOfXTilesAtLevel(0) ); if (metadata.bandCount > 1) { console.log( "ArcGISTiledElevationTerrainProvider: Terrain data has more than 1 band. Using the first one." ); } that._terrainDataStructure = { elementMultiplier: 1.0, lowestEncodedHeight: metadata.minValues[0], highestEncodedHeight: metadata.maxValues[0], }; that._ready = true; return true; }) .otherwise(function (error) { var message = "An error occurred while accessing " + that._resource.url + "."; TileProviderError.handleError(undefined, that, that._errorEvent, message); return when.reject(error); }); this._errorEvent = new Event(); } Object.defineProperties(ArcGISTiledElevationTerrainProvider.prototype, { /** * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Event} */ errorEvent: { get: function () { return this._errorEvent; }, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Credit} */ credit: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "credit must not be called before ready returns true." ); } //>>includeEnd('debug'); return this._credit; }, }, /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {GeographicTilingScheme} */ tilingScheme: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "tilingScheme must not be called before ready returns true." ); } //>>includeEnd('debug'); return this._tilingScheme; }, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Boolean} */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise; }, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Boolean} */ hasWaterMask: { get: function () { return false; }, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {Boolean} */ hasVertexNormals: { get: function () { return false; }, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link TerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. * @memberof ArcGISTiledElevationTerrainProvider.prototype * @type {TileAvailability} */ availability: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "availability must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); return this._tilesAvailable; }, }, }); /** * Requests the geometry for a given tile. This function should not be called before * {@link ArcGISTiledElevationTerrainProvider#ready} returns true. The result includes terrain * data and indicates that all child tiles are available. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ ArcGISTiledElevationTerrainProvider.prototype.requestTileGeometry = function ( x, y, level, request ) { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "requestTileGeometry must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); var tileResource = this._resource.getDerivedResource({ url: "tile/" + level + "/" + y + "/" + x, request: request, }); var hasAvailability = this._hasAvailability; var availabilityPromise = when.resolve(true); var availabilityRequest; if ( hasAvailability && !defined(isTileAvailable(this, level + 1, x * 2, y * 2)) ) { // We need to load child availability var availabilityResult = requestAvailability(this, level + 1, x * 2, y * 2); availabilityPromise = availabilityResult.promise; availabilityRequest = availabilityResult.request; } var promise = tileResource.fetchArrayBuffer(); if (!defined(promise) || !defined(availabilityPromise)) { return undefined; } var that = this; var tilesAvailable = this._tilesAvailable; return when .join(promise, availabilityPromise) .then(function (result) { return new HeightmapTerrainData({ buffer: result[0], width: that._width, height: that._height, childTileMask: hasAvailability ? tilesAvailable.computeChildMaskForTile(level, x, y) : ALL_CHILDREN, structure: that._terrainDataStructure, encoding: that._encoding, }); }) .otherwise(function (error) { if ( defined(availabilityRequest) && availabilityRequest.state === RequestState$1.CANCELLED ) { request.cancel(); // Don't reject the promise till the request is actually cancelled // Otherwise it will think the request failed, but it didn't. return request.deferred.promise.always(function () { request.state = RequestState$1.CANCELLED; return when.reject(error); }); } return when.reject(error); }); }; function isTileAvailable(that, level, x, y) { if (!that._hasAvailability) { return undefined; } var tilesAvailablityLoaded = that._tilesAvailablityLoaded; var tilesAvailable = that._tilesAvailable; if (level > that._lodCount) { return false; } // Check if tiles are known to be available if (tilesAvailable.isTileAvailable(level, x, y)) { return true; } // or to not be available if (tilesAvailablityLoaded.isTileAvailable(level, x, y)) { return false; } return undefined; } /** * Gets the maximum geometric error allowed in a tile at a given level. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ ArcGISTiledElevationTerrainProvider.prototype.getLevelMaximumGeometricError = function ( level ) { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "getLevelMaximumGeometricError must not be called before ready returns true." ); } //>>includeEnd('debug'); return this._levelZeroMaximumGeometricError / (1 << level); }; /** * Determines whether data for a tile is available to be loaded. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported, otherwise true or false. */ ArcGISTiledElevationTerrainProvider.prototype.getTileDataAvailable = function ( x, y, level ) { if (!this._hasAvailability) { return undefined; } var result = isTileAvailable(this, level, x, y); if (defined(result)) { return result; } requestAvailability(this, level, x, y); return undefined; }; /** * Makes sure we load availability data for a tile * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ ArcGISTiledElevationTerrainProvider.prototype.loadTileDataAvailability = function ( x, y, level ) { return undefined; }; function findRange(origin, width, height, data) { var endCol = width - 1; var endRow = height - 1; var value = data[origin.y * width + origin.x]; var endingIndices = []; var range = { startX: origin.x, startY: origin.y, endX: 0, endY: 0, }; var corner = new Cartesian2(origin.x + 1, origin.y + 1); var doneX = false; var doneY = false; while (!(doneX && doneY)) { // We want to use the original value when checking Y, // so get it before it possibly gets incremented var endX = corner.x; // If we no longer move in the Y direction we need to check the corner tile in X pass var endY = doneY ? corner.y + 1 : corner.y; // Check X range if (!doneX) { for (var y = origin.y; y < endY; ++y) { if (data[y * width + corner.x] !== value) { doneX = true; break; } } if (doneX) { endingIndices.push(new Cartesian2(corner.x, origin.y)); // Use the last good column so we can continue with Y --corner.x; --endX; range.endX = corner.x; } else if (corner.x === endCol) { range.endX = corner.x; doneX = true; } else { ++corner.x; } } // Check Y range - The corner tile is checked here if (!doneY) { var col = corner.y * width; for (var x = origin.x; x <= endX; ++x) { if (data[col + x] !== value) { doneY = true; break; } } if (doneY) { endingIndices.push(new Cartesian2(origin.x, corner.y)); // Use the last good row so we can continue with X --corner.y; range.endY = corner.y; } else if (corner.y === endRow) { range.endY = corner.y; doneY = true; } else { ++corner.y; } } } return { endingIndices: endingIndices, range: range, value: value, }; } function computeAvailability(x, y, width, height, data) { var ranges = []; var singleValue = data.every(function (val) { return val === data[0]; }); if (singleValue) { if (data[0] === 1) { ranges.push({ startX: x, startY: y, endX: x + width - 1, endY: y + height - 1, }); } return ranges; } var positions = [new Cartesian2(0, 0)]; while (positions.length > 0) { var origin = positions.pop(); var result = findRange(origin, width, height, data); if (result.value === 1) { // Convert range into the array into global tile coordinates var range = result.range; range.startX += x; range.endX += x; range.startY += y; range.endY += y; ranges.push(range); } var endingIndices = result.endingIndices; if (endingIndices.length > 0) { positions = positions.concat(endingIndices); } } return ranges; } function requestAvailability(that, level, x, y) { if (!that._hasAvailability) { return {}; } // Fetch 128x128 availability list, so we make the minimum amount of requests var xOffset = Math.floor(x / 128) * 128; var yOffset = Math.floor(y / 128) * 128; var dim = Math.min(1 << level, 128); var url = "tilemap/" + level + "/" + yOffset + "/" + xOffset + "/" + dim + "/" + dim; var availableCache = that._availableCache; if (defined(availableCache[url])) { return availableCache[url]; } var request = new Request({ throttle: false, throttleByServer: true, type: RequestType$1.TERRAIN, }); var tilemapResource = that._resource.getDerivedResource({ url: url, request: request, }); var promise = tilemapResource.fetchJson(); if (!defined(promise)) { return {}; } promise = promise.then(function (result) { var available = computeAvailability( xOffset, yOffset, dim, dim, result.data ); // Mark whole area as having availability loaded that._tilesAvailablityLoaded.addAvailableTileRange( level, xOffset, yOffset, xOffset + dim, yOffset + dim ); var tilesAvailable = that._tilesAvailable; for (var i = 0; i < available.length; ++i) { var range = available[i]; tilesAvailable.addAvailableTileRange( level, range.startX, range.startY, range.endX, range.endY ); } // Conveniently return availability of original tile return isTileAvailable(that, level, x, y); }); availableCache[url] = { promise: promise, request: request, }; promise = promise.always(function (result) { delete availableCache[url]; return result; }); return { promise: promise, request: request, }; } /** * ArcType defines the path that should be taken connecting vertices. * * @enum {Number} */ var ArcType = { /** * Straight line that does not conform to the surface of the ellipsoid. * * @type {Number} * @constant */ NONE: 0, /** * Follow geodesic path. * * @type {Number} * @constant */ GEODESIC: 1, /** * Follow rhumb or loxodrome path. * * @type {Number} * @constant */ RHUMB: 2, }; var ArcType$1 = Object.freeze(ArcType); /** * A collection of key-value pairs that is stored as a hash for easy * lookup but also provides an array for fast iteration. * @alias AssociativeArray * @constructor */ function AssociativeArray() { this._array = []; this._hash = {}; } Object.defineProperties(AssociativeArray.prototype, { /** * Gets the number of items in the collection. * @memberof AssociativeArray.prototype * * @type {Number} */ length: { get: function () { return this._array.length; }, }, /** * Gets an unordered array of all values in the collection. * This is a live array that will automatically reflect the values in the collection, * it should not be modified directly. * @memberof AssociativeArray.prototype * * @type {Array} */ values: { get: function () { return this._array; }, }, }); /** * Determines if the provided key is in the array. * * @param {String|Number} key The key to check. * @returns {Boolean} true if the key is in the array, false otherwise. */ AssociativeArray.prototype.contains = function (key) { //>>includeStart('debug', pragmas.debug); if (typeof key !== "string" && typeof key !== "number") { throw new DeveloperError("key is required to be a string or number."); } //>>includeEnd('debug'); return defined(this._hash[key]); }; /** * Associates the provided key with the provided value. If the key already * exists, it is overwritten with the new value. * * @param {String|Number} key A unique identifier. * @param {*} value The value to associate with the provided key. */ AssociativeArray.prototype.set = function (key, value) { //>>includeStart('debug', pragmas.debug); if (typeof key !== "string" && typeof key !== "number") { throw new DeveloperError("key is required to be a string or number."); } //>>includeEnd('debug'); var oldValue = this._hash[key]; if (value !== oldValue) { this.remove(key); this._hash[key] = value; this._array.push(value); } }; /** * Retrieves the value associated with the provided key. * * @param {String|Number} key The key whose value is to be retrieved. * @returns {*} The associated value, or undefined if the key does not exist in the collection. */ AssociativeArray.prototype.get = function (key) { //>>includeStart('debug', pragmas.debug); if (typeof key !== "string" && typeof key !== "number") { throw new DeveloperError("key is required to be a string or number."); } //>>includeEnd('debug'); return this._hash[key]; }; /** * Removes a key-value pair from the collection. * * @param {String|Number} key The key to be removed. * @returns {Boolean} True if it was removed, false if the key was not in the collection. */ AssociativeArray.prototype.remove = function (key) { //>>includeStart('debug', pragmas.debug); if (defined(key) && typeof key !== "string" && typeof key !== "number") { throw new DeveloperError("key is required to be a string or number."); } //>>includeEnd('debug'); var value = this._hash[key]; var hasValue = defined(value); if (hasValue) { var array = this._array; array.splice(array.indexOf(value), 1); delete this._hash[key]; } return hasValue; }; /** * Clears the collection. */ AssociativeArray.prototype.removeAll = function () { var array = this._array; if (array.length > 0) { this._hash = {}; array.length = 0; } }; var url = "https://dev.virtualearth.net/REST/v1/Locations"; /** * Provides geocoding through Bing Maps. * @alias BingMapsGeocoderService * @constructor * * @param {Object} options Object with the following properties: * @param {String} options.key A key to use with the Bing Maps geocoding service */ function BingMapsGeocoderService(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var key = options.key; //>>includeStart('debug', pragmas.debug); if (!defined(key)) { throw new DeveloperError("options.key is required."); } //>>includeEnd('debug'); this._key = key; this._resource = new Resource({ url: url, queryParameters: { key: key, }, }); } Object.defineProperties(BingMapsGeocoderService.prototype, { /** * The URL endpoint for the Bing geocoder service * @type {String} * @memberof BingMapsGeocoderService.prototype * @readonly */ url: { get: function () { return url; }, }, /** * The key for the Bing geocoder service * @type {String} * @memberof BingMapsGeocoderService.prototype * @readonly */ key: { get: function () { return this._key; }, }, }); /** * @function * * @param {String} query The query to be sent to the geocoder service * @returns {Promise} */ BingMapsGeocoderService.prototype.geocode = function (query) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("query", query); //>>includeEnd('debug'); var resource = this._resource.getDerivedResource({ queryParameters: { query: query, }, }); return resource.fetchJsonp("jsonp").then(function (result) { if (result.resourceSets.length === 0) { return []; } var results = result.resourceSets[0].resources; return results.map(function (resource) { var bbox = resource.bbox; var south = bbox[0]; var west = bbox[1]; var north = bbox[2]; var east = bbox[3]; return { displayName: resource.name, destination: Rectangle.fromDegrees(west, south, east, north), }; }); }); }; /** * A bounding rectangle given by a corner, width and height. * @alias BoundingRectangle * @constructor * * @param {Number} [x=0.0] The x coordinate of the rectangle. * @param {Number} [y=0.0] The y coordinate of the rectangle. * @param {Number} [width=0.0] The width of the rectangle. * @param {Number} [height=0.0] The height of the rectangle. * * @see BoundingSphere * @see Packable */ function BoundingRectangle(x, y, width, height) { /** * The x coordinate of the rectangle. * @type {Number} * @default 0.0 */ this.x = defaultValue(x, 0.0); /** * The y coordinate of the rectangle. * @type {Number} * @default 0.0 */ this.y = defaultValue(y, 0.0); /** * The width of the rectangle. * @type {Number} * @default 0.0 */ this.width = defaultValue(width, 0.0); /** * The height of the rectangle. * @type {Number} * @default 0.0 */ this.height = defaultValue(height, 0.0); } /** * The number of elements used to pack the object into an array. * @type {Number} */ BoundingRectangle.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {BoundingRectangle} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ BoundingRectangle.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.x; array[startingIndex++] = value.y; array[startingIndex++] = value.width; array[startingIndex] = value.height; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {BoundingRectangle} [result] The object into which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new BoundingRectangle(); } result.x = array[startingIndex++]; result.y = array[startingIndex++]; result.width = array[startingIndex++]; result.height = array[startingIndex]; return result; }; /** * Computes a bounding rectangle enclosing the list of 2D points. * The rectangle is oriented with the corner at the bottom left. * * @param {Cartesian2[]} positions List of points that the bounding rectangle will enclose. Each point must have x and y properties. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.fromPoints = function (positions, result) { if (!defined(result)) { result = new BoundingRectangle(); } if (!defined(positions) || positions.length === 0) { result.x = 0; result.y = 0; result.width = 0; result.height = 0; return result; } var length = positions.length; var minimumX = positions[0].x; var minimumY = positions[0].y; var maximumX = positions[0].x; var maximumY = positions[0].y; for (var i = 1; i < length; i++) { var p = positions[i]; var x = p.x; var y = p.y; minimumX = Math.min(x, minimumX); maximumX = Math.max(x, maximumX); minimumY = Math.min(y, minimumY); maximumY = Math.max(y, maximumY); } result.x = minimumX; result.y = minimumY; result.width = maximumX - minimumX; result.height = maximumY - minimumY; return result; }; var defaultProjection = new GeographicProjection(); var fromRectangleLowerLeft = new Cartographic(); var fromRectangleUpperRight = new Cartographic(); /** * Computes a bounding rectangle from a rectangle. * * @param {Rectangle} rectangle The valid rectangle used to create a bounding rectangle. * @param {Object} [projection=GeographicProjection] The projection used to project the rectangle into 2D. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.fromRectangle = function (rectangle, projection, result) { if (!defined(result)) { result = new BoundingRectangle(); } if (!defined(rectangle)) { result.x = 0; result.y = 0; result.width = 0; result.height = 0; return result; } projection = defaultValue(projection, defaultProjection); var lowerLeft = projection.project( Rectangle.southwest(rectangle, fromRectangleLowerLeft) ); var upperRight = projection.project( Rectangle.northeast(rectangle, fromRectangleUpperRight) ); Cartesian2.subtract(upperRight, lowerLeft, upperRight); result.x = lowerLeft.x; result.y = lowerLeft.y; result.width = upperRight.x; result.height = upperRight.y; return result; }; /** * Duplicates a BoundingRectangle instance. * * @param {BoundingRectangle} rectangle The bounding rectangle to duplicate. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. (Returns undefined if rectangle is undefined) */ BoundingRectangle.clone = function (rectangle, result) { if (!defined(rectangle)) { return undefined; } if (!defined(result)) { return new BoundingRectangle( rectangle.x, rectangle.y, rectangle.width, rectangle.height ); } result.x = rectangle.x; result.y = rectangle.y; result.width = rectangle.width; result.height = rectangle.height; return result; }; /** * Computes a bounding rectangle that is the union of the left and right bounding rectangles. * * @param {BoundingRectangle} left A rectangle to enclose in bounding rectangle. * @param {BoundingRectangle} right A rectangle to enclose in a bounding rectangle. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.union = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); if (!defined(result)) { result = new BoundingRectangle(); } var lowerLeftX = Math.min(left.x, right.x); var lowerLeftY = Math.min(left.y, right.y); var upperRightX = Math.max(left.x + left.width, right.x + right.width); var upperRightY = Math.max(left.y + left.height, right.y + right.height); result.x = lowerLeftX; result.y = lowerLeftY; result.width = upperRightX - lowerLeftX; result.height = upperRightY - lowerLeftY; return result; }; /** * Computes a bounding rectangle by enlarging the provided rectangle until it contains the provided point. * * @param {BoundingRectangle} rectangle A rectangle to expand. * @param {Cartesian2} point A point to enclose in a bounding rectangle. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.expand = function (rectangle, point, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Check.typeOf.object("point", point); //>>includeEnd('debug'); result = BoundingRectangle.clone(rectangle, result); var width = point.x - result.x; var height = point.y - result.y; if (width > result.width) { result.width = width; } else if (width < 0) { result.width -= width; result.x = point.x; } if (height > result.height) { result.height = height; } else if (height < 0) { result.height -= height; result.y = point.y; } return result; }; /** * Determines if two rectangles intersect. * * @param {BoundingRectangle} left A rectangle to check for intersection. * @param {BoundingRectangle} right The other rectangle to check for intersection. * @returns {Intersect} Intersect.INTERSECTING if the rectangles intersect, Intersect.OUTSIDE otherwise. */ BoundingRectangle.intersect = function (left, right) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); //>>includeEnd('debug'); var leftX = left.x; var leftY = left.y; var rightX = right.x; var rightY = right.y; if ( !( leftX > rightX + right.width || leftX + left.width < rightX || leftY + left.height < rightY || leftY > rightY + right.height ) ) { return Intersect$1.INTERSECTING; } return Intersect$1.OUTSIDE; }; /** * Compares the provided BoundingRectangles componentwise and returns * true if they are equal, false otherwise. * * @param {BoundingRectangle} [left] The first BoundingRectangle. * @param {BoundingRectangle} [right] The second BoundingRectangle. * @returns {Boolean} true if left and right are equal, false otherwise. */ BoundingRectangle.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.x === right.x && left.y === right.y && left.width === right.width && left.height === right.height) ); }; /** * Duplicates this BoundingRectangle instance. * * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The modified result parameter or a new BoundingRectangle instance if one was not provided. */ BoundingRectangle.prototype.clone = function (result) { return BoundingRectangle.clone(this, result); }; /** * Determines if this rectangle intersects with another. * * @param {BoundingRectangle} right A rectangle to check for intersection. * @returns {Intersect} Intersect.INTERSECTING if the rectangles intersect, Intersect.OUTSIDE otherwise. */ BoundingRectangle.prototype.intersect = function (right) { return BoundingRectangle.intersect(this, right); }; /** * Compares this BoundingRectangle against the provided BoundingRectangle componentwise and returns * true if they are equal, false otherwise. * * @param {BoundingRectangle} [right] The right hand side BoundingRectangle. * @returns {Boolean} true if they are equal, false otherwise. */ BoundingRectangle.prototype.equals = function (right) { return BoundingRectangle.equals(this, right); }; /** * Fill an array or a portion of an array with a given value. * * @param {Array} array The array to fill. * @param {*} value The value to fill the array with. * @param {Number} [start=0] The index to start filling at. * @param {Number} [end=array.length] The index to end stop at. * * @returns {Array} The resulting array. * @private */ function arrayFill(array, value, start, end) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); Check.defined("value", value); if (defined(start)) { Check.typeOf.number("start", start); } if (defined(end)) { Check.typeOf.number("end", end); } //>>includeEnd('debug'); if (typeof array.fill === "function") { return array.fill(value, start, end); } var length = array.length >>> 0; var relativeStart = defaultValue(start, 0); // If negative, find wrap around position var k = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length); var relativeEnd = defaultValue(end, length); // If negative, find wrap around position var last = relativeEnd < 0 ? Math.max(length + relativeEnd, 0) : Math.min(relativeEnd, length); // Fill array accordingly while (k < last) { array[k] = value; k++; } return array; } /** * @private */ var GeometryType = { NONE: 0, TRIANGLES: 1, LINES: 2, POLYLINES: 3, }; var GeometryType$1 = Object.freeze(GeometryType); /** * A 2x2 matrix, indexable as a column-major order array. * Constructor parameters are in row-major order for code readability. * @alias Matrix2 * @constructor * @implements {ArrayLike} * * @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.fromColumnMajorArray * @see Matrix2.fromRowMajorArray * @see Matrix2.fromScale * @see Matrix2.fromUniformScale * @see Matrix3 * @see Matrix4 */ function Matrix2(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); } /** * The number of elements used to pack the object into an array. * @type {Number} */ Matrix2.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {Matrix2} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Matrix2.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value[0]; array[startingIndex++] = value[1]; array[startingIndex++] = value[2]; array[startingIndex++] = value[3]; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Matrix2} [result] The object into which to store the result. * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. */ Matrix2.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new Matrix2(); } result[0] = array[startingIndex++]; result[1] = array[startingIndex++]; result[2] = array[startingIndex++]; result[3] = array[startingIndex++]; return result; }; /** * Duplicates a Matrix2 instance. * * @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 (matrix, result) { if (!defined(matrix)) { return undefined; } 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; }; /** * Creates a Matrix2 from 4 consecutive elements in an array. * * @param {Number[]} 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. * * @example * // Create the Matrix2: * // [1.0, 2.0] * // [1.0, 2.0] * * var v = [1.0, 1.0, 2.0, 2.0]; * var m = Cesium.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 = Cesium.Matrix2.fromArray(v2, 2); */ Matrix2.fromArray = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); 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. * * @param {Number[]} 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 {Matrix2} The modified result parameter, or a new Matrix2 instance if one was not provided. */ Matrix2.fromColumnMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); return Matrix2.clone(values, result); }; /** * Creates a Matrix2 instance from a row-major order array. * The resulting matrix will be in column-major order. * * @param {Number[]} 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 {Matrix2} The modified result parameter, or a new Matrix2 instance if one was not provided. */ Matrix2.fromRowMajorArray = function (values, result) { //>>includeStart('debug', pragmas.debug); Check.defined("values", values); //>>includeEnd('debug'); 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. * * @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 {Matrix2} The modified result parameter, or a new Matrix2 instance if one was not provided. * * @example * // Creates * // [7.0, 0.0] * // [0.0, 8.0] * var m = Cesium.Matrix2.fromScale(new Cesium.Cartesian2(7.0, 8.0)); */ Matrix2.fromScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("scale", scale); //>>includeEnd('debug'); 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. * * @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 {Matrix2} The modified result parameter, or a new Matrix2 instance if one was not provided. * * @example * // Creates * // [2.0, 0.0] * // [0.0, 2.0] * var m = Cesium.Matrix2.fromUniformScale(2.0); */ Matrix2.fromUniformScale = function (scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("scale", scale); //>>includeEnd('debug'); 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 {Matrix2} The modified result parameter, or a new Matrix2 instance if one was not provided. * * @example * // Rotate a point 45 degrees counterclockwise. * var p = new Cesium.Cartesian2(5, 6); * var m = Cesium.Matrix2.fromRotation(Cesium.Math.toRadians(45.0)); * var rotated = Cesium.Matrix2.multiplyByVector(m, p, new Cesium.Cartesian2()); */ Matrix2.fromRotation = function (angle, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("angle", angle); //>>includeEnd('debug'); 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. * * @param {Matrix2} matrix The matrix to use.. * @param {Number[]} [result] The Array onto which to store the result. * @returns {Number[]} The modified Array parameter or a new Array instance if one was not provided. */ Matrix2.toArray = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); //>>includeEnd('debug'); 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. * * @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 must be 0 or 1. * @exception {DeveloperError} column must be 0 or 1. * * @example * var myMatrix = new Cesium.Matrix2(); * var column1Row0Index = Cesium.Matrix2.getElementIndex(1, 0); * var column1Row0 = myMatrix[column1Row0Index] * myMatrix[column1Row0Index] = 10.0; */ Matrix2.getElementIndex = function (column, row) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("row", row, 0); Check.typeOf.number.lessThanOrEquals("row", row, 1); Check.typeOf.number.greaterThanOrEquals("column", column, 0); Check.typeOf.number.lessThanOrEquals("column", column, 1); //>>includeEnd('debug'); return column * 2 + row; }; /** * Retrieves a copy of the matrix column at the provided index as a Cartesian2 instance. * * @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. * * @exception {DeveloperError} index must be 0 or 1. */ Matrix2.getColumn = function (matrix, index, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 1); Check.typeOf.object("result", result); //>>includeEnd('debug'); var startIndex = index * 2; var x = matrix[startIndex]; var y = matrix[startIndex + 1]; 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. * * @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. * * @exception {DeveloperError} index must be 0 or 1. */ Matrix2.setColumn = function (matrix, index, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 1); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @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. * * @exception {DeveloperError} index must be 0 or 1. */ Matrix2.getRow = function (matrix, index, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 1); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = matrix[index]; var y = matrix[index + 2]; 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. * * @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 {Matrix2} result The object onto which to store the result. * @returns {Matrix2} The modified result parameter. * * @exception {DeveloperError} index must be 0 or 1. */ Matrix2.setRow = function (matrix, index, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number.greaterThanOrEquals("index", index, 0); Check.typeOf.number.lessThanOrEquals("index", index, 1); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); result = Matrix2.clone(matrix, result); result[index] = cartesian.x; result[index + 2] = cartesian.y; return result; }; var scratchColumn = new Cartesian2(); /** * Extracts the non-uniform scale assuming the matrix is an affine transformation. * * @param {Matrix2} matrix The matrix. * @param {Cartesian2} result The object onto which to store the result. * @returns {Cartesian2} The modified result parameter. */ Matrix2.getScale = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.x = Cartesian2.magnitude( Cartesian2.fromElements(matrix[0], matrix[1], scratchColumn) ); result.y = Cartesian2.magnitude( Cartesian2.fromElements(matrix[2], matrix[3], scratchColumn) ); return result; }; var scratchScale$4 = new Cartesian2(); /** * Computes the maximum scale assuming the matrix is an affine transformation. * The maximum scale is the maximum length of the column vectors. * * @param {Matrix2} matrix The matrix. * @returns {Number} The maximum scale. */ Matrix2.getMaximumScale = function (matrix) { Matrix2.getScale(matrix, scratchScale$4); return Cartesian2.maximumComponent(scratchScale$4); }; /** * Computes the product of two matrices. * * @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. */ Matrix2.multiply = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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]; result[0] = column0Row0; result[1] = column0Row1; result[2] = column1Row0; result[3] = column1Row1; return result; }; /** * Computes the sum of two matrices. * * @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. */ Matrix2.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] + right[0]; result[1] = left[1] + right[1]; result[2] = left[2] + right[2]; result[3] = left[3] + right[3]; return result; }; /** * Computes the difference of two matrices. * * @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. */ Matrix2.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = left[0] - right[0]; result[1] = left[1] - right[1]; result[2] = left[2] - right[2]; result[3] = left[3] - right[3]; return result; }; /** * Computes the product of a matrix and a column vector. * * @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. */ Matrix2.multiplyByVector = function (matrix, cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("cartesian", cartesian); Check.typeOf.object("result", result); //>>includeEnd('debug'); var x = matrix[0] * cartesian.x + matrix[2] * cartesian.y; var y = matrix[1] * cartesian.x + matrix[3] * cartesian.y; result.x = x; result.y = y; return result; }; /** * Computes the product of a matrix and a scalar. * * @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. */ Matrix2.multiplyByScalar = function (matrix, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = matrix[0] * scalar; result[1] = matrix[1] * scalar; result[2] = matrix[2] * scalar; result[3] = matrix[3] * scalar; return result; }; /** * Computes the product of a matrix times a (non-uniform) scale, as if the scale were a scale matrix. * * @param {Matrix2} matrix The matrix on the left-hand side. * @param {Cartesian2} scale The non-uniform scale on the right-hand side. * @param {Matrix2} result The object onto which to store the result. * @returns {Matrix2} The modified result parameter. * * * @example * // Instead of Cesium.Matrix2.multiply(m, Cesium.Matrix2.fromScale(scale), m); * Cesium.Matrix2.multiplyByScale(m, scale, m); * * @see Matrix2.fromScale * @see Matrix2.multiplyByUniformScale */ Matrix2.multiplyByScale = function (matrix, scale, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("scale", scale); Check.typeOf.object("result", result); //>>includeEnd('debug'); result[0] = matrix[0] * scale.x; result[1] = matrix[1] * scale.x; result[2] = matrix[2] * scale.y; result[3] = matrix[3] * scale.y; return result; }; /** * Creates a negated copy of the provided matrix. * * @param {Matrix2} matrix The matrix to negate. * @param {Matrix2} result The object onto which to store the result. * @returns {Matrix2} The modified result parameter. */ Matrix2.negate = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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. * * @param {Matrix2} matrix The matrix to transpose. * @param {Matrix2} result The object onto which to store the result. * @returns {Matrix2} The modified result parameter. */ Matrix2.transpose = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); var column0Row0 = matrix[0]; var column0Row1 = matrix[2]; var column1Row0 = matrix[1]; var column1Row1 = matrix[3]; 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. * * @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. */ Matrix2.abs = function (matrix, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("matrix", matrix); Check.typeOf.object("result", result); //>>includeEnd('debug'); 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 * true if they are equal, false otherwise. * * @param {Matrix2} [left] The first matrix. * @param {Matrix2} [right] The second matrix. * @returns {Boolean} true if left and right are equal, false 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]) ); }; /** * @private */ Matrix2.equalsArray = function (matrix, array, offset) { return ( matrix[0] === array[offset] && matrix[1] === array[offset + 1] && matrix[2] === array[offset + 2] && matrix[3] === array[offset + 3] ); }; /** * Compares the provided matrices componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix2} [left] The first matrix. * @param {Matrix2} [right] The second matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. */ Matrix2.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0); 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. * * @type {Matrix2} * @constant */ Matrix2.IDENTITY = Object.freeze(new Matrix2(1.0, 0.0, 0.0, 1.0)); /** * An immutable Matrix2 instance initialized to the zero matrix. * * @type {Matrix2} * @constant */ Matrix2.ZERO = Object.freeze(new Matrix2(0.0, 0.0, 0.0, 0.0)); /** * The index into Matrix2 for column 0, row 0. * * @type {Number} * @constant * * @example * var matrix = new Cesium.Matrix2(); * matrix[Cesium.Matrix2.COLUMN0ROW0] = 5.0; // set column 0, row 0 to 5.0 */ Matrix2.COLUMN0ROW0 = 0; /** * The index into Matrix2 for column 0, row 1. * * @type {Number} * @constant * * @example * var matrix = new Cesium.Matrix2(); * matrix[Cesium.Matrix2.COLUMN0ROW1] = 5.0; // set column 0, row 1 to 5.0 */ Matrix2.COLUMN0ROW1 = 1; /** * The index into Matrix2 for column 1, row 0. * * @type {Number} * @constant * * @example * var matrix = new Cesium.Matrix2(); * matrix[Cesium.Matrix2.COLUMN1ROW0] = 5.0; // set column 1, row 0 to 5.0 */ Matrix2.COLUMN1ROW0 = 2; /** * The index into Matrix2 for column 1, row 1. * * @type {Number} * @constant * * @example * var matrix = new Cesium.Matrix2(); * matrix[Cesium.Matrix2.COLUMN1ROW1] = 5.0; // set column 1, row 1 to 5.0 */ Matrix2.COLUMN1ROW1 = 3; Object.defineProperties(Matrix2.prototype, { /** * Gets the number of items in the collection. * @memberof Matrix2.prototype * * @type {Number} */ length: { get: function () { return Matrix2.packedLength; }, }, }); /** * Duplicates the provided Matrix2 instance. * * @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 * true if they are equal, false otherwise. * * @param {Matrix2} [right] The right hand side matrix. * @returns {Boolean} true if they are equal, false otherwise. */ Matrix2.prototype.equals = function (right) { return Matrix2.equals(this, right); }; /** * Compares this matrix to the provided matrix componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {Matrix2} [right] The right hand side matrix. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ 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)'. * * @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] + ")" ); }; /** * The type of a geometric primitive, i.e., points, lines, and triangles. * * @enum {Number} */ var PrimitiveType = { /** * Points primitive where each vertex (or index) is a separate point. * * @type {Number} * @constant */ POINTS: WebGLConstants$1.POINTS, /** * Lines primitive where each two vertices (or indices) is a line segment. Line segments are not necessarily connected. * * @type {Number} * @constant */ LINES: WebGLConstants$1.LINES, /** * 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: WebGLConstants$1.LINE_LOOP, /** * Line strip primitive where each vertex (or index) after the first connects a line to the previous vertex. * * @type {Number} * @constant */ LINE_STRIP: WebGLConstants$1.LINE_STRIP, /** * Triangles primitive where each three vertices (or indices) is a triangle. Triangles do not necessarily share edges. * * @type {Number} * @constant */ TRIANGLES: WebGLConstants$1.TRIANGLES, /** * 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: WebGLConstants$1.TRIANGLE_STRIP, /** * 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: WebGLConstants$1.TRIANGLE_FAN, }; /** * @private */ PrimitiveType.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 ); }; var PrimitiveType$1 = Object.freeze(PrimitiveType); /** * 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 Primitive can * be created from many heterogeneous - in many cases - geometries for performance. *

* Geometries can be transformed and optimized using functions in {@link GeometryPipeline}. *

* * @alias Geometry * @constructor * * @param {Object} options Object with the following properties: * @param {GeometryAttributes} options.attributes Attributes, which make up the geometry's vertices. * @param {PrimitiveType} [options.primitiveType=PrimitiveType.TRIANGLES] The type of primitives in the geometry. * @param {Uint16Array|Uint32Array} [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. * * @see PolygonGeometry * @see RectangleGeometry * @see EllipseGeometry * @see CircleGeometry * @see WallGeometry * @see SimplePolylineGeometry * @see BoxGeometry * @see EllipsoidGeometry * * @demo {@link https://sandcastle.cesium.com/index.html?src=Geometry%20and%20Appearances.html|Geometry and Appearances Demo} * * @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 Cesium.Geometry({ * attributes : { * position : new Cesium.GeometryAttribute({ * componentDatatype : Cesium.ComponentDatatype.DOUBLE, * componentsPerAttribute : 3, * values : positions * }) * }, * indices : new Uint16Array([0, 1, 1, 2, 2, 0]), * primitiveType : Cesium.PrimitiveType.LINES, * boundingSphere : Cesium.BoundingSphere.fromVertices(positions) * }); */ function Geometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.attributes", options.attributes); //>>includeEnd('debug'); /** * Attributes, which make up the geometry's vertices. Each property in this object corresponds to a * {@link GeometryAttribute} containing the attribute's data. *

* Attributes are always stored non-interleaved in a Geometry. *

*

* There are reserved attribute names with well-known semantics. The following attributes * are created by a Geometry (depending on the provided {@link VertexFormat}. *

    *
  • position - 3D vertex position. 64-bit floating-point (for precision). 3 components per attribute. See {@link VertexFormat#position}.
  • *
  • normal - Normal (normalized), commonly used for lighting. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#normal}.
  • *
  • st - 2D texture coordinate. 32-bit floating-point. 2 components per attribute. See {@link VertexFormat#st}.
  • *
  • bitangent - Bitangent (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#bitangent}.
  • *
  • tangent - Tangent (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#tangent}.
  • *
*

*

* 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. *

    *
  • position3DHigh - High 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.
  • *
  • position3DLow - Low 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.
  • *
  • position3DHigh - High 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.
  • *
  • position2DLow - Low 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.
  • *
  • color - RGBA color (normalized) usually from {@link GeometryInstance#color}. 32-bit floating-point. 4 components per attribute.
  • *
  • pickColor - RGBA color used for picking. 32-bit floating-point. 4 components per attribute.
  • *
*

* * @type GeometryAttributes * * @default undefined * * * @example * geometry.attributes.position = new Cesium.GeometryAttribute({ * componentDatatype : Cesium.ComponentDatatype.FLOAT, * componentsPerAttribute : 3, * values : new Float32Array(0) * }); * * @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 = defaultValue( options.primitiveType, PrimitiveType$1.TRIANGLES ); /** * An optional bounding sphere that fully encloses the geometry. This is * commonly used for culling. * * @type BoundingSphere * * @default undefined */ this.boundingSphere = options.boundingSphere; /** * @private */ this.geometryType = defaultValue(options.geometryType, GeometryType$1.NONE); /** * @private */ this.boundingSphereCV = options.boundingSphereCV; /** * Used for computing the bounding sphere for geometry using the applyOffset vertex attribute * @private */ this.offsetAttribute = options.offsetAttribute; } /** * 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. * * @param {Geometry} geometry The geometry. * @returns {Number} The number of vertices in the geometry. * * @example * var numVertices = Cesium.Geometry.computeNumberOfVertices(geometry); */ Geometry.computeNumberOfVertices = function (geometry) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("geometry", geometry); //>>includeEnd('debug'); 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; //>>includeStart('debug', pragmas.debug); if (numberOfVertices !== num && numberOfVertices !== -1) { throw new DeveloperError( "All attribute lists must have the same number of attributes." ); } //>>includeEnd('debug'); numberOfVertices = num; } } return numberOfVertices; }; var rectangleCenterScratch$3 = new Cartographic(); var enuCenterScratch = new Cartesian3(); var fixedFrameToEnuScratch = new Matrix4(); var boundingRectanglePointsCartographicScratch = [ new Cartographic(), new Cartographic(), new Cartographic(), ]; var boundingRectanglePointsEnuScratch = [ new Cartesian2(), new Cartesian2(), new Cartesian2(), ]; var points2DScratch$2 = [new Cartesian2(), new Cartesian2(), new Cartesian2()]; var pointEnuScratch = new Cartesian3(); var enuRotationScratch = new Quaternion(); var enuRotationMatrixScratch = new Matrix4(); var rotation2DScratch$1 = new Matrix2(); /** * For remapping texture coordinates when rendering GroundPrimitives with materials. * GroundPrimitive texture coordinates are computed to align with the cartographic coordinate system on the globe. * However, EllipseGeometry, RectangleGeometry, and PolygonGeometry all bake rotations to per-vertex texture coordinates * using different strategies. * * This method is used by EllipseGeometry and PolygonGeometry to approximate the same visual effect. * We encapsulate rotation and scale by computing a "transformed" texture coordinate system and computing * a set of reference points from which "cartographic" texture coordinates can be remapped to the "transformed" * system using distances to lines in 2D. * * This approximation becomes less accurate as the covered area increases, especially for GroundPrimitives near the poles, * but is generally reasonable for polygons and ellipses around the size of USA states. * * RectangleGeometry has its own version of this method that computes remapping coordinates using cartographic space * as an intermediary instead of local ENU, which is more accurate for large-area rectangles. * * @param {Cartesian3[]} positions Array of positions outlining the geometry * @param {Number} stRotation Texture coordinate rotation. * @param {Ellipsoid} ellipsoid Ellipsoid for projecting and generating local vectors. * @param {Rectangle} boundingRectangle Bounding rectangle around the positions. * @returns {Number[]} An array of 6 numbers specifying [minimum point, u extent, v extent] as points in the "cartographic" system. * @private */ Geometry._textureCoordinateRotationPoints = function ( positions, stRotation, ellipsoid, boundingRectangle ) { var i; // Create a local east-north-up coordinate system centered on the polygon's bounding rectangle. // Project the southwest, northwest, and southeast corners of the bounding rectangle into the plane of ENU as 2D points. // These are the equivalents of (0,0), (0,1), and (1,0) in the texture coordiante system computed in ShadowVolumeAppearanceFS, // aka "ENU texture space." var rectangleCenter = Rectangle.center( boundingRectangle, rectangleCenterScratch$3 ); var enuCenter = Cartographic.toCartesian( rectangleCenter, ellipsoid, enuCenterScratch ); var enuToFixedFrame = Transforms.eastNorthUpToFixedFrame( enuCenter, ellipsoid, fixedFrameToEnuScratch ); var fixedFrameToEnu = Matrix4.inverse( enuToFixedFrame, fixedFrameToEnuScratch ); var boundingPointsEnu = boundingRectanglePointsEnuScratch; var boundingPointsCarto = boundingRectanglePointsCartographicScratch; boundingPointsCarto[0].longitude = boundingRectangle.west; boundingPointsCarto[0].latitude = boundingRectangle.south; boundingPointsCarto[1].longitude = boundingRectangle.west; boundingPointsCarto[1].latitude = boundingRectangle.north; boundingPointsCarto[2].longitude = boundingRectangle.east; boundingPointsCarto[2].latitude = boundingRectangle.south; var posEnu = pointEnuScratch; for (i = 0; i < 3; i++) { Cartographic.toCartesian(boundingPointsCarto[i], ellipsoid, posEnu); posEnu = Matrix4.multiplyByPointAsVector(fixedFrameToEnu, posEnu, posEnu); boundingPointsEnu[i].x = posEnu.x; boundingPointsEnu[i].y = posEnu.y; } // Rotate each point in the polygon around the up vector in the ENU by -stRotation and project into ENU as 2D. // Compute the bounding box of these rotated points in the 2D ENU plane. // Rotate the corners back by stRotation, then compute their equivalents in the ENU texture space using the corners computed earlier. var rotation = Quaternion.fromAxisAngle( Cartesian3.UNIT_Z, -stRotation, enuRotationScratch ); var textureMatrix = Matrix3.fromQuaternion( rotation, enuRotationMatrixScratch ); var positionsLength = positions.length; var enuMinX = Number.POSITIVE_INFINITY; var enuMinY = Number.POSITIVE_INFINITY; var enuMaxX = Number.NEGATIVE_INFINITY; var enuMaxY = Number.NEGATIVE_INFINITY; for (i = 0; i < positionsLength; i++) { posEnu = Matrix4.multiplyByPointAsVector( fixedFrameToEnu, positions[i], posEnu ); posEnu = Matrix3.multiplyByVector(textureMatrix, posEnu, posEnu); enuMinX = Math.min(enuMinX, posEnu.x); enuMinY = Math.min(enuMinY, posEnu.y); enuMaxX = Math.max(enuMaxX, posEnu.x); enuMaxY = Math.max(enuMaxY, posEnu.y); } var toDesiredInComputed = Matrix2.fromRotation(stRotation, rotation2DScratch$1); var points2D = points2DScratch$2; points2D[0].x = enuMinX; points2D[0].y = enuMinY; points2D[1].x = enuMinX; points2D[1].y = enuMaxY; points2D[2].x = enuMaxX; points2D[2].y = enuMinY; var boundingEnuMin = boundingPointsEnu[0]; var boundingPointsWidth = boundingPointsEnu[2].x - boundingEnuMin.x; var boundingPointsHeight = boundingPointsEnu[1].y - boundingEnuMin.y; for (i = 0; i < 3; i++) { var point2D = points2D[i]; // rotate back Matrix2.multiplyByVector(toDesiredInComputed, point2D, point2D); // Convert point into east-north texture coordinate space point2D.x = (point2D.x - boundingEnuMin.x) / boundingPointsWidth; point2D.y = (point2D.y - boundingEnuMin.y) / boundingPointsHeight; } var minXYCorner = points2D[0]; var maxYCorner = points2D[1]; var maxXCorner = points2D[2]; var result = new Array(6); Cartesian2.pack(minXYCorner, result); Cartesian2.pack(maxYCorner, result, 2); Cartesian2.pack(maxXCorner, result, 4); return result; }; /** * 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 {Object} [options] Object with the following properties: * @param {ComponentDatatype} [options.componentDatatype] The datatype of each component in the attribute, e.g., individual elements in values. * @param {Number} [options.componentsPerAttribute] A number between 1 and 4 that defines the number of components in an attributes. * @param {Boolean} [options.normalize=false] When true and componentDatatype 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 {number[]|Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} [options.values] The values for the attributes stored in a typed array. * * @exception {DeveloperError} options.componentsPerAttribute must be between 1 and 4. * * * @example * var geometry = new Cesium.Geometry({ * attributes : { * position : new Cesium.GeometryAttribute({ * componentDatatype : Cesium.ComponentDatatype.FLOAT, * componentsPerAttribute : 3, * values : new Float32Array([ * 0.0, 0.0, 0.0, * 7500000.0, 0.0, 0.0, * 0.0, 7500000.0, 0.0 * ]) * }) * }, * primitiveType : Cesium.PrimitiveType.LINE_LOOP * }); * * @see Geometry */ function GeometryAttribute(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); /** * The datatype of each component in the attribute, e.g., individual elements in * {@link 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 = Cesium.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 true and componentDatatype 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. *

* This is commonly used when storing colors using {@link ComponentDatatype.UNSIGNED_BYTE}. *

* * @type Boolean * * @default false * * @example * attribute.componentDatatype = Cesium.ComponentDatatype.UNSIGNED_BYTE; * attribute.componentsPerAttribute = 4; * attribute.normalize = true; * attribute.values = new Uint8Array([ * Cesium.Color.floatToByte(color.red), * Cesium.Color.floatToByte(color.green), * Cesium.Color.floatToByte(color.blue), * Cesium.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 values defines one attributes since * componentsPerAttribute is 3. * * @type {number[]|Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} * * @default undefined * * @example * attribute.componentDatatype = Cesium.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; } /** * Attributes, which make up a geometry's vertices. Each property in this object corresponds to a * {@link GeometryAttribute} containing the attribute's data. *

* Attributes are always stored non-interleaved in a Geometry. *

* * @alias GeometryAttributes * @constructor */ function GeometryAttributes(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The 3D position attribute. *

* 64-bit floating-point (for precision). 3 components per attribute. *

* * @type GeometryAttribute * * @default undefined */ this.position = options.position; /** * The normal attribute (normalized), which is commonly used for lighting. *

* 32-bit floating-point. 3 components per attribute. *

* * @type GeometryAttribute * * @default undefined */ this.normal = options.normal; /** * The 2D texture coordinate attribute. *

* 32-bit floating-point. 2 components per attribute *

* * @type GeometryAttribute * * @default undefined */ this.st = options.st; /** * The bitangent attribute (normalized), which is used for tangent-space effects like bump mapping. *

* 32-bit floating-point. 3 components per attribute. *

* * @type GeometryAttribute * * @default undefined */ this.bitangent = options.bitangent; /** * The tangent attribute (normalized), which is used for tangent-space effects like bump mapping. *

* 32-bit floating-point. 3 components per attribute. *

* * @type GeometryAttribute * * @default undefined */ this.tangent = options.tangent; /** * The color attribute. *

* 8-bit unsigned integer. 4 components per attribute. *

* * @type GeometryAttribute * * @default undefined */ this.color = options.color; } /** * Represents which vertices should have a value of `true` for the `applyOffset` attribute * @private */ var GeometryOffsetAttribute = { NONE: 0, TOP: 1, ALL: 2, }; var GeometryOffsetAttribute$1 = Object.freeze(GeometryOffsetAttribute); /** * A vertex format defines what attributes make up a vertex. A VertexFormat can be provided * to a {@link Geometry} to request that certain properties be computed, e.g., just position, * position and normal, etc. * * @param {Object} [options] An object with boolean properties corresponding to VertexFormat properties as shown in the code example. * * @alias VertexFormat * @constructor * * @example * // Create a vertex format with position and 2D texture coordinate attributes. * var format = new Cesium.VertexFormat({ * position : true, * st : true * }); * * @see Geometry#attributes * @see Packable */ function VertexFormat(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * When true, the vertex has a 3D position attribute. *

* 64-bit floating-point (for precision). 3 components per attribute. *

* * @type Boolean * * @default false */ this.position = defaultValue(options.position, false); /** * When true, the vertex has a normal attribute (normalized), which is commonly used for lighting. *

* 32-bit floating-point. 3 components per attribute. *

* * @type Boolean * * @default false */ this.normal = defaultValue(options.normal, false); /** * When true, the vertex has a 2D texture coordinate attribute. *

* 32-bit floating-point. 2 components per attribute *

* * @type Boolean * * @default false */ this.st = defaultValue(options.st, false); /** * When true, the vertex has a bitangent attribute (normalized), which is used for tangent-space effects like bump mapping. *

* 32-bit floating-point. 3 components per attribute. *

* * @type Boolean * * @default false */ this.bitangent = defaultValue(options.bitangent, false); /** * When true, the vertex has a tangent attribute (normalized), which is used for tangent-space effects like bump mapping. *

* 32-bit floating-point. 3 components per attribute. *

* * @type Boolean * * @default false */ this.tangent = defaultValue(options.tangent, false); /** * When true, the vertex has an RGB color attribute. *

* 8-bit unsigned byte. 3 components per attribute. *

* * @type Boolean * * @default false */ this.color = defaultValue(options.color, false); } /** * An immutable vertex format with only a position attribute. * * @type {VertexFormat} * @constant * * @see VertexFormat#position */ VertexFormat.POSITION_ONLY = Object.freeze( new VertexFormat({ position: true, }) ); /** * An immutable vertex format with position and normal attributes. * This is compatible with per-instance color appearances like {@link PerInstanceColorAppearance}. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#normal */ VertexFormat.POSITION_AND_NORMAL = Object.freeze( new VertexFormat({ position: true, normal: true, }) ); /** * An immutable vertex format with position, normal, and st attributes. * This is compatible with {@link MaterialAppearance} when {@link MaterialAppearance#materialSupport} * is TEXTURED/code>. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#normal * @see VertexFormat#st */ VertexFormat.POSITION_NORMAL_AND_ST = Object.freeze( new VertexFormat({ position: true, normal: true, st: true, }) ); /** * An immutable vertex format with position and st attributes. * This is compatible with {@link EllipsoidSurfaceAppearance}. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#st */ VertexFormat.POSITION_AND_ST = Object.freeze( new VertexFormat({ position: true, st: true, }) ); /** * An immutable vertex format with position and color attributes. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#color */ VertexFormat.POSITION_AND_COLOR = Object.freeze( new VertexFormat({ position: true, color: true, }) ); /** * An immutable vertex format with well-known attributes: position, normal, st, tangent, and bitangent. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#normal * @see VertexFormat#st * @see VertexFormat#tangent * @see VertexFormat#bitangent */ VertexFormat.ALL = Object.freeze( new VertexFormat({ position: true, normal: true, st: true, tangent: true, bitangent: true, }) ); /** * An immutable vertex format with position, normal, and st attributes. * This is compatible with most appearances and materials; however * normal and st attributes are not always required. When this is * known in advance, another VertexFormat should be used. * * @type {VertexFormat} * @constant * * @see VertexFormat#position * @see VertexFormat#normal */ VertexFormat.DEFAULT = VertexFormat.POSITION_NORMAL_AND_ST; /** * The number of elements used to pack the object into an array. * @type {Number} */ VertexFormat.packedLength = 6; /** * Stores the provided instance into the provided array. * * @param {VertexFormat} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ VertexFormat.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.position ? 1.0 : 0.0; array[startingIndex++] = value.normal ? 1.0 : 0.0; array[startingIndex++] = value.st ? 1.0 : 0.0; array[startingIndex++] = value.tangent ? 1.0 : 0.0; array[startingIndex++] = value.bitangent ? 1.0 : 0.0; array[startingIndex] = value.color ? 1.0 : 0.0; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {VertexFormat} [result] The object into which to store the result. * @returns {VertexFormat} The modified result parameter or a new VertexFormat instance if one was not provided. */ VertexFormat.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new VertexFormat(); } result.position = array[startingIndex++] === 1.0; result.normal = array[startingIndex++] === 1.0; result.st = array[startingIndex++] === 1.0; result.tangent = array[startingIndex++] === 1.0; result.bitangent = array[startingIndex++] === 1.0; result.color = array[startingIndex] === 1.0; return result; }; /** * Duplicates a VertexFormat instance. * * @param {VertexFormat} vertexFormat The vertex format to duplicate. * @param {VertexFormat} [result] The object onto which to store the result. * @returns {VertexFormat} The modified result parameter or a new VertexFormat instance if one was not provided. (Returns undefined if vertexFormat is undefined) */ VertexFormat.clone = function (vertexFormat, result) { if (!defined(vertexFormat)) { return undefined; } if (!defined(result)) { result = new VertexFormat(); } result.position = vertexFormat.position; result.normal = vertexFormat.normal; result.st = vertexFormat.st; result.tangent = vertexFormat.tangent; result.bitangent = vertexFormat.bitangent; result.color = vertexFormat.color; return result; }; var diffScratch$1 = new Cartesian3(); /** * Describes a cube centered at the origin. * * @alias BoxGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.minimum The minimum x, y, and z coordinates of the box. * @param {Cartesian3} options.maximum The maximum x, y, and z coordinates of the box. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @see BoxGeometry.fromDimensions * @see BoxGeometry.createGeometry * @see Packable * * @demo {@link https://sandcastle.cesium.com/index.html?src=Box.html|Cesium Sandcastle Box Demo} * * @example * var box = new Cesium.BoxGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY, * maximum : new Cesium.Cartesian3(250000.0, 250000.0, 250000.0), * minimum : new Cesium.Cartesian3(-250000.0, -250000.0, -250000.0) * }); * var geometry = Cesium.BoxGeometry.createGeometry(box); */ function BoxGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var min = options.minimum; var max = options.maximum; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("min", min); Check.typeOf.object("max", max); if ( defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute$1.TOP ) { throw new DeveloperError( "GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry." ); } //>>includeEnd('debug'); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); this._minimum = Cartesian3.clone(min); this._maximum = Cartesian3.clone(max); this._vertexFormat = vertexFormat; this._offsetAttribute = options.offsetAttribute; this._workerName = "createBoxGeometry"; } /** * Creates a cube centered at the origin given its dimensions. * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.dimensions The width, depth, and height of the box stored in the x, y, and z coordinates of the Cartesian3, respectively. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @returns {BoxGeometry} * * @exception {DeveloperError} All dimensions components must be greater than or equal to zero. * * * @example * var box = Cesium.BoxGeometry.fromDimensions({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY, * dimensions : new Cesium.Cartesian3(500000.0, 500000.0, 500000.0) * }); * var geometry = Cesium.BoxGeometry.createGeometry(box); * * @see BoxGeometry.createGeometry */ BoxGeometry.fromDimensions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var dimensions = options.dimensions; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("dimensions", dimensions); Check.typeOf.number.greaterThanOrEquals("dimensions.x", dimensions.x, 0); Check.typeOf.number.greaterThanOrEquals("dimensions.y", dimensions.y, 0); Check.typeOf.number.greaterThanOrEquals("dimensions.z", dimensions.z, 0); //>>includeEnd('debug'); var corner = Cartesian3.multiplyByScalar(dimensions, 0.5, new Cartesian3()); return new BoxGeometry({ minimum: Cartesian3.negate(corner, new Cartesian3()), maximum: corner, vertexFormat: options.vertexFormat, offsetAttribute: options.offsetAttribute, }); }; /** * Creates a cube from the dimensions of an AxisAlignedBoundingBox. * * @param {AxisAlignedBoundingBox} boundingBox A description of the AxisAlignedBoundingBox. * @returns {BoxGeometry} * * * * @example * var aabb = Cesium.AxisAlignedBoundingBox.fromPoints(Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ])); * var box = Cesium.BoxGeometry.fromAxisAlignedBoundingBox(aabb); * * @see BoxGeometry.createGeometry */ BoxGeometry.fromAxisAlignedBoundingBox = function (boundingBox) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("boundingBox", boundingBox); //>>includeEnd('debug'); return new BoxGeometry({ minimum: boundingBox.minimum, maximum: boundingBox.maximum, }); }; /** * The number of elements used to pack the object into an array. * @type {Number} */ BoxGeometry.packedLength = 2 * Cartesian3.packedLength + VertexFormat.packedLength + 1; /** * Stores the provided instance into the provided array. * * @param {BoxGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ BoxGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._minimum, array, startingIndex); Cartesian3.pack( value._maximum, array, startingIndex + Cartesian3.packedLength ); VertexFormat.pack( value._vertexFormat, array, startingIndex + 2 * Cartesian3.packedLength ); array[ startingIndex + 2 * Cartesian3.packedLength + VertexFormat.packedLength ] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchMin$3 = new Cartesian3(); var scratchMax$3 = new Cartesian3(); var scratchVertexFormat$c = new VertexFormat(); var scratchOptions$n = { minimum: scratchMin$3, maximum: scratchMax$3, vertexFormat: scratchVertexFormat$c, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {BoxGeometry} [result] The object into which to store the result. * @returns {BoxGeometry} The modified result parameter or a new BoxGeometry instance if one was not provided. */ BoxGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var min = Cartesian3.unpack(array, startingIndex, scratchMin$3); var max = Cartesian3.unpack( array, startingIndex + Cartesian3.packedLength, scratchMax$3 ); var vertexFormat = VertexFormat.unpack( array, startingIndex + 2 * Cartesian3.packedLength, scratchVertexFormat$c ); var offsetAttribute = array[ startingIndex + 2 * Cartesian3.packedLength + VertexFormat.packedLength ]; if (!defined(result)) { scratchOptions$n.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new BoxGeometry(scratchOptions$n); } result._minimum = Cartesian3.clone(min, result._minimum); result._maximum = Cartesian3.clone(max, result._maximum); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of a box, including its vertices, indices, and a bounding sphere. * * @param {BoxGeometry} boxGeometry A description of the box. * @returns {Geometry|undefined} The computed vertices and indices. */ BoxGeometry.createGeometry = function (boxGeometry) { var min = boxGeometry._minimum; var max = boxGeometry._maximum; var vertexFormat = boxGeometry._vertexFormat; if (Cartesian3.equals(min, max)) { return; } var attributes = new GeometryAttributes(); var indices; var positions; if ( vertexFormat.position && (vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) ) { if (vertexFormat.position) { // 8 corner points. Duplicated 3 times each for each incident edge/face. positions = new Float64Array(6 * 4 * 3); // +z face positions[0] = min.x; positions[1] = min.y; positions[2] = max.z; positions[3] = max.x; positions[4] = min.y; positions[5] = max.z; positions[6] = max.x; positions[7] = max.y; positions[8] = max.z; positions[9] = min.x; positions[10] = max.y; positions[11] = max.z; // -z face positions[12] = min.x; positions[13] = min.y; positions[14] = min.z; positions[15] = max.x; positions[16] = min.y; positions[17] = min.z; positions[18] = max.x; positions[19] = max.y; positions[20] = min.z; positions[21] = min.x; positions[22] = max.y; positions[23] = min.z; // +x face positions[24] = max.x; positions[25] = min.y; positions[26] = min.z; positions[27] = max.x; positions[28] = max.y; positions[29] = min.z; positions[30] = max.x; positions[31] = max.y; positions[32] = max.z; positions[33] = max.x; positions[34] = min.y; positions[35] = max.z; // -x face positions[36] = min.x; positions[37] = min.y; positions[38] = min.z; positions[39] = min.x; positions[40] = max.y; positions[41] = min.z; positions[42] = min.x; positions[43] = max.y; positions[44] = max.z; positions[45] = min.x; positions[46] = min.y; positions[47] = max.z; // +y face positions[48] = min.x; positions[49] = max.y; positions[50] = min.z; positions[51] = max.x; positions[52] = max.y; positions[53] = min.z; positions[54] = max.x; positions[55] = max.y; positions[56] = max.z; positions[57] = min.x; positions[58] = max.y; positions[59] = max.z; // -y face positions[60] = min.x; positions[61] = min.y; positions[62] = min.z; positions[63] = max.x; positions[64] = min.y; positions[65] = min.z; positions[66] = max.x; positions[67] = min.y; positions[68] = max.z; positions[69] = min.x; positions[70] = min.y; positions[71] = max.z; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); } if (vertexFormat.normal) { var normals = new Float32Array(6 * 4 * 3); // +z face normals[0] = 0.0; normals[1] = 0.0; normals[2] = 1.0; normals[3] = 0.0; normals[4] = 0.0; normals[5] = 1.0; normals[6] = 0.0; normals[7] = 0.0; normals[8] = 1.0; normals[9] = 0.0; normals[10] = 0.0; normals[11] = 1.0; // -z face normals[12] = 0.0; normals[13] = 0.0; normals[14] = -1.0; normals[15] = 0.0; normals[16] = 0.0; normals[17] = -1.0; normals[18] = 0.0; normals[19] = 0.0; normals[20] = -1.0; normals[21] = 0.0; normals[22] = 0.0; normals[23] = -1.0; // +x face normals[24] = 1.0; normals[25] = 0.0; normals[26] = 0.0; normals[27] = 1.0; normals[28] = 0.0; normals[29] = 0.0; normals[30] = 1.0; normals[31] = 0.0; normals[32] = 0.0; normals[33] = 1.0; normals[34] = 0.0; normals[35] = 0.0; // -x face normals[36] = -1.0; normals[37] = 0.0; normals[38] = 0.0; normals[39] = -1.0; normals[40] = 0.0; normals[41] = 0.0; normals[42] = -1.0; normals[43] = 0.0; normals[44] = 0.0; normals[45] = -1.0; normals[46] = 0.0; normals[47] = 0.0; // +y face normals[48] = 0.0; normals[49] = 1.0; normals[50] = 0.0; normals[51] = 0.0; normals[52] = 1.0; normals[53] = 0.0; normals[54] = 0.0; normals[55] = 1.0; normals[56] = 0.0; normals[57] = 0.0; normals[58] = 1.0; normals[59] = 0.0; // -y face normals[60] = 0.0; normals[61] = -1.0; normals[62] = 0.0; normals[63] = 0.0; normals[64] = -1.0; normals[65] = 0.0; normals[66] = 0.0; normals[67] = -1.0; normals[68] = 0.0; normals[69] = 0.0; normals[70] = -1.0; normals[71] = 0.0; attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.st) { var texCoords = new Float32Array(6 * 4 * 2); // +z face texCoords[0] = 0.0; texCoords[1] = 0.0; texCoords[2] = 1.0; texCoords[3] = 0.0; texCoords[4] = 1.0; texCoords[5] = 1.0; texCoords[6] = 0.0; texCoords[7] = 1.0; // -z face texCoords[8] = 1.0; texCoords[9] = 0.0; texCoords[10] = 0.0; texCoords[11] = 0.0; texCoords[12] = 0.0; texCoords[13] = 1.0; texCoords[14] = 1.0; texCoords[15] = 1.0; //+x face texCoords[16] = 0.0; texCoords[17] = 0.0; texCoords[18] = 1.0; texCoords[19] = 0.0; texCoords[20] = 1.0; texCoords[21] = 1.0; texCoords[22] = 0.0; texCoords[23] = 1.0; // -x face texCoords[24] = 1.0; texCoords[25] = 0.0; texCoords[26] = 0.0; texCoords[27] = 0.0; texCoords[28] = 0.0; texCoords[29] = 1.0; texCoords[30] = 1.0; texCoords[31] = 1.0; // +y face texCoords[32] = 1.0; texCoords[33] = 0.0; texCoords[34] = 0.0; texCoords[35] = 0.0; texCoords[36] = 0.0; texCoords[37] = 1.0; texCoords[38] = 1.0; texCoords[39] = 1.0; // -y face texCoords[40] = 0.0; texCoords[41] = 0.0; texCoords[42] = 1.0; texCoords[43] = 0.0; texCoords[44] = 1.0; texCoords[45] = 1.0; texCoords[46] = 0.0; texCoords[47] = 1.0; attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: texCoords, }); } if (vertexFormat.tangent) { var tangents = new Float32Array(6 * 4 * 3); // +z face tangents[0] = 1.0; tangents[1] = 0.0; tangents[2] = 0.0; tangents[3] = 1.0; tangents[4] = 0.0; tangents[5] = 0.0; tangents[6] = 1.0; tangents[7] = 0.0; tangents[8] = 0.0; tangents[9] = 1.0; tangents[10] = 0.0; tangents[11] = 0.0; // -z face tangents[12] = -1.0; tangents[13] = 0.0; tangents[14] = 0.0; tangents[15] = -1.0; tangents[16] = 0.0; tangents[17] = 0.0; tangents[18] = -1.0; tangents[19] = 0.0; tangents[20] = 0.0; tangents[21] = -1.0; tangents[22] = 0.0; tangents[23] = 0.0; // +x face tangents[24] = 0.0; tangents[25] = 1.0; tangents[26] = 0.0; tangents[27] = 0.0; tangents[28] = 1.0; tangents[29] = 0.0; tangents[30] = 0.0; tangents[31] = 1.0; tangents[32] = 0.0; tangents[33] = 0.0; tangents[34] = 1.0; tangents[35] = 0.0; // -x face tangents[36] = 0.0; tangents[37] = -1.0; tangents[38] = 0.0; tangents[39] = 0.0; tangents[40] = -1.0; tangents[41] = 0.0; tangents[42] = 0.0; tangents[43] = -1.0; tangents[44] = 0.0; tangents[45] = 0.0; tangents[46] = -1.0; tangents[47] = 0.0; // +y face tangents[48] = -1.0; tangents[49] = 0.0; tangents[50] = 0.0; tangents[51] = -1.0; tangents[52] = 0.0; tangents[53] = 0.0; tangents[54] = -1.0; tangents[55] = 0.0; tangents[56] = 0.0; tangents[57] = -1.0; tangents[58] = 0.0; tangents[59] = 0.0; // -y face tangents[60] = 1.0; tangents[61] = 0.0; tangents[62] = 0.0; tangents[63] = 1.0; tangents[64] = 0.0; tangents[65] = 0.0; tangents[66] = 1.0; tangents[67] = 0.0; tangents[68] = 0.0; tangents[69] = 1.0; tangents[70] = 0.0; tangents[71] = 0.0; attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { var bitangents = new Float32Array(6 * 4 * 3); // +z face bitangents[0] = 0.0; bitangents[1] = 1.0; bitangents[2] = 0.0; bitangents[3] = 0.0; bitangents[4] = 1.0; bitangents[5] = 0.0; bitangents[6] = 0.0; bitangents[7] = 1.0; bitangents[8] = 0.0; bitangents[9] = 0.0; bitangents[10] = 1.0; bitangents[11] = 0.0; // -z face bitangents[12] = 0.0; bitangents[13] = 1.0; bitangents[14] = 0.0; bitangents[15] = 0.0; bitangents[16] = 1.0; bitangents[17] = 0.0; bitangents[18] = 0.0; bitangents[19] = 1.0; bitangents[20] = 0.0; bitangents[21] = 0.0; bitangents[22] = 1.0; bitangents[23] = 0.0; // +x face bitangents[24] = 0.0; bitangents[25] = 0.0; bitangents[26] = 1.0; bitangents[27] = 0.0; bitangents[28] = 0.0; bitangents[29] = 1.0; bitangents[30] = 0.0; bitangents[31] = 0.0; bitangents[32] = 1.0; bitangents[33] = 0.0; bitangents[34] = 0.0; bitangents[35] = 1.0; // -x face bitangents[36] = 0.0; bitangents[37] = 0.0; bitangents[38] = 1.0; bitangents[39] = 0.0; bitangents[40] = 0.0; bitangents[41] = 1.0; bitangents[42] = 0.0; bitangents[43] = 0.0; bitangents[44] = 1.0; bitangents[45] = 0.0; bitangents[46] = 0.0; bitangents[47] = 1.0; // +y face bitangents[48] = 0.0; bitangents[49] = 0.0; bitangents[50] = 1.0; bitangents[51] = 0.0; bitangents[52] = 0.0; bitangents[53] = 1.0; bitangents[54] = 0.0; bitangents[55] = 0.0; bitangents[56] = 1.0; bitangents[57] = 0.0; bitangents[58] = 0.0; bitangents[59] = 1.0; // -y face bitangents[60] = 0.0; bitangents[61] = 0.0; bitangents[62] = 1.0; bitangents[63] = 0.0; bitangents[64] = 0.0; bitangents[65] = 1.0; bitangents[66] = 0.0; bitangents[67] = 0.0; bitangents[68] = 1.0; bitangents[69] = 0.0; bitangents[70] = 0.0; bitangents[71] = 1.0; attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } // 12 triangles: 6 faces, 2 triangles each. indices = new Uint16Array(6 * 2 * 3); // +z face indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3; // -z face indices[6] = 4 + 2; indices[7] = 4 + 1; indices[8] = 4 + 0; indices[9] = 4 + 3; indices[10] = 4 + 2; indices[11] = 4 + 0; // +x face indices[12] = 8 + 0; indices[13] = 8 + 1; indices[14] = 8 + 2; indices[15] = 8 + 0; indices[16] = 8 + 2; indices[17] = 8 + 3; // -x face indices[18] = 12 + 2; indices[19] = 12 + 1; indices[20] = 12 + 0; indices[21] = 12 + 3; indices[22] = 12 + 2; indices[23] = 12 + 0; // +y face indices[24] = 16 + 2; indices[25] = 16 + 1; indices[26] = 16 + 0; indices[27] = 16 + 3; indices[28] = 16 + 2; indices[29] = 16 + 0; // -y face indices[30] = 20 + 0; indices[31] = 20 + 1; indices[32] = 20 + 2; indices[33] = 20 + 0; indices[34] = 20 + 2; indices[35] = 20 + 3; } else { // Positions only - no need to duplicate corner points positions = new Float64Array(8 * 3); positions[0] = min.x; positions[1] = min.y; positions[2] = min.z; positions[3] = max.x; positions[4] = min.y; positions[5] = min.z; positions[6] = max.x; positions[7] = max.y; positions[8] = min.z; positions[9] = min.x; positions[10] = max.y; positions[11] = min.z; positions[12] = min.x; positions[13] = min.y; positions[14] = max.z; positions[15] = max.x; positions[16] = min.y; positions[17] = max.z; positions[18] = max.x; positions[19] = max.y; positions[20] = max.z; positions[21] = min.x; positions[22] = max.y; positions[23] = max.z; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); // 12 triangles: 6 faces, 2 triangles each. indices = new Uint16Array(6 * 2 * 3); // plane z = corner.Z indices[0] = 4; indices[1] = 5; indices[2] = 6; indices[3] = 4; indices[4] = 6; indices[5] = 7; // plane z = -corner.Z indices[6] = 1; indices[7] = 0; indices[8] = 3; indices[9] = 1; indices[10] = 3; indices[11] = 2; // plane x = corner.X indices[12] = 1; indices[13] = 6; indices[14] = 5; indices[15] = 1; indices[16] = 2; indices[17] = 6; // plane y = corner.Y indices[18] = 2; indices[19] = 3; indices[20] = 7; indices[21] = 2; indices[22] = 7; indices[23] = 6; // plane x = -corner.X indices[24] = 3; indices[25] = 0; indices[26] = 4; indices[27] = 3; indices[28] = 4; indices[29] = 7; // plane y = -corner.Y indices[30] = 0; indices[31] = 1; indices[32] = 5; indices[33] = 0; indices[34] = 5; indices[35] = 4; } var diff = Cartesian3.subtract(max, min, diffScratch$1); var radius = Cartesian3.magnitude(diff) * 0.5; if (defined(boxGeometry._offsetAttribute)) { var length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = boxGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: new BoundingSphere(Cartesian3.ZERO, radius), offsetAttribute: boxGeometry._offsetAttribute, }); }; var unitBoxGeometry; /** * Returns the geometric representation of a unit box, including its vertices, indices, and a bounding sphere. * @returns {Geometry} The computed vertices and indices. * * @private */ BoxGeometry.getUnitBox = function () { if (!defined(unitBoxGeometry)) { unitBoxGeometry = BoxGeometry.createGeometry( BoxGeometry.fromDimensions({ dimensions: new Cartesian3(1.0, 1.0, 1.0), vertexFormat: VertexFormat.POSITION_ONLY, }) ); } return unitBoxGeometry; }; var diffScratch = new Cartesian3(); /** * A description of the outline of a cube centered at the origin. * * @alias BoxOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.minimum The minimum x, y, and z coordinates of the box. * @param {Cartesian3} options.maximum The maximum x, y, and z coordinates of the box. * * @see BoxOutlineGeometry.fromDimensions * @see BoxOutlineGeometry.createGeometry * @see Packable * * @example * var box = new Cesium.BoxOutlineGeometry({ * maximum : new Cesium.Cartesian3(250000.0, 250000.0, 250000.0), * minimum : new Cesium.Cartesian3(-250000.0, -250000.0, -250000.0) * }); * var geometry = Cesium.BoxOutlineGeometry.createGeometry(box); */ function BoxOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var min = options.minimum; var max = options.maximum; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("min", min); Check.typeOf.object("max", max); if ( defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute$1.TOP ) { throw new DeveloperError( "GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry." ); } //>>includeEnd('debug'); this._min = Cartesian3.clone(min); this._max = Cartesian3.clone(max); this._offsetAttribute = options.offsetAttribute; this._workerName = "createBoxOutlineGeometry"; } /** * Creates an outline of a cube centered at the origin given its dimensions. * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.dimensions The width, depth, and height of the box stored in the x, y, and z coordinates of the Cartesian3, respectively. * @returns {BoxOutlineGeometry} * * @exception {DeveloperError} All dimensions components must be greater than or equal to zero. * * * @example * var box = Cesium.BoxOutlineGeometry.fromDimensions({ * dimensions : new Cesium.Cartesian3(500000.0, 500000.0, 500000.0) * }); * var geometry = Cesium.BoxOutlineGeometry.createGeometry(box); * * @see BoxOutlineGeometry.createGeometry */ BoxOutlineGeometry.fromDimensions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var dimensions = options.dimensions; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("dimensions", dimensions); Check.typeOf.number.greaterThanOrEquals("dimensions.x", dimensions.x, 0); Check.typeOf.number.greaterThanOrEquals("dimensions.y", dimensions.y, 0); Check.typeOf.number.greaterThanOrEquals("dimensions.z", dimensions.z, 0); //>>includeEnd('debug'); var corner = Cartesian3.multiplyByScalar(dimensions, 0.5, new Cartesian3()); return new BoxOutlineGeometry({ minimum: Cartesian3.negate(corner, new Cartesian3()), maximum: corner, offsetAttribute: options.offsetAttribute, }); }; /** * Creates an outline of a cube from the dimensions of an AxisAlignedBoundingBox. * * @param {AxisAlignedBoundingBox} boundingBox A description of the AxisAlignedBoundingBox. * @returns {BoxOutlineGeometry} * * * * @example * var aabb = Cesium.AxisAlignedBoundingBox.fromPoints(Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ])); * var box = Cesium.BoxOutlineGeometry.fromAxisAlignedBoundingBox(aabb); * * @see BoxOutlineGeometry.createGeometry */ BoxOutlineGeometry.fromAxisAlignedBoundingBox = function (boundingBox) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("boundindBox", boundingBox); //>>includeEnd('debug'); return new BoxOutlineGeometry({ minimum: boundingBox.minimum, maximum: boundingBox.maximum, }); }; /** * The number of elements used to pack the object into an array. * @type {Number} */ BoxOutlineGeometry.packedLength = 2 * Cartesian3.packedLength + 1; /** * Stores the provided instance into the provided array. * * @param {BoxOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ BoxOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._min, array, startingIndex); Cartesian3.pack(value._max, array, startingIndex + Cartesian3.packedLength); array[startingIndex + Cartesian3.packedLength * 2] = defaultValue( value._offsetAttribute, -1 ); return array; }; var scratchMin$2 = new Cartesian3(); var scratchMax$2 = new Cartesian3(); var scratchOptions$m = { minimum: scratchMin$2, maximum: scratchMax$2, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {BoxOutlineGeometry} [result] The object into which to store the result. * @returns {BoxOutlineGeometry} The modified result parameter or a new BoxOutlineGeometry instance if one was not provided. */ BoxOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var min = Cartesian3.unpack(array, startingIndex, scratchMin$2); var max = Cartesian3.unpack( array, startingIndex + Cartesian3.packedLength, scratchMax$2 ); var offsetAttribute = array[startingIndex + Cartesian3.packedLength * 2]; if (!defined(result)) { scratchOptions$m.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new BoxOutlineGeometry(scratchOptions$m); } result._min = Cartesian3.clone(min, result._min); result._max = Cartesian3.clone(max, result._max); result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of an outline of a box, including its vertices, indices, and a bounding sphere. * * @param {BoxOutlineGeometry} boxGeometry A description of the box outline. * @returns {Geometry|undefined} The computed vertices and indices. */ BoxOutlineGeometry.createGeometry = function (boxGeometry) { var min = boxGeometry._min; var max = boxGeometry._max; if (Cartesian3.equals(min, max)) { return; } var attributes = new GeometryAttributes(); var indices = new Uint16Array(12 * 2); var positions = new Float64Array(8 * 3); positions[0] = min.x; positions[1] = min.y; positions[2] = min.z; positions[3] = max.x; positions[4] = min.y; positions[5] = min.z; positions[6] = max.x; positions[7] = max.y; positions[8] = min.z; positions[9] = min.x; positions[10] = max.y; positions[11] = min.z; positions[12] = min.x; positions[13] = min.y; positions[14] = max.z; positions[15] = max.x; positions[16] = min.y; positions[17] = max.z; positions[18] = max.x; positions[19] = max.y; positions[20] = max.z; positions[21] = min.x; positions[22] = max.y; positions[23] = max.z; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); // top indices[0] = 4; indices[1] = 5; indices[2] = 5; indices[3] = 6; indices[4] = 6; indices[5] = 7; indices[6] = 7; indices[7] = 4; // bottom indices[8] = 0; indices[9] = 1; indices[10] = 1; indices[11] = 2; indices[12] = 2; indices[13] = 3; indices[14] = 3; indices[15] = 0; // left indices[16] = 0; indices[17] = 4; indices[18] = 1; indices[19] = 5; //right indices[20] = 2; indices[21] = 6; indices[22] = 3; indices[23] = 7; var diff = Cartesian3.subtract(max, min, diffScratch); var radius = Cartesian3.magnitude(diff) * 0.5; if (defined(boxGeometry._offsetAttribute)) { var length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = boxGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: new BoundingSphere(Cartesian3.ZERO, radius), offsetAttribute: boxGeometry._offsetAttribute, }); }; /** * Geocodes queries containing longitude and latitude coordinates and an optional height. * Query format: `longitude latitude (height)` with longitude/latitude in degrees and height in meters. * * @alias CartographicGeocoderService * @constructor */ function CartographicGeocoderService() {} /** * @function * * @param {String} query The query to be sent to the geocoder service * @returns {Promise} */ CartographicGeocoderService.prototype.geocode = function (query) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("query", query); //>>includeEnd('debug'); var splitQuery = query.match(/[^\s,\n]+/g); if (splitQuery.length === 2 || splitQuery.length === 3) { var longitude = +splitQuery[0]; var latitude = +splitQuery[1]; var height = splitQuery.length === 3 ? +splitQuery[2] : 300.0; if (isNaN(longitude) && isNaN(latitude)) { var coordTest = /^(\d+.?\d*)([nsew])/i; for (var i = 0; i < splitQuery.length; ++i) { var splitCoord = splitQuery[i].match(coordTest); if (coordTest.test(splitQuery[i]) && splitCoord.length === 3) { if (/^[ns]/i.test(splitCoord[2])) { latitude = /^[n]/i.test(splitCoord[2]) ? +splitCoord[1] : -splitCoord[1]; } else if (/^[ew]/i.test(splitCoord[2])) { longitude = /^[e]/i.test(splitCoord[2]) ? +splitCoord[1] : -splitCoord[1]; } } } } if (!isNaN(longitude) && !isNaN(latitude) && !isNaN(height)) { var result = { displayName: query, destination: Cartesian3.fromDegrees(longitude, latitude, height), }; return when.resolve([result]); } } return when.resolve([]); }; /** * Creates a curve parameterized and evaluated by time. This type describes an interface * and is not intended to be instantiated directly. * * @alias Spline * @constructor * * @see CatmullRomSpline * @see HermiteSpline * @see LinearSpline * @see QuaternionSpline */ function Spline() { /** * An array of times for the control points. * @type {Number[]} * @default undefined */ this.times = undefined; /** * An array of control points. * @type {Cartesian3[]|Quaternion[]} * @default undefined */ this.points = undefined; DeveloperError.throwInstantiationError(); } /** * Evaluates the curve at a given time. * @function * * @param {Number} time The time at which to evaluate the curve. * @param {Cartesian3|Quaternion|Number[]} [result] The object onto which to store the result. * @returns {Cartesian3|Quaternion|Number[]} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ Spline.prototype.evaluate = DeveloperError.throwInstantiationError; /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * * @param {Number} time The time. * @param {Number} startIndex The index from which to start the search. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ Spline.prototype.findTimeInterval = function (time, startIndex) { var times = this.times; var length = times.length; //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (time < times[0] || time > times[length - 1]) { throw new DeveloperError("time is out of range."); } //>>includeEnd('debug'); // Take advantage of temporal coherence by checking current, next and previous intervals // for containment of time. startIndex = defaultValue(startIndex, 0); if (time >= times[startIndex]) { if (startIndex + 1 < length && time < times[startIndex + 1]) { return startIndex; } else if (startIndex + 2 < length && time < times[startIndex + 2]) { return startIndex + 1; } } else if (startIndex - 1 >= 0 && time >= times[startIndex - 1]) { return startIndex - 1; } // The above failed so do a linear search. For the use cases so far, the // length of the list is less than 10. In the future, if there is a bottle neck, // it might be here. var i; if (time > times[startIndex]) { for (i = startIndex; i < length - 1; ++i) { if (time >= times[i] && time < times[i + 1]) { break; } } } else { for (i = startIndex - 1; i >= 0; --i) { if (time >= times[i] && time < times[i + 1]) { break; } } } if (i === length - 1) { i = length - 2; } return i; }; /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around the animation period. */ Spline.prototype.wrapTime = function (time) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("time", time); //>>includeEnd('debug'); var times = this.times; var timeEnd = times[times.length - 1]; var timeStart = times[0]; var timeStretch = timeEnd - timeStart; var divs; if (time < timeStart) { divs = Math.floor((timeStart - time) / timeStretch) + 1; time += divs * timeStretch; } if (time > timeEnd) { divs = Math.floor((time - timeEnd) / timeStretch) + 1; time -= divs * timeStretch; } return time; }; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ Spline.prototype.clampTime = function (time) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("time", time); //>>includeEnd('debug'); var times = this.times; return CesiumMath.clamp(time, times[0], times[times.length - 1]); }; /** * A spline that uses piecewise linear interpolation to create a curve. * * @alias LinearSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. * The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Cartesian3[]} options.points The array of {@link Cartesian3} control points. * * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * * @example * var times = [ 0.0, 1.5, 3.0, 4.5, 6.0 ]; * var spline = new Cesium.LinearSpline({ * times : times, * points : [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ] * }); * * var p0 = spline.evaluate(times[0]); * * @see HermiteSpline * @see CatmullRomSpline * @see QuaternionSpline * @see WeightSpline */ function LinearSpline(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var points = options.points; var times = options.times; //>>includeStart('debug', pragmas.debug); if (!defined(points) || !defined(times)) { throw new DeveloperError("points and times are required."); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length) { throw new DeveloperError("times.length must be equal to points.length."); } //>>includeEnd('debug'); this._times = times; this._points = points; this._lastTimeIndex = 0; } Object.defineProperties(LinearSpline.prototype, { /** * An array of times for the control points. * * @memberof LinearSpline.prototype * * @type {Number[]} * @readonly */ times: { get: function () { return this._times; }, }, /** * An array of {@link Cartesian3} control points. * * @memberof LinearSpline.prototype * * @type {Cartesian3[]} * @readonly */ points: { get: function () { return this._points; }, }, }); /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ LinearSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */ LinearSpline.prototype.wrapTime = Spline.prototype.wrapTime; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ LinearSpline.prototype.clampTime = Spline.prototype.clampTime; /** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ LinearSpline.prototype.evaluate = function (time, result) { var points = this.points; var times = this.times; var i = (this._lastTimeIndex = this.findTimeInterval( time, this._lastTimeIndex )); var u = (time - times[i]) / (times[i + 1] - times[i]); if (!defined(result)) { result = new Cartesian3(); } return Cartesian3.lerp(points[i], points[i + 1], u, result); }; /** * Uses the Tridiagonal Matrix Algorithm, also known as the Thomas Algorithm, to solve * a system of linear equations where the coefficient matrix is a tridiagonal matrix. * * @namespace TridiagonalSystemSolver */ var TridiagonalSystemSolver = {}; /** * Solves a tridiagonal system of linear equations. * * @param {Number[]} diagonal An array with length n that contains the diagonal of the coefficient matrix. * @param {Number[]} lower An array with length n - 1 that contains the lower diagonal of the coefficient matrix. * @param {Number[]} upper An array with length n - 1 that contains the upper diagonal of the coefficient matrix. * @param {Cartesian3[]} right An array of Cartesians with length n that is the right side of the system of equations. * * @exception {DeveloperError} diagonal and right must have the same lengths. * @exception {DeveloperError} lower and upper must have the same lengths. * @exception {DeveloperError} lower and upper must be one less than the length of diagonal. * * @performance Linear time. * * @example * var lowerDiagonal = [1.0, 1.0, 1.0, 1.0]; * var diagonal = [2.0, 4.0, 4.0, 4.0, 2.0]; * var upperDiagonal = [1.0, 1.0, 1.0, 1.0]; * var rightHandSide = [ * new Cesium.Cartesian3(410757.0, -1595711.0, 1375302.0), * new Cesium.Cartesian3(-5986705.0, -2190640.0, 1099600.0), * new Cesium.Cartesian3(-12593180.0, 288588.0, -1755549.0), * new Cesium.Cartesian3(-5349898.0, 2457005.0, -2685438.0), * new Cesium.Cartesian3(845820.0, 1573488.0, -1205591.0) * ]; * * var solution = Cesium.TridiagonalSystemSolver.solve(lowerDiagonal, diagonal, upperDiagonal, rightHandSide); * * @returns {Cartesian3[]} An array of Cartesians with length n that is the solution to the tridiagonal system of equations. */ TridiagonalSystemSolver.solve = function (lower, diagonal, upper, right) { //>>includeStart('debug', pragmas.debug); if (!defined(lower) || !(lower instanceof Array)) { throw new DeveloperError("The array lower is required."); } if (!defined(diagonal) || !(diagonal instanceof Array)) { throw new DeveloperError("The array diagonal is required."); } if (!defined(upper) || !(upper instanceof Array)) { throw new DeveloperError("The array upper is required."); } if (!defined(right) || !(right instanceof Array)) { throw new DeveloperError("The array right is required."); } if (diagonal.length !== right.length) { throw new DeveloperError("diagonal and right must have the same lengths."); } if (lower.length !== upper.length) { throw new DeveloperError("lower and upper must have the same lengths."); } else if (lower.length !== diagonal.length - 1) { throw new DeveloperError( "lower and upper must be one less than the length of diagonal." ); } //>>includeEnd('debug'); var c = new Array(upper.length); var d = new Array(right.length); var x = new Array(right.length); var i; for (i = 0; i < d.length; i++) { d[i] = new Cartesian3(); x[i] = new Cartesian3(); } c[0] = upper[0] / diagonal[0]; d[0] = Cartesian3.multiplyByScalar(right[0], 1.0 / diagonal[0], d[0]); var scalar; for (i = 1; i < c.length; ++i) { scalar = 1.0 / (diagonal[i] - c[i - 1] * lower[i - 1]); c[i] = upper[i] * scalar; d[i] = Cartesian3.subtract( right[i], Cartesian3.multiplyByScalar(d[i - 1], lower[i - 1], d[i]), d[i] ); d[i] = Cartesian3.multiplyByScalar(d[i], scalar, d[i]); } scalar = 1.0 / (diagonal[i] - c[i - 1] * lower[i - 1]); d[i] = Cartesian3.subtract( right[i], Cartesian3.multiplyByScalar(d[i - 1], lower[i - 1], d[i]), d[i] ); d[i] = Cartesian3.multiplyByScalar(d[i], scalar, d[i]); x[x.length - 1] = d[d.length - 1]; for (i = x.length - 2; i >= 0; --i) { x[i] = Cartesian3.subtract( d[i], Cartesian3.multiplyByScalar(x[i + 1], c[i], x[i]), x[i] ); } return x; }; var scratchLower = []; var scratchDiagonal = []; var scratchUpper = []; var scratchRight$3 = []; function generateClamped(points, firstTangent, lastTangent) { var l = scratchLower; var u = scratchUpper; var d = scratchDiagonal; var r = scratchRight$3; l.length = u.length = points.length - 1; d.length = r.length = points.length; var i; l[0] = d[0] = 1.0; u[0] = 0.0; var right = r[0]; if (!defined(right)) { right = r[0] = new Cartesian3(); } Cartesian3.clone(firstTangent, right); for (i = 1; i < l.length - 1; ++i) { l[i] = u[i] = 1.0; d[i] = 4.0; right = r[i]; if (!defined(right)) { right = r[i] = new Cartesian3(); } Cartesian3.subtract(points[i + 1], points[i - 1], right); Cartesian3.multiplyByScalar(right, 3.0, right); } l[i] = 0.0; u[i] = 1.0; d[i] = 4.0; right = r[i]; if (!defined(right)) { right = r[i] = new Cartesian3(); } Cartesian3.subtract(points[i + 1], points[i - 1], right); Cartesian3.multiplyByScalar(right, 3.0, right); d[i + 1] = 1.0; right = r[i + 1]; if (!defined(right)) { right = r[i + 1] = new Cartesian3(); } Cartesian3.clone(lastTangent, right); return TridiagonalSystemSolver.solve(l, d, u, r); } function generateNatural(points) { var l = scratchLower; var u = scratchUpper; var d = scratchDiagonal; var r = scratchRight$3; l.length = u.length = points.length - 1; d.length = r.length = points.length; var i; l[0] = u[0] = 1.0; d[0] = 2.0; var right = r[0]; if (!defined(right)) { right = r[0] = new Cartesian3(); } Cartesian3.subtract(points[1], points[0], right); Cartesian3.multiplyByScalar(right, 3.0, right); for (i = 1; i < l.length; ++i) { l[i] = u[i] = 1.0; d[i] = 4.0; right = r[i]; if (!defined(right)) { right = r[i] = new Cartesian3(); } Cartesian3.subtract(points[i + 1], points[i - 1], right); Cartesian3.multiplyByScalar(right, 3.0, right); } d[i] = 2.0; right = r[i]; if (!defined(right)) { right = r[i] = new Cartesian3(); } Cartesian3.subtract(points[i], points[i - 1], right); Cartesian3.multiplyByScalar(right, 3.0, right); return TridiagonalSystemSolver.solve(l, d, u, r); } /** * A Hermite spline is a cubic interpolating spline. Points, incoming tangents, outgoing tangents, and times * must be defined for each control point. The outgoing tangents are defined for points [0, n - 2] and the incoming * tangents are defined for points [1, n - 1]. For example, when interpolating a segment of the curve between points[i] and * points[i + 1], the tangents at the points will be outTangents[i] and inTangents[i], * respectively. * * @alias HermiteSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. * The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Cartesian3[]} options.points The array of {@link Cartesian3} control points. * @param {Cartesian3[]} options.inTangents The array of {@link Cartesian3} incoming tangents at each control point. * @param {Cartesian3[]} options.outTangents The array of {@link Cartesian3} outgoing tangents at each control point. * * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * @exception {DeveloperError} inTangents and outTangents must have a length equal to points.length - 1. * * * @example * // Create a G1 continuous Hermite spline * var times = [ 0.0, 1.5, 3.0, 4.5, 6.0 ]; * var spline = new Cesium.HermiteSpline({ * times : times, * points : [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ], * outTangents : [ * new Cesium.Cartesian3(1125196, -161816, 270551), * new Cesium.Cartesian3(-996690.5, -365906.5, 184028.5), * new Cesium.Cartesian3(-2096917, 48379.5, -292683.5), * new Cesium.Cartesian3(-890902.5, 408999.5, -447115) * ], * inTangents : [ * new Cesium.Cartesian3(-1993381, -731813, 368057), * new Cesium.Cartesian3(-4193834, 96759, -585367), * new Cesium.Cartesian3(-1781805, 817999, -894230), * new Cesium.Cartesian3(1165345, 112641, 47281) * ] * }); * * var p0 = spline.evaluate(times[0]); * * @see CatmullRomSpline * @see LinearSpline * @see QuaternionSpline * @see WeightSpline */ function HermiteSpline(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var points = options.points; var times = options.times; var inTangents = options.inTangents; var outTangents = options.outTangents; //>>includeStart('debug', pragmas.debug); if ( !defined(points) || !defined(times) || !defined(inTangents) || !defined(outTangents) ) { throw new DeveloperError( "times, points, inTangents, and outTangents are required." ); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length) { throw new DeveloperError("times.length must be equal to points.length."); } if ( inTangents.length !== outTangents.length || inTangents.length !== points.length - 1 ) { throw new DeveloperError( "inTangents and outTangents must have a length equal to points.length - 1." ); } //>>includeEnd('debug'); this._times = times; this._points = points; this._inTangents = inTangents; this._outTangents = outTangents; this._lastTimeIndex = 0; } Object.defineProperties(HermiteSpline.prototype, { /** * An array of times for the control points. * * @memberof HermiteSpline.prototype * * @type {Number[]} * @readonly */ times: { get: function () { return this._times; }, }, /** * An array of {@link Cartesian3} control points. * * @memberof HermiteSpline.prototype * * @type {Cartesian3[]} * @readonly */ points: { get: function () { return this._points; }, }, /** * An array of {@link Cartesian3} incoming tangents at each control point. * * @memberof HermiteSpline.prototype * * @type {Cartesian3[]} * @readonly */ inTangents: { get: function () { return this._inTangents; }, }, /** * An array of {@link Cartesian3} outgoing tangents at each control point. * * @memberof HermiteSpline.prototype * * @type {Cartesian3[]} * @readonly */ outTangents: { get: function () { return this._outTangents; }, }, }); /** * Creates a spline where the tangents at each control point are the same. * The curves are guaranteed to be at least in the class C1. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Cartesian3[]} options.points The array of control points. * @param {Cartesian3[]} options.tangents The array of tangents at the control points. * @returns {HermiteSpline} A hermite spline. * * @exception {DeveloperError} points, times and tangents are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times, points and tangents must have the same length. * * @example * var points = [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ]; * * // Add tangents * var tangents = new Array(points.length); * tangents[0] = new Cesium.Cartesian3(1125196, -161816, 270551); * var temp = new Cesium.Cartesian3(); * for (var i = 1; i < tangents.length - 1; ++i) { * tangents[i] = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.subtract(points[i + 1], points[i - 1], temp), 0.5, new Cesium.Cartesian3()); * } * tangents[tangents.length - 1] = new Cesium.Cartesian3(1165345, 112641, 47281); * * var spline = Cesium.HermiteSpline.createC1({ * times : times, * points : points, * tangents : tangents * }); */ HermiteSpline.createC1 = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var times = options.times; var points = options.points; var tangents = options.tangents; //>>includeStart('debug', pragmas.debug); if (!defined(points) || !defined(times) || !defined(tangents)) { throw new DeveloperError("points, times and tangents are required."); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length || times.length !== tangents.length) { throw new DeveloperError( "times, points and tangents must have the same length." ); } //>>includeEnd('debug'); var outTangents = tangents.slice(0, tangents.length - 1); var inTangents = tangents.slice(1, tangents.length); return new HermiteSpline({ times: times, points: points, inTangents: inTangents, outTangents: outTangents, }); }; /** * Creates a natural cubic spline. The tangents at the control points are generated * to create a curve in the class C2. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Cartesian3[]} options.points The array of control points. * @returns {HermiteSpline|LinearSpline} A hermite spline or a linear spline if less than 3 control points were given. * * @exception {DeveloperError} points and times are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * @example * // Create a natural cubic spline above the earth from Philadelphia to Los Angeles. * var spline = Cesium.HermiteSpline.createNaturalCubic({ * times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ], * points : [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ] * }); */ HermiteSpline.createNaturalCubic = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var times = options.times; var points = options.points; //>>includeStart('debug', pragmas.debug); if (!defined(points) || !defined(times)) { throw new DeveloperError("points and times are required."); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length) { throw new DeveloperError("times.length must be equal to points.length."); } //>>includeEnd('debug'); if (points.length < 3) { return new LinearSpline({ points: points, times: times, }); } var tangents = generateNatural(points); var outTangents = tangents.slice(0, tangents.length - 1); var inTangents = tangents.slice(1, tangents.length); return new HermiteSpline({ times: times, points: points, inTangents: inTangents, outTangents: outTangents, }); }; /** * Creates a clamped cubic spline. The tangents at the interior control points are generated * to create a curve in the class C2. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Cartesian3[]} options.points The array of control points. * @param {Cartesian3} options.firstTangent The outgoing tangent of the first control point. * @param {Cartesian3} options.lastTangent The incoming tangent of the last control point. * @returns {HermiteSpline|LinearSpline} A hermite spline or a linear spline if less than 3 control points were given. * * @exception {DeveloperError} points, times, firstTangent and lastTangent are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * @example * // Create a clamped cubic spline above the earth from Philadelphia to Los Angeles. * var spline = Cesium.HermiteSpline.createClampedCubic({ * times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ], * points : [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ], * firstTangent : new Cesium.Cartesian3(1125196, -161816, 270551), * lastTangent : new Cesium.Cartesian3(1165345, 112641, 47281) * }); */ HermiteSpline.createClampedCubic = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var times = options.times; var points = options.points; var firstTangent = options.firstTangent; var lastTangent = options.lastTangent; //>>includeStart('debug', pragmas.debug); if ( !defined(points) || !defined(times) || !defined(firstTangent) || !defined(lastTangent) ) { throw new DeveloperError( "points, times, firstTangent and lastTangent are required." ); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length) { throw new DeveloperError("times.length must be equal to points.length."); } //>>includeEnd('debug'); if (points.length < 3) { return new LinearSpline({ points: points, times: times, }); } var tangents = generateClamped(points, firstTangent, lastTangent); var outTangents = tangents.slice(0, tangents.length - 1); var inTangents = tangents.slice(1, tangents.length); return new HermiteSpline({ times: times, points: points, inTangents: inTangents, outTangents: outTangents, }); }; HermiteSpline.hermiteCoefficientMatrix = new Matrix4( 2.0, -3.0, 0.0, 1.0, -2.0, 3.0, 0.0, 0.0, 1.0, -2.0, 1.0, 0.0, 1.0, -1.0, 0.0, 0.0 ); /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ HermiteSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; var scratchTimeVec$1 = new Cartesian4(); var scratchTemp = new Cartesian3(); /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */ HermiteSpline.prototype.wrapTime = Spline.prototype.wrapTime; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ HermiteSpline.prototype.clampTime = Spline.prototype.clampTime; /** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ HermiteSpline.prototype.evaluate = function (time, result) { if (!defined(result)) { result = new Cartesian3(); } var points = this.points; var times = this.times; var inTangents = this.inTangents; var outTangents = this.outTangents; var i = (this._lastTimeIndex = this.findTimeInterval( time, this._lastTimeIndex )); var u = (time - times[i]) / (times[i + 1] - times[i]); var timeVec = scratchTimeVec$1; timeVec.z = u; timeVec.y = u * u; timeVec.x = timeVec.y * u; timeVec.w = 1.0; var coefs = Matrix4.multiplyByVector( HermiteSpline.hermiteCoefficientMatrix, timeVec, timeVec ); result = Cartesian3.multiplyByScalar(points[i], coefs.x, result); Cartesian3.multiplyByScalar(points[i + 1], coefs.y, scratchTemp); Cartesian3.add(result, scratchTemp, result); Cartesian3.multiplyByScalar(outTangents[i], coefs.z, scratchTemp); Cartesian3.add(result, scratchTemp, result); Cartesian3.multiplyByScalar(inTangents[i], coefs.w, scratchTemp); return Cartesian3.add(result, scratchTemp, result); }; var scratchTimeVec = new Cartesian4(); var scratchTemp0 = new Cartesian3(); var scratchTemp1 = new Cartesian3(); function createEvaluateFunction$1(spline) { var points = spline.points; var times = spline.times; if (points.length < 3) { var t0 = times[0]; var invSpan = 1.0 / (times[1] - t0); var p0 = points[0]; var p1 = points[1]; return function (time, result) { if (!defined(result)) { result = new Cartesian3(); } var u = (time - t0) * invSpan; return Cartesian3.lerp(p0, p1, u, result); }; } return function (time, result) { if (!defined(result)) { result = new Cartesian3(); } var i = (spline._lastTimeIndex = spline.findTimeInterval( time, spline._lastTimeIndex )); var u = (time - times[i]) / (times[i + 1] - times[i]); var timeVec = scratchTimeVec; timeVec.z = u; timeVec.y = u * u; timeVec.x = timeVec.y * u; timeVec.w = 1.0; var p0; var p1; var p2; var p3; var coefs; if (i === 0) { p0 = points[0]; p1 = points[1]; p2 = spline.firstTangent; p3 = Cartesian3.subtract(points[2], p0, scratchTemp0); Cartesian3.multiplyByScalar(p3, 0.5, p3); coefs = Matrix4.multiplyByVector( HermiteSpline.hermiteCoefficientMatrix, timeVec, timeVec ); } else if (i === points.length - 2) { p0 = points[i]; p1 = points[i + 1]; p3 = spline.lastTangent; p2 = Cartesian3.subtract(p1, points[i - 1], scratchTemp0); Cartesian3.multiplyByScalar(p2, 0.5, p2); coefs = Matrix4.multiplyByVector( HermiteSpline.hermiteCoefficientMatrix, timeVec, timeVec ); } else { p0 = points[i - 1]; p1 = points[i]; p2 = points[i + 1]; p3 = points[i + 2]; coefs = Matrix4.multiplyByVector( CatmullRomSpline.catmullRomCoefficientMatrix, timeVec, timeVec ); } result = Cartesian3.multiplyByScalar(p0, coefs.x, result); Cartesian3.multiplyByScalar(p1, coefs.y, scratchTemp1); Cartesian3.add(result, scratchTemp1, result); Cartesian3.multiplyByScalar(p2, coefs.z, scratchTemp1); Cartesian3.add(result, scratchTemp1, result); Cartesian3.multiplyByScalar(p3, coefs.w, scratchTemp1); return Cartesian3.add(result, scratchTemp1, result); }; } var firstTangentScratch = new Cartesian3(); var lastTangentScratch = new Cartesian3(); /** * A Catmull-Rom spline is a cubic spline where the tangent at control points, * except the first and last, are computed using the previous and next control points. * Catmull-Rom splines are in the class C1. * * @alias CatmullRomSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. * The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Cartesian3[]} options.points The array of {@link Cartesian3} control points. * @param {Cartesian3} [options.firstTangent] The tangent of the curve at the first control point. * If the tangent is not given, it will be estimated. * @param {Cartesian3} [options.lastTangent] The tangent of the curve at the last control point. * If the tangent is not given, it will be estimated. * * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * * @example * // spline above the earth from Philadelphia to Los Angeles * var spline = new Cesium.CatmullRomSpline({ * times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ], * points : [ * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ] * }); * * var p0 = spline.evaluate(times[i]); // equal to positions[i] * var p1 = spline.evaluate(times[i] + delta); // interpolated value when delta < times[i + 1] - times[i] * * @see HermiteSpline * @see LinearSpline * @see QuaternionSpline * @see WeightSpline */ function CatmullRomSpline(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var points = options.points; var times = options.times; var firstTangent = options.firstTangent; var lastTangent = options.lastTangent; //>>includeStart('debug', pragmas.debug); Check.defined("points", points); Check.defined("times", times); Check.typeOf.number.greaterThanOrEquals("points.length", points.length, 2); Check.typeOf.number.equals( "times.length", "points.length", times.length, points.length ); //>>includeEnd('debug'); if (points.length > 2) { if (!defined(firstTangent)) { firstTangent = firstTangentScratch; Cartesian3.multiplyByScalar(points[1], 2.0, firstTangent); Cartesian3.subtract(firstTangent, points[2], firstTangent); Cartesian3.subtract(firstTangent, points[0], firstTangent); Cartesian3.multiplyByScalar(firstTangent, 0.5, firstTangent); } if (!defined(lastTangent)) { var n = points.length - 1; lastTangent = lastTangentScratch; Cartesian3.multiplyByScalar(points[n - 1], 2.0, lastTangent); Cartesian3.subtract(points[n], lastTangent, lastTangent); Cartesian3.add(lastTangent, points[n - 2], lastTangent); Cartesian3.multiplyByScalar(lastTangent, 0.5, lastTangent); } } this._times = times; this._points = points; this._firstTangent = Cartesian3.clone(firstTangent); this._lastTangent = Cartesian3.clone(lastTangent); this._evaluateFunction = createEvaluateFunction$1(this); this._lastTimeIndex = 0; } Object.defineProperties(CatmullRomSpline.prototype, { /** * An array of times for the control points. * * @memberof CatmullRomSpline.prototype * * @type {Number[]} * @readonly */ times: { get: function () { return this._times; }, }, /** * An array of {@link Cartesian3} control points. * * @memberof CatmullRomSpline.prototype * * @type {Cartesian3[]} * @readonly */ points: { get: function () { return this._points; }, }, /** * The tangent at the first control point. * * @memberof CatmullRomSpline.prototype * * @type {Cartesian3} * @readonly */ firstTangent: { get: function () { return this._firstTangent; }, }, /** * The tangent at the last control point. * * @memberof CatmullRomSpline.prototype * * @type {Cartesian3} * @readonly */ lastTangent: { get: function () { return this._lastTangent; }, }, }); /** * @private */ CatmullRomSpline.catmullRomCoefficientMatrix = new Matrix4( -0.5, 1.0, -0.5, 0.0, 1.5, -2.5, 0.0, 1.0, -1.5, 2.0, 0.5, 0.0, 0.5, -0.5, 0.0, 0.0 ); /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ CatmullRomSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */ CatmullRomSpline.prototype.wrapTime = Spline.prototype.wrapTime; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ CatmullRomSpline.prototype.clampTime = Spline.prototype.clampTime; /** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ CatmullRomSpline.prototype.evaluate = function (time, result) { return this._evaluateFunction(time, result); }; /** * Reads a string from a Uint8Array. * * @function * * @param {Uint8Array} uint8Array The Uint8Array to read from. * @param {Number} [byteOffset=0] The byte offset to start reading from. * @param {Number} [byteLength] The byte length to read. If byteLength is omitted the remainder of the buffer is read. * @returns {String} The string. * * @private */ function getStringFromTypedArray(uint8Array, byteOffset, byteLength) { //>>includeStart('debug', pragmas.debug); if (!defined(uint8Array)) { throw new DeveloperError("uint8Array is required."); } if (byteOffset < 0) { throw new DeveloperError("byteOffset cannot be negative."); } if (byteLength < 0) { throw new DeveloperError("byteLength cannot be negative."); } if (byteOffset + byteLength > uint8Array.byteLength) { throw new DeveloperError("sub-region exceeds array bounds."); } //>>includeEnd('debug'); byteOffset = defaultValue(byteOffset, 0); byteLength = defaultValue(byteLength, uint8Array.byteLength - byteOffset); uint8Array = uint8Array.subarray(byteOffset, byteOffset + byteLength); return getStringFromTypedArray.decode(uint8Array); } // Exposed functions for testing getStringFromTypedArray.decodeWithTextDecoder = function (view) { var decoder = new TextDecoder("utf-8"); return decoder.decode(view); }; getStringFromTypedArray.decodeWithFromCharCode = function (view) { var result = ""; var codePoints = utf8Handler(view); var length = codePoints.length; for (var i = 0; i < length; ++i) { var cp = codePoints[i]; if (cp <= 0xffff) { result += String.fromCharCode(cp); } else { cp -= 0x10000; result += String.fromCharCode((cp >> 10) + 0xd800, (cp & 0x3ff) + 0xdc00); } } return result; }; function inRange(a, min, max) { return min <= a && a <= max; } // This code is inspired by public domain code found here: https://github.com/inexorabletash/text-encoding function utf8Handler(utfBytes) { var codePoint = 0; var bytesSeen = 0; var bytesNeeded = 0; var lowerBoundary = 0x80; var upperBoundary = 0xbf; var codePoints = []; var length = utfBytes.length; for (var i = 0; i < length; ++i) { var currentByte = utfBytes[i]; // If bytesNeeded = 0, then we are starting a new character if (bytesNeeded === 0) { // 1 Byte Ascii character if (inRange(currentByte, 0x00, 0x7f)) { // Return a code point whose value is byte. codePoints.push(currentByte); continue; } // 2 Byte character if (inRange(currentByte, 0xc2, 0xdf)) { bytesNeeded = 1; codePoint = currentByte & 0x1f; continue; } // 3 Byte character if (inRange(currentByte, 0xe0, 0xef)) { // If byte is 0xE0, set utf-8 lower boundary to 0xA0. if (currentByte === 0xe0) { lowerBoundary = 0xa0; } // If byte is 0xED, set utf-8 upper boundary to 0x9F. if (currentByte === 0xed) { upperBoundary = 0x9f; } bytesNeeded = 2; codePoint = currentByte & 0xf; continue; } // 4 Byte character if (inRange(currentByte, 0xf0, 0xf4)) { // If byte is 0xF0, set utf-8 lower boundary to 0x90. if (currentByte === 0xf0) { lowerBoundary = 0x90; } // If byte is 0xF4, set utf-8 upper boundary to 0x8F. if (currentByte === 0xf4) { upperBoundary = 0x8f; } bytesNeeded = 3; codePoint = currentByte & 0x7; continue; } throw new RuntimeError("String decoding failed."); } // Out of range, so ignore the first part(s) of the character and continue with this byte on its own if (!inRange(currentByte, lowerBoundary, upperBoundary)) { codePoint = bytesNeeded = bytesSeen = 0; lowerBoundary = 0x80; upperBoundary = 0xbf; --i; continue; } // Set appropriate boundaries, since we've now checked byte 2 of a potential longer character lowerBoundary = 0x80; upperBoundary = 0xbf; // Add byte to code point codePoint = (codePoint << 6) | (currentByte & 0x3f); // We have the correct number of bytes, so push and reset for next character ++bytesSeen; if (bytesSeen === bytesNeeded) { codePoints.push(codePoint); codePoint = bytesNeeded = bytesSeen = 0; } } return codePoints; } if (typeof TextDecoder !== "undefined") { getStringFromTypedArray.decode = getStringFromTypedArray.decodeWithTextDecoder; } else { getStringFromTypedArray.decode = getStringFromTypedArray.decodeWithFromCharCode; } /** * Parses JSON from a Uint8Array. * * @function * * @param {Uint8Array} uint8Array The Uint8Array to read from. * @param {Number} [byteOffset=0] The byte offset to start reading from. * @param {Number} [byteLength] The byte length to read. If byteLength is omitted the remainder of the buffer is read. * @returns {Object} An object containing the parsed JSON. * * @private */ function getJsonFromTypedArray(uint8Array, byteOffset, byteLength) { return JSON.parse( getStringFromTypedArray(uint8Array, byteOffset, byteLength) ); } /** * Contains functions for operating on 2D triangles. * * @namespace Intersections2D */ var Intersections2D = {}; /** * Splits a 2D triangle at given axis-aligned threshold value and returns the resulting * polygon on a given side of the threshold. The resulting polygon may have 0, 1, 2, * 3, or 4 vertices. * * @param {Number} threshold The threshold coordinate value at which to clip the triangle. * @param {Boolean} keepAbove true to keep the portion of the triangle above the threshold, or false * to keep the portion below. * @param {Number} u0 The coordinate of the first vertex in the triangle, in counter-clockwise order. * @param {Number} u1 The coordinate of the second vertex in the triangle, in counter-clockwise order. * @param {Number} u2 The coordinate of the third vertex in the triangle, in counter-clockwise order. * @param {Number[]} [result] The array into which to copy the result. If this parameter is not supplied, * a new array is constructed and returned. * @returns {Number[]} The polygon that results after the clip, specified as a list of * vertices. The vertices are specified in counter-clockwise order. * Each vertex is either an index from the existing list (identified as * a 0, 1, or 2) or -1 indicating a new vertex not in the original triangle. * For new vertices, the -1 is followed by three additional numbers: the * index of each of the two original vertices forming the line segment that * the new vertex lies on, and the fraction of the distance from the first * vertex to the second one. * * @example * var result = Cesium.Intersections2D.clipTriangleAtAxisAlignedThreshold(0.5, false, 0.2, 0.6, 0.4); * // result === [2, 0, -1, 1, 0, 0.25, -1, 1, 2, 0.5] */ Intersections2D.clipTriangleAtAxisAlignedThreshold = function ( threshold, keepAbove, u0, u1, u2, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(threshold)) { throw new DeveloperError("threshold is required."); } if (!defined(keepAbove)) { throw new DeveloperError("keepAbove is required."); } if (!defined(u0)) { throw new DeveloperError("u0 is required."); } if (!defined(u1)) { throw new DeveloperError("u1 is required."); } if (!defined(u2)) { throw new DeveloperError("u2 is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = []; } else { result.length = 0; } var u0Behind; var u1Behind; var u2Behind; if (keepAbove) { u0Behind = u0 < threshold; u1Behind = u1 < threshold; u2Behind = u2 < threshold; } else { u0Behind = u0 > threshold; u1Behind = u1 > threshold; u2Behind = u2 > threshold; } var numBehind = u0Behind + u1Behind + u2Behind; var u01Ratio; var u02Ratio; var u12Ratio; var u10Ratio; var u20Ratio; var u21Ratio; if (numBehind === 1) { if (u0Behind) { u01Ratio = (threshold - u0) / (u1 - u0); u02Ratio = (threshold - u0) / (u2 - u0); result.push(1); result.push(2); if (u02Ratio !== 1.0) { result.push(-1); result.push(0); result.push(2); result.push(u02Ratio); } if (u01Ratio !== 1.0) { result.push(-1); result.push(0); result.push(1); result.push(u01Ratio); } } else if (u1Behind) { u12Ratio = (threshold - u1) / (u2 - u1); u10Ratio = (threshold - u1) / (u0 - u1); result.push(2); result.push(0); if (u10Ratio !== 1.0) { result.push(-1); result.push(1); result.push(0); result.push(u10Ratio); } if (u12Ratio !== 1.0) { result.push(-1); result.push(1); result.push(2); result.push(u12Ratio); } } else if (u2Behind) { u20Ratio = (threshold - u2) / (u0 - u2); u21Ratio = (threshold - u2) / (u1 - u2); result.push(0); result.push(1); if (u21Ratio !== 1.0) { result.push(-1); result.push(2); result.push(1); result.push(u21Ratio); } if (u20Ratio !== 1.0) { result.push(-1); result.push(2); result.push(0); result.push(u20Ratio); } } } else if (numBehind === 2) { if (!u0Behind && u0 !== threshold) { u10Ratio = (threshold - u1) / (u0 - u1); u20Ratio = (threshold - u2) / (u0 - u2); result.push(0); result.push(-1); result.push(1); result.push(0); result.push(u10Ratio); result.push(-1); result.push(2); result.push(0); result.push(u20Ratio); } else if (!u1Behind && u1 !== threshold) { u21Ratio = (threshold - u2) / (u1 - u2); u01Ratio = (threshold - u0) / (u1 - u0); result.push(1); result.push(-1); result.push(2); result.push(1); result.push(u21Ratio); result.push(-1); result.push(0); result.push(1); result.push(u01Ratio); } else if (!u2Behind && u2 !== threshold) { u02Ratio = (threshold - u0) / (u2 - u0); u12Ratio = (threshold - u1) / (u2 - u1); result.push(2); result.push(-1); result.push(0); result.push(2); result.push(u02Ratio); result.push(-1); result.push(1); result.push(2); result.push(u12Ratio); } } else if (numBehind !== 3) { // Completely in front of threshold result.push(0); result.push(1); result.push(2); } // else Completely behind threshold return result; }; /** * Compute the barycentric coordinates of a 2D position within a 2D triangle. * * @param {Number} x The x coordinate of the position for which to find the barycentric coordinates. * @param {Number} y The y coordinate of the position for which to find the barycentric coordinates. * @param {Number} x1 The x coordinate of the triangle's first vertex. * @param {Number} y1 The y coordinate of the triangle's first vertex. * @param {Number} x2 The x coordinate of the triangle's second vertex. * @param {Number} y2 The y coordinate of the triangle's second vertex. * @param {Number} x3 The x coordinate of the triangle's third vertex. * @param {Number} y3 The y coordinate of the triangle's third vertex. * @param {Cartesian3} [result] The instance into to which to copy the result. If this parameter * is undefined, a new instance is created and returned. * @returns {Cartesian3} The barycentric coordinates of the position within the triangle. * * @example * var result = Cesium.Intersections2D.computeBarycentricCoordinates(0.0, 0.0, 0.0, 1.0, -1, -0.5, 1, -0.5); * // result === new Cesium.Cartesian3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0); */ Intersections2D.computeBarycentricCoordinates = function ( x, y, x1, y1, x2, y2, x3, y3, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(x)) { throw new DeveloperError("x is required."); } if (!defined(y)) { throw new DeveloperError("y is required."); } if (!defined(x1)) { throw new DeveloperError("x1 is required."); } if (!defined(y1)) { throw new DeveloperError("y1 is required."); } if (!defined(x2)) { throw new DeveloperError("x2 is required."); } if (!defined(y2)) { throw new DeveloperError("y2 is required."); } if (!defined(x3)) { throw new DeveloperError("x3 is required."); } if (!defined(y3)) { throw new DeveloperError("y3 is required."); } //>>includeEnd('debug'); var x1mx3 = x1 - x3; var x3mx2 = x3 - x2; var y2my3 = y2 - y3; var y1my3 = y1 - y3; var inverseDeterminant = 1.0 / (y2my3 * x1mx3 + x3mx2 * y1my3); var ymy3 = y - y3; var xmx3 = x - x3; var l1 = (y2my3 * xmx3 + x3mx2 * ymy3) * inverseDeterminant; var l2 = (-y1my3 * xmx3 + x1mx3 * ymy3) * inverseDeterminant; var l3 = 1.0 - l1 - l2; if (defined(result)) { result.x = l1; result.y = l2; result.z = l3; return result; } return new Cartesian3(l1, l2, l3); }; /** * Compute the intersection between 2 line segments * * @param {Number} x00 The x coordinate of the first line's first vertex. * @param {Number} y00 The y coordinate of the first line's first vertex. * @param {Number} x01 The x coordinate of the first line's second vertex. * @param {Number} y01 The y coordinate of the first line's second vertex. * @param {Number} x10 The x coordinate of the second line's first vertex. * @param {Number} y10 The y coordinate of the second line's first vertex. * @param {Number} x11 The x coordinate of the second line's second vertex. * @param {Number} y11 The y coordinate of the second line's second vertex. * @param {Cartesian2} [result] The instance into to which to copy the result. If this parameter * is undefined, a new instance is created and returned. * @returns {Cartesian2} The intersection point, undefined if there is no intersection point or lines are coincident. * * @example * var result = Cesium.Intersections2D.computeLineSegmentLineSegmentIntersection(0.0, 0.0, 0.0, 2.0, -1, 1, 1, 1); * // result === new Cesium.Cartesian2(0.0, 1.0); */ Intersections2D.computeLineSegmentLineSegmentIntersection = function ( x00, y00, x01, y01, x10, y10, x11, y11, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("x00", x00); Check.typeOf.number("y00", y00); Check.typeOf.number("x01", x01); Check.typeOf.number("y01", y01); Check.typeOf.number("x10", x10); Check.typeOf.number("y10", y10); Check.typeOf.number("x11", x11); Check.typeOf.number("y11", y11); //>>includeEnd('debug'); var numerator1A = (x11 - x10) * (y00 - y10) - (y11 - y10) * (x00 - x10); var numerator1B = (x01 - x00) * (y00 - y10) - (y01 - y00) * (x00 - x10); var denominator1 = (y11 - y10) * (x01 - x00) - (x11 - x10) * (y01 - y00); // If denominator = 0, then lines are parallel. If denominator = 0 and both numerators are 0, then coincident if (denominator1 === 0) { return; } var ua1 = numerator1A / denominator1; var ub1 = numerator1B / denominator1; if (ua1 >= 0 && ua1 <= 1 && ub1 >= 0 && ub1 <= 1) { if (!defined(result)) { result = new Cartesian2(); } result.x = x00 + ua1 * (x01 - x00); result.y = y00 + ua1 * (y01 - y00); return result; } }; /** * Terrain data for a single tile where the terrain data is represented as a quantized mesh. A quantized * mesh consists of three vertex attributes, longitude, latitude, and height. All attributes are expressed * as 16-bit values in the range 0 to 32767. Longitude and latitude are zero at the southwest corner * of the tile and 32767 at the northeast corner. Height is zero at the minimum height in the tile * and 32767 at the maximum height in the tile. * * @alias QuantizedMeshTerrainData * @constructor * * @param {Object} options Object with the following properties: * @param {Uint16Array} options.quantizedVertices The buffer containing the quantized mesh. * @param {Uint16Array|Uint32Array} options.indices The indices specifying how the quantized vertices are linked * together into triangles. Each three indices specifies one triangle. * @param {Number} options.minimumHeight The minimum terrain height within the tile, in meters above the ellipsoid. * @param {Number} options.maximumHeight The maximum terrain height within the tile, in meters above the ellipsoid. * @param {BoundingSphere} options.boundingSphere A sphere bounding all of the vertices in the mesh. * @param {OrientedBoundingBox} [options.orientedBoundingBox] An OrientedBoundingBox bounding all of the vertices in the mesh. * @param {Cartesian3} options.horizonOcclusionPoint The horizon occlusion point of the mesh. If this point * is below the horizon, the entire tile is assumed to be below the horizon as well. * The point is expressed in ellipsoid-scaled coordinates. * @param {Number[]} options.westIndices The indices of the vertices on the western edge of the tile. * @param {Number[]} options.southIndices The indices of the vertices on the southern edge of the tile. * @param {Number[]} options.eastIndices The indices of the vertices on the eastern edge of the tile. * @param {Number[]} options.northIndices The indices of the vertices on the northern edge of the tile. * @param {Number} options.westSkirtHeight The height of the skirt to add on the western edge of the tile. * @param {Number} options.southSkirtHeight The height of the skirt to add on the southern edge of the tile. * @param {Number} options.eastSkirtHeight The height of the skirt to add on the eastern edge of the tile. * @param {Number} options.northSkirtHeight The height of the skirt to add on the northern edge of the tile. * @param {Number} [options.childTileMask=15] A bit mask indicating which of this tile's four children exist. * If a child's bit is set, geometry will be requested for that tile as well when it * is needed. If the bit is cleared, the child tile is not requested and geometry is * instead upsampled from the parent. The bit values are as follows: * * * * * * *
Bit PositionBit ValueChild Tile
01Southwest
12Southeast
24Northwest
38Northeast
* @param {Boolean} [options.createdByUpsampling=false] True if this instance was created by upsampling another instance; * otherwise, false. * @param {Uint8Array} [options.encodedNormals] The buffer containing per vertex normals, encoded using 'oct' encoding * @param {Uint8Array} [options.waterMask] The buffer containing the watermask. * @param {Credit[]} [options.credits] Array of credits for this tile. * * * @example * var data = new Cesium.QuantizedMeshTerrainData({ * minimumHeight : -100, * maximumHeight : 2101, * quantizedVertices : new Uint16Array([// order is SW NW SE NE * // longitude * 0, 0, 32767, 32767, * // latitude * 0, 32767, 0, 32767, * // heights * 16384, 0, 32767, 16384]), * indices : new Uint16Array([0, 3, 1, * 0, 2, 3]), * boundingSphere : new Cesium.BoundingSphere(new Cesium.Cartesian3(1.0, 2.0, 3.0), 10000), * orientedBoundingBox : new Cesium.OrientedBoundingBox(new Cesium.Cartesian3(1.0, 2.0, 3.0), Cesium.Matrix3.fromRotationX(Cesium.Math.PI, new Cesium.Matrix3())), * horizonOcclusionPoint : new Cesium.Cartesian3(3.0, 2.0, 1.0), * westIndices : [0, 1], * southIndices : [0, 1], * eastIndices : [2, 3], * northIndices : [1, 3], * westSkirtHeight : 1.0, * southSkirtHeight : 1.0, * eastSkirtHeight : 1.0, * northSkirtHeight : 1.0 * }); * * @see TerrainData * @see HeightmapTerrainData * @see GoogleEarthEnterpriseTerrainData */ function QuantizedMeshTerrainData(options) { //>>includeStart('debug', pragmas.debug) if (!defined(options) || !defined(options.quantizedVertices)) { throw new DeveloperError("options.quantizedVertices is required."); } if (!defined(options.indices)) { throw new DeveloperError("options.indices is required."); } if (!defined(options.minimumHeight)) { throw new DeveloperError("options.minimumHeight is required."); } if (!defined(options.maximumHeight)) { throw new DeveloperError("options.maximumHeight is required."); } if (!defined(options.maximumHeight)) { throw new DeveloperError("options.maximumHeight is required."); } if (!defined(options.boundingSphere)) { throw new DeveloperError("options.boundingSphere is required."); } if (!defined(options.horizonOcclusionPoint)) { throw new DeveloperError("options.horizonOcclusionPoint is required."); } if (!defined(options.westIndices)) { throw new DeveloperError("options.westIndices is required."); } if (!defined(options.southIndices)) { throw new DeveloperError("options.southIndices is required."); } if (!defined(options.eastIndices)) { throw new DeveloperError("options.eastIndices is required."); } if (!defined(options.northIndices)) { throw new DeveloperError("options.northIndices is required."); } if (!defined(options.westSkirtHeight)) { throw new DeveloperError("options.westSkirtHeight is required."); } if (!defined(options.southSkirtHeight)) { throw new DeveloperError("options.southSkirtHeight is required."); } if (!defined(options.eastSkirtHeight)) { throw new DeveloperError("options.eastSkirtHeight is required."); } if (!defined(options.northSkirtHeight)) { throw new DeveloperError("options.northSkirtHeight is required."); } //>>includeEnd('debug'); this._quantizedVertices = options.quantizedVertices; this._encodedNormals = options.encodedNormals; this._indices = options.indices; this._minimumHeight = options.minimumHeight; this._maximumHeight = options.maximumHeight; this._boundingSphere = options.boundingSphere; this._orientedBoundingBox = options.orientedBoundingBox; this._horizonOcclusionPoint = options.horizonOcclusionPoint; this._credits = options.credits; var vertexCount = this._quantizedVertices.length / 3; var uValues = (this._uValues = this._quantizedVertices.subarray( 0, vertexCount )); var vValues = (this._vValues = this._quantizedVertices.subarray( vertexCount, 2 * vertexCount )); this._heightValues = this._quantizedVertices.subarray( 2 * vertexCount, 3 * vertexCount ); // We don't assume that we can count on the edge vertices being sorted by u or v. function sortByV(a, b) { return vValues[a] - vValues[b]; } function sortByU(a, b) { return uValues[a] - uValues[b]; } this._westIndices = sortIndicesIfNecessary( options.westIndices, sortByV, vertexCount ); this._southIndices = sortIndicesIfNecessary( options.southIndices, sortByU, vertexCount ); this._eastIndices = sortIndicesIfNecessary( options.eastIndices, sortByV, vertexCount ); this._northIndices = sortIndicesIfNecessary( options.northIndices, sortByU, vertexCount ); this._westSkirtHeight = options.westSkirtHeight; this._southSkirtHeight = options.southSkirtHeight; this._eastSkirtHeight = options.eastSkirtHeight; this._northSkirtHeight = options.northSkirtHeight; this._childTileMask = defaultValue(options.childTileMask, 15); this._createdByUpsampling = defaultValue(options.createdByUpsampling, false); this._waterMask = options.waterMask; this._mesh = undefined; } Object.defineProperties(QuantizedMeshTerrainData.prototype, { /** * An array of credits for this tile. * @memberof QuantizedMeshTerrainData.prototype * @type {Credit[]} */ credits: { get: function () { return this._credits; }, }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. * @memberof QuantizedMeshTerrainData.prototype * @type {Uint8Array|HTMLImageElement|HTMLCanvasElement} */ waterMask: { get: function () { return this._waterMask; }, }, childTileMask: { get: function () { return this._childTileMask; }, }, canUpsample: { get: function () { return defined(this._mesh); }, }, }); var arrayScratch$1 = []; function sortIndicesIfNecessary(indices, sortFunction, vertexCount) { arrayScratch$1.length = indices.length; var needsSort = false; for (var i = 0, len = indices.length; i < len; ++i) { arrayScratch$1[i] = indices[i]; needsSort = needsSort || (i > 0 && sortFunction(indices[i - 1], indices[i]) > 0); } if (needsSort) { arrayScratch$1.sort(sortFunction); return IndexDatatype$1.createTypedArray(vertexCount, arrayScratch$1); } return indices; } var createMeshTaskName$1 = "createVerticesFromQuantizedTerrainMesh"; var createMeshTaskProcessorNoThrottle$1 = new TaskProcessor(createMeshTaskName$1); var createMeshTaskProcessorThrottle$1 = new TaskProcessor( createMeshTaskName$1, TerrainData.maximumAsynchronousTasks ); /** * Creates a {@link TerrainMesh} from this terrain data. * * @private * * @param {Object} options Object with the following properties: * @param {TilingScheme} options.tilingScheme The tiling scheme to which this tile belongs. * @param {Number} options.x The X coordinate of the tile for which to create the terrain data. * @param {Number} options.y The Y coordinate of the tile for which to create the terrain data. * @param {Number} options.level The level of the tile for which to create the terrain data. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * @param {Boolean} [options.throttle=true] If true, indicates that this operation will need to be retried if too many asynchronous mesh creations are already in progress. * @returns {Promise.|undefined} A promise for the terrain mesh, or undefined if too many * asynchronous mesh creations are already in progress and the operation should * be retried later. */ QuantizedMeshTerrainData.prototype.createMesh = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.tilingScheme", options.tilingScheme); Check.typeOf.number("options.x", options.x); Check.typeOf.number("options.y", options.y); Check.typeOf.number("options.level", options.level); //>>includeEnd('debug'); var tilingScheme = options.tilingScheme; var x = options.x; var y = options.y; var level = options.level; var exaggeration = defaultValue(options.exaggeration, 1.0); var throttle = defaultValue(options.throttle, true); var ellipsoid = tilingScheme.ellipsoid; var rectangle = tilingScheme.tileXYToRectangle(x, y, level); var createMeshTaskProcessor = throttle ? createMeshTaskProcessorThrottle$1 : createMeshTaskProcessorNoThrottle$1; var verticesPromise = createMeshTaskProcessor.scheduleTask({ minimumHeight: this._minimumHeight, maximumHeight: this._maximumHeight, quantizedVertices: this._quantizedVertices, octEncodedNormals: this._encodedNormals, includeWebMercatorT: true, indices: this._indices, westIndices: this._westIndices, southIndices: this._southIndices, eastIndices: this._eastIndices, northIndices: this._northIndices, westSkirtHeight: this._westSkirtHeight, southSkirtHeight: this._southSkirtHeight, eastSkirtHeight: this._eastSkirtHeight, northSkirtHeight: this._northSkirtHeight, rectangle: rectangle, relativeToCenter: this._boundingSphere.center, ellipsoid: ellipsoid, exaggeration: exaggeration, }); if (!defined(verticesPromise)) { // Postponed return undefined; } var that = this; return when(verticesPromise, function (result) { var vertexCountWithoutSkirts = that._quantizedVertices.length / 3; var vertexCount = vertexCountWithoutSkirts + that._westIndices.length + that._southIndices.length + that._eastIndices.length + that._northIndices.length; var indicesTypedArray = IndexDatatype$1.createTypedArray( vertexCount, result.indices ); var vertices = new Float32Array(result.vertices); var rtc = result.center; var minimumHeight = result.minimumHeight; var maximumHeight = result.maximumHeight; var boundingSphere = defaultValue( BoundingSphere.clone(result.boundingSphere), that._boundingSphere ); var obb = defaultValue( OrientedBoundingBox.clone(result.orientedBoundingBox), that._orientedBoundingBox ); var occludeePointInScaledSpace = defaultValue( Cartesian3.clone(result.occludeePointInScaledSpace), that._horizonOcclusionPoint ); var stride = result.vertexStride; var terrainEncoding = TerrainEncoding.clone(result.encoding); // Clone complex result objects because the transfer from the web worker // has stripped them down to JSON-style objects. that._mesh = new TerrainMesh( rtc, vertices, indicesTypedArray, result.indexCountWithoutSkirts, vertexCountWithoutSkirts, minimumHeight, maximumHeight, boundingSphere, occludeePointInScaledSpace, stride, obb, terrainEncoding, exaggeration, result.westIndicesSouthToNorth, result.southIndicesEastToWest, result.eastIndicesNorthToSouth, result.northIndicesWestToEast ); // Free memory received from server after mesh is created. that._quantizedVertices = undefined; that._encodedNormals = undefined; that._indices = undefined; that._uValues = undefined; that._vValues = undefined; that._heightValues = undefined; that._westIndices = undefined; that._southIndices = undefined; that._eastIndices = undefined; that._northIndices = undefined; return that._mesh; }); }; var upsampleTaskProcessor$1 = new TaskProcessor( "upsampleQuantizedTerrainMesh", TerrainData.maximumAsynchronousTasks ); /** * Upsamples this terrain data for use by a descendant tile. The resulting instance will contain a subset of the * vertices in this instance, interpolated if necessary. * * @param {TilingScheme} tilingScheme The tiling scheme of this terrain data. * @param {Number} thisX The X coordinate of this tile in the tiling scheme. * @param {Number} thisY The Y coordinate of this tile in the tiling scheme. * @param {Number} thisLevel The level of this tile in the tiling scheme. * @param {Number} descendantX The X coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantY The Y coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantLevel The level within the tiling scheme of the descendant tile for which we are upsampling. * @returns {Promise.|undefined} A promise for upsampled heightmap terrain data for the descendant tile, * or undefined if too many asynchronous upsample operations are in progress and the request has been * deferred. */ QuantizedMeshTerrainData.prototype.upsample = function ( tilingScheme, thisX, thisY, thisLevel, descendantX, descendantY, descendantLevel ) { //>>includeStart('debug', pragmas.debug); if (!defined(tilingScheme)) { throw new DeveloperError("tilingScheme is required."); } if (!defined(thisX)) { throw new DeveloperError("thisX is required."); } if (!defined(thisY)) { throw new DeveloperError("thisY is required."); } if (!defined(thisLevel)) { throw new DeveloperError("thisLevel is required."); } if (!defined(descendantX)) { throw new DeveloperError("descendantX is required."); } if (!defined(descendantY)) { throw new DeveloperError("descendantY is required."); } if (!defined(descendantLevel)) { throw new DeveloperError("descendantLevel is required."); } var levelDifference = descendantLevel - thisLevel; if (levelDifference > 1) { throw new DeveloperError( "Upsampling through more than one level at a time is not currently supported." ); } //>>includeEnd('debug'); var mesh = this._mesh; if (!defined(this._mesh)) { return undefined; } var isEastChild = thisX * 2 !== descendantX; var isNorthChild = thisY * 2 === descendantY; var ellipsoid = tilingScheme.ellipsoid; var childRectangle = tilingScheme.tileXYToRectangle( descendantX, descendantY, descendantLevel ); var upsamplePromise = upsampleTaskProcessor$1.scheduleTask({ vertices: mesh.vertices, vertexCountWithoutSkirts: mesh.vertexCountWithoutSkirts, indices: mesh.indices, indexCountWithoutSkirts: mesh.indexCountWithoutSkirts, encoding: mesh.encoding, minimumHeight: this._minimumHeight, maximumHeight: this._maximumHeight, isEastChild: isEastChild, isNorthChild: isNorthChild, childRectangle: childRectangle, ellipsoid: ellipsoid, exaggeration: mesh.exaggeration, }); if (!defined(upsamplePromise)) { // Postponed return undefined; } var shortestSkirt = Math.min(this._westSkirtHeight, this._eastSkirtHeight); shortestSkirt = Math.min(shortestSkirt, this._southSkirtHeight); shortestSkirt = Math.min(shortestSkirt, this._northSkirtHeight); var westSkirtHeight = isEastChild ? shortestSkirt * 0.5 : this._westSkirtHeight; var southSkirtHeight = isNorthChild ? shortestSkirt * 0.5 : this._southSkirtHeight; var eastSkirtHeight = isEastChild ? this._eastSkirtHeight : shortestSkirt * 0.5; var northSkirtHeight = isNorthChild ? this._northSkirtHeight : shortestSkirt * 0.5; var credits = this._credits; return when(upsamplePromise).then(function (result) { var quantizedVertices = new Uint16Array(result.vertices); var indicesTypedArray = IndexDatatype$1.createTypedArray( quantizedVertices.length / 3, result.indices ); var encodedNormals; if (defined(result.encodedNormals)) { encodedNormals = new Uint8Array(result.encodedNormals); } return new QuantizedMeshTerrainData({ quantizedVertices: quantizedVertices, indices: indicesTypedArray, encodedNormals: encodedNormals, minimumHeight: result.minimumHeight, maximumHeight: result.maximumHeight, boundingSphere: BoundingSphere.clone(result.boundingSphere), orientedBoundingBox: OrientedBoundingBox.clone( result.orientedBoundingBox ), horizonOcclusionPoint: Cartesian3.clone(result.horizonOcclusionPoint), westIndices: result.westIndices, southIndices: result.southIndices, eastIndices: result.eastIndices, northIndices: result.northIndices, westSkirtHeight: westSkirtHeight, southSkirtHeight: southSkirtHeight, eastSkirtHeight: eastSkirtHeight, northSkirtHeight: northSkirtHeight, childTileMask: 0, credits: credits, createdByUpsampling: true, }); }); }; var maxShort = 32767; var barycentricCoordinateScratch$1 = new Cartesian3(); /** * Computes the terrain height at a specified longitude and latitude. * * @param {Rectangle} rectangle The rectangle covered by this terrain data. * @param {Number} longitude The longitude in radians. * @param {Number} latitude The latitude in radians. * @returns {Number} The terrain height at the specified position. The position is clamped to * the rectangle, so expect incorrect results for positions far outside the rectangle. */ QuantizedMeshTerrainData.prototype.interpolateHeight = function ( rectangle, longitude, latitude ) { var u = CesiumMath.clamp( (longitude - rectangle.west) / rectangle.width, 0.0, 1.0 ); u *= maxShort; var v = CesiumMath.clamp( (latitude - rectangle.south) / rectangle.height, 0.0, 1.0 ); v *= maxShort; if (!defined(this._mesh)) { return interpolateHeight$1(this, u, v); } return interpolateMeshHeight$1(this, u, v); }; function pointInBoundingBox(u, v, u0, v0, u1, v1, u2, v2) { var minU = Math.min(u0, u1, u2); var maxU = Math.max(u0, u1, u2); var minV = Math.min(v0, v1, v2); var maxV = Math.max(v0, v1, v2); return u >= minU && u <= maxU && v >= minV && v <= maxV; } var texCoordScratch0$1 = new Cartesian2(); var texCoordScratch1$1 = new Cartesian2(); var texCoordScratch2$1 = new Cartesian2(); function interpolateMeshHeight$1(terrainData, u, v) { var mesh = terrainData._mesh; var vertices = mesh.vertices; var encoding = mesh.encoding; var indices = mesh.indices; for (var i = 0, len = indices.length; i < len; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; var i2 = indices[i + 2]; var uv0 = encoding.decodeTextureCoordinates(vertices, i0, texCoordScratch0$1); var uv1 = encoding.decodeTextureCoordinates(vertices, i1, texCoordScratch1$1); var uv2 = encoding.decodeTextureCoordinates(vertices, i2, texCoordScratch2$1); if (pointInBoundingBox(u, v, uv0.x, uv0.y, uv1.x, uv1.y, uv2.x, uv2.y)) { var barycentric = Intersections2D.computeBarycentricCoordinates( u, v, uv0.x, uv0.y, uv1.x, uv1.y, uv2.x, uv2.y, barycentricCoordinateScratch$1 ); if ( barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15 ) { var h0 = encoding.decodeHeight(vertices, i0); var h1 = encoding.decodeHeight(vertices, i1); var h2 = encoding.decodeHeight(vertices, i2); return barycentric.x * h0 + barycentric.y * h1 + barycentric.z * h2; } } } // Position does not lie in any triangle in this mesh. return undefined; } function interpolateHeight$1(terrainData, u, v) { var uBuffer = terrainData._uValues; var vBuffer = terrainData._vValues; var heightBuffer = terrainData._heightValues; var indices = terrainData._indices; for (var i = 0, len = indices.length; i < len; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; var i2 = indices[i + 2]; var u0 = uBuffer[i0]; var u1 = uBuffer[i1]; var u2 = uBuffer[i2]; var v0 = vBuffer[i0]; var v1 = vBuffer[i1]; var v2 = vBuffer[i2]; if (pointInBoundingBox(u, v, u0, v0, u1, v1, u2, v2)) { var barycentric = Intersections2D.computeBarycentricCoordinates( u, v, u0, v0, u1, v1, u2, v2, barycentricCoordinateScratch$1 ); if ( barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15 ) { var quantizedHeight = barycentric.x * heightBuffer[i0] + barycentric.y * heightBuffer[i1] + barycentric.z * heightBuffer[i2]; return CesiumMath.lerp( terrainData._minimumHeight, terrainData._maximumHeight, quantizedHeight / maxShort ); } } } // Position does not lie in any triangle in this mesh. return undefined; } /** * Determines if a given child tile is available, based on the * {@link HeightmapTerrainData.childTileMask}. The given child tile coordinates are assumed * to be one of the four children of this tile. If non-child tile coordinates are * given, the availability of the southeast child tile is returned. * * @param {Number} thisX The tile X coordinate of this (the parent) tile. * @param {Number} thisY The tile Y coordinate of this (the parent) tile. * @param {Number} childX The tile X coordinate of the child tile to check for availability. * @param {Number} childY The tile Y coordinate of the child tile to check for availability. * @returns {Boolean} True if the child tile is available; otherwise, false. */ QuantizedMeshTerrainData.prototype.isChildAvailable = function ( thisX, thisY, childX, childY ) { //>>includeStart('debug', pragmas.debug); if (!defined(thisX)) { throw new DeveloperError("thisX is required."); } if (!defined(thisY)) { throw new DeveloperError("thisY is required."); } if (!defined(childX)) { throw new DeveloperError("childX is required."); } if (!defined(childY)) { throw new DeveloperError("childY is required."); } //>>includeEnd('debug'); var bitNumber = 2; // northwest child if (childX !== thisX * 2) { ++bitNumber; // east child } if (childY !== thisY * 2) { bitNumber -= 2; // south child } return (this._childTileMask & (1 << bitNumber)) !== 0; }; /** * Gets a value indicating whether or not this terrain data was created by upsampling lower resolution * terrain data. If this value is false, the data was obtained from some other source, such * as by downloading it from a remote server. This method should return true for instances * returned from a call to {@link HeightmapTerrainData#upsample}. * * @returns {Boolean} True if this instance was created by upsampling; otherwise, false. */ QuantizedMeshTerrainData.prototype.wasCreatedByUpsampling = function () { return this._createdByUpsampling; }; function LayerInformation(layer) { this.resource = layer.resource; this.version = layer.version; this.isHeightmap = layer.isHeightmap; this.tileUrlTemplates = layer.tileUrlTemplates; this.availability = layer.availability; this.hasVertexNormals = layer.hasVertexNormals; this.hasWaterMask = layer.hasWaterMask; this.hasMetadata = layer.hasMetadata; this.availabilityLevels = layer.availabilityLevels; this.availabilityTilesLoaded = layer.availabilityTilesLoaded; this.littleEndianExtensionSize = layer.littleEndianExtensionSize; this.availabilityPromiseCache = {}; } /** * A {@link TerrainProvider} that accesses terrain data in a Cesium terrain format. * * @alias CesiumTerrainProvider * @constructor * * @param {Object} options Object with the following properties: * @param {Resource|String|Promise|Promise} options.url The URL of the Cesium terrain server. * @param {Boolean} [options.requestVertexNormals=false] Flag that indicates if the client should request additional lighting information from the server, in the form of per vertex normals if available. * @param {Boolean} [options.requestWaterMask=false] Flag that indicates if the client should request per tile water masks from the server, if available. * @param {Boolean} [options.requestMetadata=true] Flag that indicates if the client should request per tile metadata from the server, if available. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * * * @example * // Create Arctic DEM terrain with normals. * var viewer = new Cesium.Viewer('cesiumContainer', { * terrainProvider : new Cesium.CesiumTerrainProvider({ * url : Cesium.IonResource.fromAssetId(3956), * requestVertexNormals : true * }) * }); * * @see createWorldTerrain * @see TerrainProvider */ function CesiumTerrainProvider(options) { //>>includeStart('debug', pragmas.debug) if (!defined(options) || !defined(options.url)) { throw new DeveloperError("options.url is required."); } //>>includeEnd('debug'); this._heightmapWidth = 65; this._heightmapStructure = undefined; this._hasWaterMask = false; this._hasVertexNormals = false; this._ellipsoid = options.ellipsoid; /** * Boolean flag that indicates if the client should request vertex normals from the server. * @type {Boolean} * @default false * @private */ this._requestVertexNormals = defaultValue( options.requestVertexNormals, false ); /** * Boolean flag that indicates if the client should request tile watermasks from the server. * @type {Boolean} * @default false * @private */ this._requestWaterMask = defaultValue(options.requestWaterMask, false); /** * Boolean flag that indicates if the client should request tile metadata from the server. * @type {Boolean} * @default true * @private */ this._requestMetadata = defaultValue(options.requestMetadata, true); this._errorEvent = new Event(); var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } this._credit = credit; this._availability = undefined; var deferred = when.defer(); this._ready = false; this._readyPromise = deferred; this._tileCredits = undefined; var that = this; var lastResource; var layerJsonResource; var metadataError; var layers = (this._layers = []); var attribution = ""; var overallAvailability = []; var overallMaxZoom = 0; when(options.url) .then(function (url) { var resource = Resource.createIfNeeded(url); resource.appendForwardSlash(); lastResource = resource; layerJsonResource = lastResource.getDerivedResource({ url: "layer.json", }); // ion resources have a credits property we can use for additional attribution. that._tileCredits = resource.credits; requestLayerJson(); }) .otherwise(function (e) { deferred.reject(e); }); function parseMetadataSuccess(data) { var message; if (!data.format) { message = "The tile format is not specified in the layer.json file."; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); return; } if (!data.tiles || data.tiles.length === 0) { message = "The layer.json file does not specify any tile URL templates."; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); return; } var hasVertexNormals = false; var hasWaterMask = false; var hasMetadata = false; var littleEndianExtensionSize = true; var isHeightmap = false; if (data.format === "heightmap-1.0") { isHeightmap = true; if (!defined(that._heightmapStructure)) { that._heightmapStructure = { heightScale: 1.0 / 5.0, heightOffset: -1000.0, elementsPerHeight: 1, stride: 1, elementMultiplier: 256.0, isBigEndian: false, lowestEncodedHeight: 0, highestEncodedHeight: 256 * 256 - 1, }; } hasWaterMask = true; that._requestWaterMask = true; } else if (data.format.indexOf("quantized-mesh-1.") !== 0) { message = 'The tile format "' + data.format + '" is invalid or not supported.'; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); return; } var tileUrlTemplates = data.tiles; var maxZoom = data.maxzoom; overallMaxZoom = Math.max(overallMaxZoom, maxZoom); // Keeps track of which of the availablity containing tiles have been loaded if (!data.projection || data.projection === "EPSG:4326") { that._tilingScheme = new GeographicTilingScheme({ numberOfLevelZeroTilesX: 2, numberOfLevelZeroTilesY: 1, ellipsoid: that._ellipsoid, }); } else if (data.projection === "EPSG:3857") { that._tilingScheme = new WebMercatorTilingScheme({ numberOfLevelZeroTilesX: 1, numberOfLevelZeroTilesY: 1, ellipsoid: that._ellipsoid, }); } else { message = 'The projection "' + data.projection + '" is invalid or not supported.'; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); return; } that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( that._tilingScheme.ellipsoid, that._heightmapWidth, that._tilingScheme.getNumberOfXTilesAtLevel(0) ); if (!data.scheme || data.scheme === "tms" || data.scheme === "slippyMap") { that._scheme = data.scheme; } else { message = 'The scheme "' + data.scheme + '" is invalid or not supported.'; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); return; } var availabilityTilesLoaded; // The vertex normals defined in the 'octvertexnormals' extension is identical to the original // contents of the original 'vertexnormals' extension. 'vertexnormals' extension is now // deprecated, as the extensionLength for this extension was incorrectly using big endian. // We maintain backwards compatibility with the legacy 'vertexnormal' implementation // by setting the _littleEndianExtensionSize to false. Always prefer 'octvertexnormals' // over 'vertexnormals' if both extensions are supported by the server. if ( defined(data.extensions) && data.extensions.indexOf("octvertexnormals") !== -1 ) { hasVertexNormals = true; } else if ( defined(data.extensions) && data.extensions.indexOf("vertexnormals") !== -1 ) { hasVertexNormals = true; littleEndianExtensionSize = false; } if ( defined(data.extensions) && data.extensions.indexOf("watermask") !== -1 ) { hasWaterMask = true; } if ( defined(data.extensions) && data.extensions.indexOf("metadata") !== -1 ) { hasMetadata = true; } var availabilityLevels = data.metadataAvailability; var availableTiles = data.available; var availability; if (defined(availableTiles) && !defined(availabilityLevels)) { availability = new TileAvailability( that._tilingScheme, availableTiles.length ); for (var level = 0; level < availableTiles.length; ++level) { var rangesAtLevel = availableTiles[level]; var yTiles = that._tilingScheme.getNumberOfYTilesAtLevel(level); if (!defined(overallAvailability[level])) { overallAvailability[level] = []; } for ( var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex ) { var range = rangesAtLevel[rangeIndex]; var yStart = yTiles - range.endY - 1; var yEnd = yTiles - range.startY - 1; overallAvailability[level].push([ range.startX, yStart, range.endX, yEnd, ]); availability.addAvailableTileRange( level, range.startX, yStart, range.endX, yEnd ); } } } else if (defined(availabilityLevels)) { availabilityTilesLoaded = new TileAvailability( that._tilingScheme, maxZoom ); availability = new TileAvailability(that._tilingScheme, maxZoom); overallAvailability[0] = [[0, 0, 1, 0]]; availability.addAvailableTileRange(0, 0, 0, 1, 0); } that._hasWaterMask = that._hasWaterMask || hasWaterMask; that._hasVertexNormals = that._hasVertexNormals || hasVertexNormals; that._hasMetadata = that._hasMetadata || hasMetadata; if (defined(data.attribution)) { if (attribution.length > 0) { attribution += " "; } attribution += data.attribution; } layers.push( new LayerInformation({ resource: lastResource, version: data.version, isHeightmap: isHeightmap, tileUrlTemplates: tileUrlTemplates, availability: availability, hasVertexNormals: hasVertexNormals, hasWaterMask: hasWaterMask, hasMetadata: hasMetadata, availabilityLevels: availabilityLevels, availabilityTilesLoaded: availabilityTilesLoaded, littleEndianExtensionSize: littleEndianExtensionSize, }) ); var parentUrl = data.parentUrl; if (defined(parentUrl)) { if (!defined(availability)) { console.log( "A layer.json can't have a parentUrl if it does't have an available array." ); return when.resolve(); } lastResource = lastResource.getDerivedResource({ url: parentUrl, }); lastResource.appendForwardSlash(); // Terrain always expects a directory layerJsonResource = lastResource.getDerivedResource({ url: "layer.json", }); var parentMetadata = layerJsonResource.fetchJson(); return when(parentMetadata, parseMetadataSuccess, parseMetadataFailure); } return when.resolve(); } function parseMetadataFailure(data) { var message = "An error occurred while accessing " + layerJsonResource.url + "."; metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestLayerJson ); } function metadataSuccess(data) { parseMetadataSuccess(data).then(function () { if (defined(metadataError)) { return; } var length = overallAvailability.length; if (length > 0) { var availability = (that._availability = new TileAvailability( that._tilingScheme, overallMaxZoom )); for (var level = 0; level < length; ++level) { var levelRanges = overallAvailability[level]; for (var i = 0; i < levelRanges.length; ++i) { var range = levelRanges[i]; availability.addAvailableTileRange( level, range[0], range[1], range[2], range[3] ); } } } if (attribution.length > 0) { var layerJsonCredit = new Credit(attribution); if (defined(that._tileCredits)) { that._tileCredits.push(layerJsonCredit); } else { that._tileCredits = [layerJsonCredit]; } } that._ready = true; that._readyPromise.resolve(true); }); } function metadataFailure(data) { // If the metadata is not found, assume this is a pre-metadata heightmap tileset. if (defined(data) && data.statusCode === 404) { metadataSuccess({ tilejson: "2.1.0", format: "heightmap-1.0", version: "1.0.0", scheme: "tms", tiles: ["{z}/{x}/{y}.terrain?v={version}"], }); return; } parseMetadataFailure(); } function requestLayerJson() { when(layerJsonResource.fetchJson()) .then(metadataSuccess) .otherwise(metadataFailure); } } /** * When using the Quantized-Mesh format, a tile may be returned that includes additional extensions, such as PerVertexNormals, watermask, etc. * This enumeration defines the unique identifiers for each type of extension data that has been appended to the standard mesh data. * * @namespace QuantizedMeshExtensionIds * @see CesiumTerrainProvider * @private */ var QuantizedMeshExtensionIds = { /** * Oct-Encoded Per-Vertex Normals are included as an extension to the tile mesh * * @type {Number} * @constant * @default 1 */ OCT_VERTEX_NORMALS: 1, /** * A watermask is included as an extension to the tile mesh * * @type {Number} * @constant * @default 2 */ WATER_MASK: 2, /** * A json object contain metadata about the tile * * @type {Number} * @constant * @default 4 */ METADATA: 4, }; function getRequestHeader(extensionsList) { if (!defined(extensionsList) || extensionsList.length === 0) { return { Accept: "application/vnd.quantized-mesh,application/octet-stream;q=0.9,*/*;q=0.01", }; } var extensions = extensionsList.join("-"); return { Accept: "application/vnd.quantized-mesh;extensions=" + extensions + ",application/octet-stream;q=0.9,*/*;q=0.01", }; } function createHeightmapTerrainData(provider, buffer, level, x, y) { var heightBuffer = new Uint16Array( buffer, 0, provider._heightmapWidth * provider._heightmapWidth ); return new HeightmapTerrainData({ buffer: heightBuffer, childTileMask: new Uint8Array(buffer, heightBuffer.byteLength, 1)[0], waterMask: new Uint8Array( buffer, heightBuffer.byteLength + 1, buffer.byteLength - heightBuffer.byteLength - 1 ), width: provider._heightmapWidth, height: provider._heightmapWidth, structure: provider._heightmapStructure, credits: provider._tileCredits, }); } function createQuantizedMeshTerrainData(provider, buffer, level, x, y, layer) { var littleEndianExtensionSize = layer.littleEndianExtensionSize; var pos = 0; var cartesian3Elements = 3; var boundingSphereElements = cartesian3Elements + 1; var cartesian3Length = Float64Array.BYTES_PER_ELEMENT * cartesian3Elements; var boundingSphereLength = Float64Array.BYTES_PER_ELEMENT * boundingSphereElements; var encodedVertexElements = 3; var encodedVertexLength = Uint16Array.BYTES_PER_ELEMENT * encodedVertexElements; var triangleElements = 3; var bytesPerIndex = Uint16Array.BYTES_PER_ELEMENT; var triangleLength = bytesPerIndex * triangleElements; var view = new DataView(buffer); var center = new Cartesian3( view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true) ); pos += cartesian3Length; var minimumHeight = view.getFloat32(pos, true); pos += Float32Array.BYTES_PER_ELEMENT; var maximumHeight = view.getFloat32(pos, true); pos += Float32Array.BYTES_PER_ELEMENT; var boundingSphere = new BoundingSphere( new Cartesian3( view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true) ), view.getFloat64(pos + cartesian3Length, true) ); pos += boundingSphereLength; var horizonOcclusionPoint = new Cartesian3( view.getFloat64(pos, true), view.getFloat64(pos + 8, true), view.getFloat64(pos + 16, true) ); pos += cartesian3Length; var vertexCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3); pos += vertexCount * encodedVertexLength; if (vertexCount > 64 * 1024) { // More than 64k vertices, so indices are 32-bit. bytesPerIndex = Uint32Array.BYTES_PER_ELEMENT; triangleLength = bytesPerIndex * triangleElements; } // Decode the vertex buffer. var uBuffer = encodedVertexBuffer.subarray(0, vertexCount); var vBuffer = encodedVertexBuffer.subarray(vertexCount, 2 * vertexCount); var heightBuffer = encodedVertexBuffer.subarray( vertexCount * 2, 3 * vertexCount ); AttributeCompression.zigZagDeltaDecode(uBuffer, vBuffer, heightBuffer); // skip over any additional padding that was added for 2/4 byte alignment if (pos % bytesPerIndex !== 0) { pos += bytesPerIndex - (pos % bytesPerIndex); } var triangleCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var indices = IndexDatatype$1.createTypedArrayFromArrayBuffer( vertexCount, buffer, pos, triangleCount * triangleElements ); pos += triangleCount * triangleLength; // High water mark decoding based on decompressIndices_ in webgl-loader's loader.js. // https://code.google.com/p/webgl-loader/source/browse/trunk/samples/loader.js?r=99#55 // Copyright 2012 Google Inc., Apache 2.0 license. var highest = 0; var length = indices.length; for (var i = 0; i < length; ++i) { var code = indices[i]; indices[i] = highest - code; if (code === 0) { ++highest; } } var westVertexCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var westIndices = IndexDatatype$1.createTypedArrayFromArrayBuffer( vertexCount, buffer, pos, westVertexCount ); pos += westVertexCount * bytesPerIndex; var southVertexCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var southIndices = IndexDatatype$1.createTypedArrayFromArrayBuffer( vertexCount, buffer, pos, southVertexCount ); pos += southVertexCount * bytesPerIndex; var eastVertexCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var eastIndices = IndexDatatype$1.createTypedArrayFromArrayBuffer( vertexCount, buffer, pos, eastVertexCount ); pos += eastVertexCount * bytesPerIndex; var northVertexCount = view.getUint32(pos, true); pos += Uint32Array.BYTES_PER_ELEMENT; var northIndices = IndexDatatype$1.createTypedArrayFromArrayBuffer( vertexCount, buffer, pos, northVertexCount ); pos += northVertexCount * bytesPerIndex; var encodedNormalBuffer; var waterMaskBuffer; while (pos < view.byteLength) { var extensionId = view.getUint8(pos, true); pos += Uint8Array.BYTES_PER_ELEMENT; var extensionLength = view.getUint32(pos, littleEndianExtensionSize); pos += Uint32Array.BYTES_PER_ELEMENT; if ( extensionId === QuantizedMeshExtensionIds.OCT_VERTEX_NORMALS && provider._requestVertexNormals ) { encodedNormalBuffer = new Uint8Array(buffer, pos, vertexCount * 2); } else if ( extensionId === QuantizedMeshExtensionIds.WATER_MASK && provider._requestWaterMask ) { waterMaskBuffer = new Uint8Array(buffer, pos, extensionLength); } else if ( extensionId === QuantizedMeshExtensionIds.METADATA && provider._requestMetadata ) { var stringLength = view.getUint32(pos, true); if (stringLength > 0) { var metadata = getJsonFromTypedArray( new Uint8Array(buffer), pos + Uint32Array.BYTES_PER_ELEMENT, stringLength ); var availableTiles = metadata.available; if (defined(availableTiles)) { for (var offset = 0; offset < availableTiles.length; ++offset) { var availableLevel = level + offset + 1; var rangesAtLevel = availableTiles[offset]; var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel( availableLevel ); for ( var rangeIndex = 0; rangeIndex < rangesAtLevel.length; ++rangeIndex ) { var range = rangesAtLevel[rangeIndex]; var yStart = yTiles - range.endY - 1; var yEnd = yTiles - range.startY - 1; provider.availability.addAvailableTileRange( availableLevel, range.startX, yStart, range.endX, yEnd ); layer.availability.addAvailableTileRange( availableLevel, range.startX, yStart, range.endX, yEnd ); } } } } layer.availabilityTilesLoaded.addAvailableTileRange(level, x, y, x, y); } pos += extensionLength; } var skirtHeight = provider.getLevelMaximumGeometricError(level) * 5.0; // The skirt is not included in the OBB computation. If this ever // causes any rendering artifacts (cracks), they are expected to be // minor and in the corners of the screen. It's possible that this // might need to be changed - just change to `minimumHeight - skirtHeight` // A similar change might also be needed in `upsampleQuantizedTerrainMesh.js`. var rectangle = provider._tilingScheme.tileXYToRectangle(x, y, level); var orientedBoundingBox = OrientedBoundingBox.fromRectangle( rectangle, minimumHeight, maximumHeight, provider._tilingScheme.ellipsoid ); return new QuantizedMeshTerrainData({ center: center, minimumHeight: minimumHeight, maximumHeight: maximumHeight, boundingSphere: boundingSphere, orientedBoundingBox: orientedBoundingBox, horizonOcclusionPoint: horizonOcclusionPoint, quantizedVertices: encodedVertexBuffer, encodedNormals: encodedNormalBuffer, indices: indices, westIndices: westIndices, southIndices: southIndices, eastIndices: eastIndices, northIndices: northIndices, westSkirtHeight: skirtHeight, southSkirtHeight: skirtHeight, eastSkirtHeight: skirtHeight, northSkirtHeight: skirtHeight, childTileMask: provider.availability.computeChildMaskForTile(level, x, y), waterMask: waterMaskBuffer, credits: provider._tileCredits, }); } /** * Requests the geometry for a given tile. This function should not be called before * {@link CesiumTerrainProvider#ready} returns true. The result must include terrain data and * may optionally include a water mask and an indication of which child tiles are available. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. * * @exception {DeveloperError} This function must not be called before {@link CesiumTerrainProvider#ready} * returns true. */ CesiumTerrainProvider.prototype.requestTileGeometry = function ( x, y, level, request ) { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "requestTileGeometry must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); var layers = this._layers; var layerToUse; var layerCount = layers.length; if (layerCount === 1) { // Optimized path for single layers layerToUse = layers[0]; } else { for (var i = 0; i < layerCount; ++i) { var layer = layers[i]; if ( !defined(layer.availability) || layer.availability.isTileAvailable(level, x, y) ) { layerToUse = layer; break; } } } return requestTileGeometry$1(this, x, y, level, layerToUse, request); }; function requestTileGeometry$1(provider, x, y, level, layerToUse, request) { if (!defined(layerToUse)) { return when.reject(new RuntimeError("Terrain tile doesn't exist")); } var urlTemplates = layerToUse.tileUrlTemplates; if (urlTemplates.length === 0) { return undefined; } // The TileMapService scheme counts from the bottom left var terrainY; if (!provider._scheme || provider._scheme === "tms") { var yTiles = provider._tilingScheme.getNumberOfYTilesAtLevel(level); terrainY = yTiles - y - 1; } else { terrainY = y; } var extensionList = []; if (provider._requestVertexNormals && layerToUse.hasVertexNormals) { extensionList.push( layerToUse.littleEndianExtensionSize ? "octvertexnormals" : "vertexnormals" ); } if (provider._requestWaterMask && layerToUse.hasWaterMask) { extensionList.push("watermask"); } if (provider._requestMetadata && layerToUse.hasMetadata) { extensionList.push("metadata"); } var headers; var query; var url = urlTemplates[(x + terrainY + level) % urlTemplates.length]; var resource = layerToUse.resource; if ( defined(resource._ionEndpoint) && !defined(resource._ionEndpoint.externalType) ) { // ion uses query paremeters to request extensions if (extensionList.length !== 0) { query = { extensions: extensionList.join("-") }; } headers = getRequestHeader(undefined); } else { //All other terrain servers headers = getRequestHeader(extensionList); } var promise = resource .getDerivedResource({ url: url, templateValues: { version: layerToUse.version, z: level, x: x, y: terrainY, }, queryParameters: query, headers: headers, request: request, }) .fetchArrayBuffer(); if (!defined(promise)) { return undefined; } return promise.then(function (buffer) { if (defined(provider._heightmapStructure)) { return createHeightmapTerrainData(provider, buffer); } return createQuantizedMeshTerrainData( provider, buffer, level, x, y, layerToUse ); }); } Object.defineProperties(CesiumTerrainProvider.prototype, { /** * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof CesiumTerrainProvider.prototype * @type {Event} */ errorEvent: { get: function () { return this._errorEvent; }, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should not be called before {@link CesiumTerrainProvider#ready} returns true. * @memberof CesiumTerrainProvider.prototype * @type {Credit} */ credit: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "credit must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); return this._credit; }, }, /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link CesiumTerrainProvider#ready} returns true. * @memberof CesiumTerrainProvider.prototype * @type {GeographicTilingScheme} */ tilingScheme: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "tilingScheme must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); return this._tilingScheme; }, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof CesiumTerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link CesiumTerrainProvider#ready} returns true. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready} */ hasWaterMask: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "hasWaterMask must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); return this._hasWaterMask && this._requestWaterMask; }, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link CesiumTerrainProvider#ready} returns true. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready} */ hasVertexNormals: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "hasVertexNormals must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); // returns true if we can request vertex normals from the server return this._hasVertexNormals && this._requestVertexNormals; }, }, /** * Gets a value indicating whether or not the requested tiles include metadata. * This function should not be called before {@link CesiumTerrainProvider#ready} returns true. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} * @exception {DeveloperError} This property must not be called before {@link CesiumTerrainProvider#ready} */ hasMetadata: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "hasMetadata must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); // returns true if we can request metadata from the server return this._hasMetadata && this._requestMetadata; }, }, /** * Boolean flag that indicates if the client should request vertex normals from the server. * Vertex normals data is appended to the standard tile mesh data only if the client requests the vertex normals and * if the server provides vertex normals. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} */ requestVertexNormals: { get: function () { return this._requestVertexNormals; }, }, /** * Boolean flag that indicates if the client should request a watermask from the server. * Watermask data is appended to the standard tile mesh data only if the client requests the watermask and * if the server provides a watermask. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} */ requestWaterMask: { get: function () { return this._requestWaterMask; }, }, /** * Boolean flag that indicates if the client should request metadata from the server. * Metadata is appended to the standard tile mesh data only if the client requests the metadata and * if the server provides a metadata. * @memberof CesiumTerrainProvider.prototype * @type {Boolean} */ requestMetadata: { get: function () { return this._requestMetadata; }, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link CesiumTerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. Note that this reflects tiles that are known to be available currently. * Additional tiles may be discovered to be available in the future, e.g. if availability information * exists deeper in the tree rather than it all being discoverable at the root. However, a tile that * is available now will not become unavailable in the future. * @memberof CesiumTerrainProvider.prototype * @type {TileAvailability} */ availability: { get: function () { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "availability must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); return this._availability; }, }, }); /** * Gets the maximum geometric error allowed in a tile at a given level. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ CesiumTerrainProvider.prototype.getLevelMaximumGeometricError = function ( level ) { return this._levelZeroMaximumGeometricError / (1 << level); }; /** * Determines whether data for a tile is available to be loaded. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported or availability is unknown, otherwise true or false. */ CesiumTerrainProvider.prototype.getTileDataAvailable = function (x, y, level) { if (!defined(this._availability)) { return undefined; } if (level > this._availability._maximumLevel) { return false; } if (this._availability.isTileAvailable(level, x, y)) { // If the tile is listed as available, then we are done return true; } if (!this._hasMetadata) { // If we don't have any layers with the metadata extension then we don't have this tile return false; } var layers = this._layers; var count = layers.length; for (var i = 0; i < count; ++i) { var layerResult = checkLayer(this, x, y, level, layers[i], i === 0); if (layerResult.result) { // There is a layer that may or may not have the tile return undefined; } } return false; }; /** * Makes sure we load availability data for a tile * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ CesiumTerrainProvider.prototype.loadTileDataAvailability = function ( x, y, level ) { if ( !defined(this._availability) || level > this._availability._maximumLevel || this._availability.isTileAvailable(level, x, y) || !this._hasMetadata ) { // We know the tile is either available or not available so nothing to wait on return undefined; } var layers = this._layers; var count = layers.length; for (var i = 0; i < count; ++i) { var layerResult = checkLayer(this, x, y, level, layers[i], i === 0); if (defined(layerResult.promise)) { return layerResult.promise; } } }; function getAvailabilityTile(layer, x, y, level) { if (level === 0) { return; } var availabilityLevels = layer.availabilityLevels; var parentLevel = level % availabilityLevels === 0 ? level - availabilityLevels : ((level / availabilityLevels) | 0) * availabilityLevels; var divisor = 1 << (level - parentLevel); var parentX = (x / divisor) | 0; var parentY = (y / divisor) | 0; return { level: parentLevel, x: parentX, y: parentY, }; } function checkLayer(provider, x, y, level, layer, topLayer) { if (!defined(layer.availabilityLevels)) { // It's definitely not in this layer return { result: false, }; } var cacheKey; var deleteFromCache = function () { delete layer.availabilityPromiseCache[cacheKey]; }; var availabilityTilesLoaded = layer.availabilityTilesLoaded; var availability = layer.availability; var tile = getAvailabilityTile(layer, x, y, level); while (defined(tile)) { if ( availability.isTileAvailable(tile.level, tile.x, tile.y) && !availabilityTilesLoaded.isTileAvailable(tile.level, tile.x, tile.y) ) { var requestPromise; if (!topLayer) { cacheKey = tile.level + "-" + tile.x + "-" + tile.y; requestPromise = layer.availabilityPromiseCache[cacheKey]; if (!defined(requestPromise)) { // For cutout terrain, if this isn't the top layer the availability tiles // may never get loaded, so request it here. var request = new Request({ throttle: false, throttleByServer: true, type: RequestType$1.TERRAIN, }); requestPromise = requestTileGeometry$1( provider, tile.x, tile.y, tile.level, layer, request ); if (defined(requestPromise)) { layer.availabilityPromiseCache[cacheKey] = requestPromise; requestPromise.then(deleteFromCache); } } } // The availability tile is available, but not loaded, so there // is still a chance that it may become available at some point return { result: true, promise: requestPromise, }; } tile = getAvailabilityTile(layer, tile.x, tile.y, tile.level); } return { result: false, }; } // Used for testing CesiumTerrainProvider._getAvailabilityTile = getAvailabilityTile; var EllipseGeometryLibrary = {}; var rotAxis = new Cartesian3(); var tempVec = new Cartesian3(); var unitQuat = new Quaternion(); var rotMtx = new Matrix3(); function pointOnEllipsoid( theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, result ) { var azimuth = theta + rotation; Cartesian3.multiplyByScalar(eastVec, Math.cos(azimuth), rotAxis); Cartesian3.multiplyByScalar(northVec, Math.sin(azimuth), tempVec); Cartesian3.add(rotAxis, tempVec, rotAxis); var cosThetaSquared = Math.cos(theta); cosThetaSquared = cosThetaSquared * cosThetaSquared; var sinThetaSquared = Math.sin(theta); sinThetaSquared = sinThetaSquared * sinThetaSquared; var radius = ab / Math.sqrt(bSqr * cosThetaSquared + aSqr * sinThetaSquared); var angle = radius / mag; // Create the quaternion to rotate the position vector to the boundary of the ellipse. Quaternion.fromAxisAngle(rotAxis, angle, unitQuat); Matrix3.fromQuaternion(unitQuat, rotMtx); Matrix3.multiplyByVector(rotMtx, unitPos, result); Cartesian3.normalize(result, result); Cartesian3.multiplyByScalar(result, mag, result); return result; } var scratchCartesian1$7 = new Cartesian3(); var scratchCartesian2$a = new Cartesian3(); var scratchCartesian3$b = new Cartesian3(); var scratchNormal$6 = new Cartesian3(); /** * Returns the positions raised to the given heights * @private */ EllipseGeometryLibrary.raisePositionsToHeight = function ( positions, options, extrude ) { var ellipsoid = options.ellipsoid; var height = options.height; var extrudedHeight = options.extrudedHeight; var size = extrude ? (positions.length / 3) * 2 : positions.length / 3; var finalPositions = new Float64Array(size * 3); var length = positions.length; var bottomOffset = extrude ? length : 0; for (var i = 0; i < length; i += 3) { var i1 = i + 1; var i2 = i + 2; var position = Cartesian3.fromArray(positions, i, scratchCartesian1$7); ellipsoid.scaleToGeodeticSurface(position, position); var extrudedPosition = Cartesian3.clone(position, scratchCartesian2$a); var normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal$6); var scaledNormal = Cartesian3.multiplyByScalar( normal, height, scratchCartesian3$b ); Cartesian3.add(position, scaledNormal, position); if (extrude) { Cartesian3.multiplyByScalar(normal, extrudedHeight, scaledNormal); Cartesian3.add(extrudedPosition, scaledNormal, extrudedPosition); finalPositions[i + bottomOffset] = extrudedPosition.x; finalPositions[i1 + bottomOffset] = extrudedPosition.y; finalPositions[i2 + bottomOffset] = extrudedPosition.z; } finalPositions[i] = position.x; finalPositions[i1] = position.y; finalPositions[i2] = position.z; } return finalPositions; }; var unitPosScratch = new Cartesian3(); var eastVecScratch = new Cartesian3(); var northVecScratch = new Cartesian3(); /** * Returns an array of positions that make up the ellipse. * @private */ EllipseGeometryLibrary.computeEllipsePositions = function ( options, addFillPositions, addEdgePositions ) { var semiMinorAxis = options.semiMinorAxis; var semiMajorAxis = options.semiMajorAxis; var rotation = options.rotation; var center = options.center; // Computing the arc-length of the ellipse is too expensive to be practical. Estimating it using the // arc length of the sphere is too inaccurate and creates sharp edges when either the semi-major or // semi-minor axis is much bigger than the other. Instead, scale the angle delta to make // the distance along the ellipse boundary more closely match the granularity. var granularity = options.granularity * 8.0; var aSqr = semiMinorAxis * semiMinorAxis; var bSqr = semiMajorAxis * semiMajorAxis; var ab = semiMajorAxis * semiMinorAxis; var mag = Cartesian3.magnitude(center); var unitPos = Cartesian3.normalize(center, unitPosScratch); var eastVec = Cartesian3.cross(Cartesian3.UNIT_Z, center, eastVecScratch); eastVec = Cartesian3.normalize(eastVec, eastVec); var northVec = Cartesian3.cross(unitPos, eastVec, northVecScratch); // The number of points in the first quadrant var numPts = 1 + Math.ceil(CesiumMath.PI_OVER_TWO / granularity); var deltaTheta = CesiumMath.PI_OVER_TWO / (numPts - 1); var theta = CesiumMath.PI_OVER_TWO - numPts * deltaTheta; if (theta < 0.0) { numPts -= Math.ceil(Math.abs(theta) / deltaTheta); } // If the number of points were three, the ellipse // would be tessellated like below: // // *---* // / | \ | \ // *---*---*---* // / | \ | \ | \ | \ // / .*---*---*---*. \ // * ` | \ | \ | \ | `* // \`.*---*---*---*.`/ // \ | \ | \ | \ | / // *---*---*---* // \ | \ | / // *---* // The first and last column have one position and fan to connect to the adjacent column. // Each other vertical column contains an even number of positions. var size = 2 * (numPts * (numPts + 2)); var positions = addFillPositions ? new Array(size * 3) : undefined; var positionIndex = 0; var position = scratchCartesian1$7; var reflectedPosition = scratchCartesian2$a; var outerPositionsLength = numPts * 4 * 3; var outerRightIndex = outerPositionsLength - 1; var outerLeftIndex = 0; var outerPositions = addEdgePositions ? new Array(outerPositionsLength) : undefined; var i; var j; var numInterior; var t; var interiorPosition; // Compute points in the 'eastern' half of the ellipse theta = CesiumMath.PI_OVER_TWO; position = pointOnEllipsoid( theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position ); if (addFillPositions) { positions[positionIndex++] = position.x; positions[positionIndex++] = position.y; positions[positionIndex++] = position.z; } if (addEdgePositions) { outerPositions[outerRightIndex--] = position.z; outerPositions[outerRightIndex--] = position.y; outerPositions[outerRightIndex--] = position.x; } theta = CesiumMath.PI_OVER_TWO - deltaTheta; for (i = 1; i < numPts + 1; ++i) { position = pointOnEllipsoid( theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position ); reflectedPosition = pointOnEllipsoid( Math.PI - theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, reflectedPosition ); if (addFillPositions) { positions[positionIndex++] = position.x; positions[positionIndex++] = position.y; positions[positionIndex++] = position.z; numInterior = 2 * i + 2; for (j = 1; j < numInterior - 1; ++j) { t = j / (numInterior - 1); interiorPosition = Cartesian3.lerp( position, reflectedPosition, t, scratchCartesian3$b ); positions[positionIndex++] = interiorPosition.x; positions[positionIndex++] = interiorPosition.y; positions[positionIndex++] = interiorPosition.z; } positions[positionIndex++] = reflectedPosition.x; positions[positionIndex++] = reflectedPosition.y; positions[positionIndex++] = reflectedPosition.z; } if (addEdgePositions) { outerPositions[outerRightIndex--] = position.z; outerPositions[outerRightIndex--] = position.y; outerPositions[outerRightIndex--] = position.x; outerPositions[outerLeftIndex++] = reflectedPosition.x; outerPositions[outerLeftIndex++] = reflectedPosition.y; outerPositions[outerLeftIndex++] = reflectedPosition.z; } theta = CesiumMath.PI_OVER_TWO - (i + 1) * deltaTheta; } // Compute points in the 'western' half of the ellipse for (i = numPts; i > 1; --i) { theta = CesiumMath.PI_OVER_TWO - (i - 1) * deltaTheta; position = pointOnEllipsoid( -theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position ); reflectedPosition = pointOnEllipsoid( theta + Math.PI, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, reflectedPosition ); if (addFillPositions) { positions[positionIndex++] = position.x; positions[positionIndex++] = position.y; positions[positionIndex++] = position.z; numInterior = 2 * (i - 1) + 2; for (j = 1; j < numInterior - 1; ++j) { t = j / (numInterior - 1); interiorPosition = Cartesian3.lerp( position, reflectedPosition, t, scratchCartesian3$b ); positions[positionIndex++] = interiorPosition.x; positions[positionIndex++] = interiorPosition.y; positions[positionIndex++] = interiorPosition.z; } positions[positionIndex++] = reflectedPosition.x; positions[positionIndex++] = reflectedPosition.y; positions[positionIndex++] = reflectedPosition.z; } if (addEdgePositions) { outerPositions[outerRightIndex--] = position.z; outerPositions[outerRightIndex--] = position.y; outerPositions[outerRightIndex--] = position.x; outerPositions[outerLeftIndex++] = reflectedPosition.x; outerPositions[outerLeftIndex++] = reflectedPosition.y; outerPositions[outerLeftIndex++] = reflectedPosition.z; } } theta = CesiumMath.PI_OVER_TWO; position = pointOnEllipsoid( -theta, rotation, northVec, eastVec, aSqr, ab, bSqr, mag, unitPos, position ); var r = {}; if (addFillPositions) { positions[positionIndex++] = position.x; positions[positionIndex++] = position.y; positions[positionIndex++] = position.z; r.positions = positions; r.numPts = numPts; } if (addEdgePositions) { outerPositions[outerRightIndex--] = position.z; outerPositions[outerRightIndex--] = position.y; outerPositions[outerRightIndex--] = position.x; r.outerPositions = outerPositions; } return r; }; /** * Geometry instancing allows one {@link Geometry} object to be positions in several * different locations and colored uniquely. For example, one {@link BoxGeometry} can * be instanced several times, each with a different modelMatrix to change * its position, rotation, and scale. * * @alias GeometryInstance * @constructor * * @param {Object} options Object with the following properties: * @param {Geometry|GeometryFactory} options.geometry The geometry to instance. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The model matrix that transforms to transform the geometry from model to world coordinates. * @param {Object} [options.id] A user-defined object to return when the instance is picked with {@link Scene#pick} or get/set per-instance attributes with {@link Primitive#getGeometryInstanceAttributes}. * @param {Object} [options.attributes] Per-instance attributes like a show or color attribute shown in the example below. * * * @example * // Create geometry for a box, and two instances that refer to it. * // One instance positions the box on the bottom and colored aqua. * // The other instance positions the box on the top and color white. * var geometry = Cesium.BoxGeometry.fromDimensions({ * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL, * dimensions : new Cesium.Cartesian3(1000000.0, 1000000.0, 500000.0) * }); * var instanceBottom = new Cesium.GeometryInstance({ * geometry : geometry, * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 1000000.0), new Cesium.Matrix4()), * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA) * }, * id : 'bottom' * }); * var instanceTop = new Cesium.GeometryInstance({ * geometry : geometry, * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 3000000.0), new Cesium.Matrix4()), * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA) * }, * id : 'top' * }); * * @see Geometry */ function GeometryInstance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!defined(options.geometry)) { throw new DeveloperError("options.geometry is required."); } //>>includeEnd('debug'); /** * The geometry being instanced. * * @type Geometry * * @default undefined */ this.geometry = options.geometry; /** * The 4x4 transformation matrix that transforms the geometry from model to world coordinates. * When this is the identity matrix, the geometry is drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type Matrix4 * * @default Matrix4.IDENTITY */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); /** * User-defined object returned when the instance is picked or used to get/set per-instance attributes. * * @type Object * * @default undefined * * @see Scene#pick * @see Primitive#getGeometryInstanceAttributes */ this.id = options.id; /** * Used for picking primitives that wrap geometry instances. * * @private */ this.pickPrimitive = options.pickPrimitive; /** * Per-instance attributes like {@link ColorGeometryInstanceAttribute} or {@link ShowGeometryInstanceAttribute}. * {@link Geometry} attributes varying per vertex; these attributes are constant for the entire instance. * * @type Object * * @default undefined */ this.attributes = defaultValue(options.attributes, {}); /** * @private */ this.westHemisphereGeometry = undefined; /** * @private */ this.eastHemisphereGeometry = undefined; } var scratchCartesian1$6 = new Cartesian3(); var scratchCartesian2$9 = new Cartesian3(); var scratchCartesian3$a = new Cartesian3(); /** * Computes the barycentric coordinates for a point with respect to a triangle. * * @function * * @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. * * @example * // Returns Cartesian3.UNIT_X * var p = new Cesium.Cartesian3(-1.0, 0.0, 0.0); * var b = Cesium.barycentricCoordinates(p, * new Cesium.Cartesian3(-1.0, 0.0, 0.0), * new Cesium.Cartesian3( 1.0, 0.0, 0.0), * new Cesium.Cartesian3( 0.0, 1.0, 1.0)); */ function barycentricCoordinates(point, p0, p1, p2, result) { //>>includeStart('debug', pragmas.debug); Check.defined("point", point); Check.defined("p0", p0); Check.defined("p1", p1); Check.defined("p2", p2); //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian3(); } // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html. var v0; var v1; var v2; var dot00; var dot01; var dot02; var dot11; var dot12; if (!defined(p0.z)) { if (Cartesian2.equalsEpsilon(point, p0, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_X, result); } if (Cartesian2.equalsEpsilon(point, p1, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_Y, result); } if (Cartesian2.equalsEpsilon(point, p2, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_Z, result); } v0 = Cartesian2.subtract(p1, p0, scratchCartesian1$6); v1 = Cartesian2.subtract(p2, p0, scratchCartesian2$9); v2 = Cartesian2.subtract(point, p0, scratchCartesian3$a); 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 { if (Cartesian3.equalsEpsilon(point, p0, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_X, result); } if (Cartesian3.equalsEpsilon(point, p1, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_Y, result); } if (Cartesian3.equalsEpsilon(point, p2, CesiumMath.EPSILON14)) { return Cartesian3.clone(Cartesian3.UNIT_Z, result); } v0 = Cartesian3.subtract(p1, p0, scratchCartesian1$6); v1 = Cartesian3.subtract(p2, p0, scratchCartesian2$9); v2 = Cartesian3.subtract(point, p0, scratchCartesian3$a); 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); } result.y = dot11 * dot02 - dot01 * dot12; result.z = dot00 * dot12 - dot01 * dot02; var q = dot00 * dot11 - dot01 * dot01; // This is done to avoid dividing by infinity causing a NaN if (result.y !== 0) { result.y /= q; } if (result.z !== 0) { result.z /= q; } result.x = 1.0 - result.y - result.z; return result; } /** * 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. *

* This is used to encode positions in vertex buffers for rendering without jittering artifacts * as described in {@link http://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}. *

* * @alias EncodedCartesian3 * @constructor * * @private */ function EncodedCartesian3() { /** * The high bits for each component. Bits 0 to 22 store the whole value. Bits 23 to 31 are not used. * * @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. * * @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 high and low properties for the high and low bits, respectively. *

* The fixed-point encoding follows {@link http://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}. *

* * @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. * * @example * var value = 1234567.1234567; * var splitValue = Cesium.EncodedCartesian3.encode(value); */ EncodedCartesian3.encode = function (value, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); 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. *

* The fixed-point encoding follows {@link https://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}. *

* * @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. * * @example * var cart = new Cesium.Cartesian3(-10000000.0, 0.0, 10000000.0); * var encoded = Cesium.EncodedCartesian3.fromCartesian(cart); */ EncodedCartesian3.fromCartesian = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); 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 cartesian, and writes it to an array with high * components followed by low components, i.e. [high.x, high.y, high.z, low.x, low.y, low.z]. *

* This is used to create interleaved high-precision position vertex attributes. *

* * @param {Cartesian3} cartesian The cartesian to encode. * @param {Number[]} cartesianArray The array to write to. * @param {Number} index The index into the array to start writing. Six elements will be written. * * @exception {DeveloperError} index must be a number greater than or equal to 0. * * @example * var positions = [ * new Cesium.Cartesian3(), * // ... * ]; * var encodedPositions = new Float32Array(2 * 3 * positions.length); * var j = 0; * for (var i = 0; i < positions.length; ++i) { * Cesium.EncodedCartesian3.writeElement(positions[i], encodedPositions, j); * j += 6; * } */ EncodedCartesian3.writeElements = function (cartesian, cartesianArray, index) { //>>includeStart('debug', pragmas.debug); Check.defined("cartesianArray", cartesianArray); Check.typeOf.number("index", index); Check.typeOf.number.greaterThanOrEquals("index", index, 0); //>>includeEnd('debug'); 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; }; /** * 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. * * @namespace Tipsify * * @see * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw * by Sander, Nehab, and Barczak * * @private */ var Tipsify = {}; /** * Calculates the average cache miss ratio (ACMR) for a given set of indices. * * @param {Object} options Object with the following properties: * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices * in the vertex buffer that define the geometry's triangles. * @param {Number} [options.maximumIndex] The maximum value of the elements in args.indices. * If not supplied, this value will be computed. * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time. * @returns {Number} The average cache miss ratio (ACMR). * * @exception {DeveloperError} indices length must be a multiple of three. * @exception {DeveloperError} cacheSize must be greater than two. * * @example * var indices = [0, 1, 2, 3, 4, 5]; * var maxIndex = 5; * var cacheSize = 3; * var acmr = Cesium.Tipsify.calculateACMR({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); */ Tipsify.calculateACMR = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var indices = options.indices; var maximumIndex = options.maximumIndex; var cacheSize = defaultValue(options.cacheSize, 24); //>>includeStart('debug', pragmas.debug); if (!defined(indices)) { throw new DeveloperError("indices is required."); } //>>includeEnd('debug'); var numIndices = indices.length; //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); // 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 {Object} options Object with the following properties: * @param {Number[]} options.indices Lists triads of numbers corresponding to the indices of the vertices * in the vertex buffer that define the geometry's triangles. * @param {Number} [options.maximumIndex] The maximum value of the elements in args.indices. * If not supplied, this value will be computed. * @param {Number} [options.cacheSize=24] The number of vertices that can be stored in the cache at any one time. * @returns {Number[]} A list of the input indices in an optimized order. * * @exception {DeveloperError} indices length must be a multiple of three. * @exception {DeveloperError} cacheSize must be greater than two. * * @example * var indices = [0, 1, 2, 3, 4, 5]; * var maxIndex = 5; * var cacheSize = 3; * var reorderedIndices = Cesium.Tipsify.tipsify({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); */ Tipsify.tipsify = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var indices = options.indices; var maximumIndex = options.maximumIndex; var cacheSize = defaultValue(options.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; } //>>includeStart('debug', pragmas.debug); if (!defined(indices)) { throw new DeveloperError("indices is required."); } //>>includeEnd('debug'); var numIndices = indices.length; //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); // 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 = []; var i; for (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; }; /** * Content pipeline functions for geometries. * * @namespace GeometryPipeline * * @see Geometry */ 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$1.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$1.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$1.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 indices * and its primitiveType is TRIANGLES, TRIANGLE_STRIP, * TRIANGLE_FAN, it is converted to LINES; otherwise, the geometry is not changed. *

* This is commonly used to create a wireframe geometry for visual debugging. *

* * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified geometry argument, with its triangle indices converted to lines. * * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN. * * @example * geometry = Cesium.GeometryPipeline.toWireframe(geometry); */ GeometryPipeline.toWireframe = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug'); var indices = geometry.indices; if (defined(indices)) { switch (geometry.primitiveType) { case PrimitiveType$1.TRIANGLES: geometry.indices = trianglesToLines(indices); break; case PrimitiveType$1.TRIANGLE_STRIP: geometry.indices = triangleStripToLines(indices); break; case PrimitiveType$1.TRIANGLE_FAN: geometry.indices = triangleFanToLines(indices); break; //>>includeStart('debug', pragmas.debug); default: throw new DeveloperError( "geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN." ); //>>includeEnd('debug'); } geometry.primitiveType = PrimitiveType$1.LINES; } return geometry; }; /** * Creates a new {@link Geometry} with LINES representing the provided * attribute (attributeName) for the provided geometry. This is used to * visualize vector attributes like normals, tangents, and bitangents. * * @param {Geometry} geometry The Geometry 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 Geometry instance with line segments for the vector. * * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter. * * @example * var geometry = Cesium.GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'bitangent', 100000.0); */ GeometryPipeline.createLineSegmentsForVectors = function ( geometry, attributeName, length ) { attributeName = defaultValue(attributeName, "normal"); //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } if (!defined(geometry.attributes.position)) { throw new DeveloperError("geometry.attributes.position is required."); } if (!defined(geometry.attributes[attributeName])) { throw new DeveloperError( "geometry.attributes must have an attribute with the same name as the attributeName parameter, " + attributeName + "." ); } //>>includeEnd('debug'); 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$1.DOUBLE, componentsPerAttribute: 3, values: newPositions, }), }, primitiveType: PrimitiveType$1.LINES, boundingSphere: newBoundingSphere, }); }; /** * Creates an object that maps attribute names to unique locations (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. * * @example * var attributeLocations = Cesium.GeometryPipeline.createAttributeLocations(geometry); * // Example output * // { * // 'position' : 0, * // 'normal' : 1 * // } */ GeometryPipeline.createAttributeLocations = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug') // 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", "tangent", "bitangent", // For shadow volumes "extrudeDirection", // From compressing texture coordinates and normals "compressedAttributes", ]; 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 indices to achieve better performance from the GPU's pre-vertex-shader cache. * * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified geometry argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache. * * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes. * * * @example * geometry = Cesium.GeometryPipeline.reorderForPreVertexCache(geometry); * * @see GeometryPipeline.reorderForPostVertexCache */ GeometryPipeline.reorderForPreVertexCache = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug'); 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$1.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$1.createTypedArray( attribute.componentDatatype, nextIndex * numComponents ); while (intoElementsIn < numVertices) { var temp = indexCrossReferenceOldToNew[intoElementsIn]; if (temp !== -1) { for (var j = 0; j < numComponents; j++) { elementsOut[numComponents * temp + j] = elementsIn[numComponents * intoElementsIn + j]; } } ++intoElementsIn; } attribute.values = elementsOut; } } } return geometry; }; /** * Reorders a geometry's indices to achieve better performance from the GPU's * post vertex-shader cache by using the Tipsify algorithm. If the geometry primitiveType * is not TRIANGLES or the geometry does not have an indices, 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 geometry argument, with its indices reordered for the post-vertex-shader cache. * * @exception {DeveloperError} cacheCapacity must be greater than two. * * * @example * geometry = Cesium.GeometryPipeline.reorderForPostVertexCache(geometry); * * @see GeometryPipeline.reorderForPreVertexCache * @see {@link http://gfx.cs.princ0eton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf|Fast Triangle Reordering for Vertex Locality and Reduced Overdraw} * by Sander, Nehab, and Barczak */ GeometryPipeline.reorderForPostVertexCache = function ( geometry, cacheCapacity ) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug'); var indices = geometry.indices; if (geometry.primitiveType === PrimitiveType$1.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 * indices fit into unsigned shorts. This is used to meet the WebGL requirements * when unsigned int indices are not supported. *

* If the geometry does not have any indices, this function has no effect. *

* * @param {Geometry} geometry The geometry to be split into multiple geometries. * @returns {Geometry[]} An array of geometries, each with indices that fit into unsigned shorts. * * @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 = Cesium.GeometryPipeline.fitToUnsignedShortIndices(geometry); */ GeometryPipeline.fitToUnsignedShortIndices = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } if ( defined(geometry.indices) && geometry.primitiveType !== PrimitiveType$1.TRIANGLES && geometry.primitiveType !== PrimitiveType$1.LINES && geometry.primitiveType !== PrimitiveType$1.POINTS ) { throw new DeveloperError( "geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS." ); } //>>includeEnd('debug'); 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$1.TRIANGLES) { indicesPerPrimitive = 3; } else if (geometry.primitiveType === PrimitiveType$1.LINES) { indicesPerPrimitive = 2; } else if (geometry.primitiveType === PrimitiveType$1.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, boundingSphereCV: geometry.boundingSphereCV, }) ); // 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, boundingSphereCV: geometry.boundingSphereCV, }) ); } } 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 position attribute to 2D, replacing the position * attribute with separate position3D and position2D attributes. *

* If the geometry does not have a position, this function has no effect. *

* * @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 geometry argument with position3D and position2D attributes. * * @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 = Cesium.GeometryPipeline.projectTo2D(geometry, 'position', 'position3D', 'position2D'); */ GeometryPipeline.projectTo2D = function ( geometry, attributeName, attributeName3D, attributeName2D, projection ) { //>>includeStart('debug', pragmas.debug); 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."); } if (!defined(geometry.attributes[attributeName])) { throw new DeveloperError( "geometry must have attribute matching the attributeName argument: " + attributeName + "." ); } if ( geometry.attributes[attributeName].componentDatatype !== ComponentDatatype$1.DOUBLE ) { throw new DeveloperError( "The attribute componentDatatype must be ComponentDatatype.DOUBLE." ); } //>>includeEnd('debug'); var attribute = geometry.attributes[attributeName]; projection = defined(projection) ? projection : new GeographicProjection(); var ellipsoid = projection.ellipsoid; // 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 ); //>>includeStart('debug', pragmas.debug); if (!defined(lonLat)) { throw new DeveloperError( "Could not project point (" + value.x + ", " + value.y + ", " + value.z + ") to 2D." ); } //>>includeEnd('debug'); 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$1.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. *

* This is commonly used to create high-precision position vertex attributes. *

* * @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 geometry argument, with its encoded attribute. * * @exception {DeveloperError} geometry must have attribute matching the attributeName argument. * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE. * * @example * geometry = Cesium.GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow'); */ GeometryPipeline.encodeAttribute = function ( geometry, attributeName, attributeHighName, attributeLowName ) { //>>includeStart('debug', pragmas.debug); 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."); } if (!defined(geometry.attributes[attributeName])) { throw new DeveloperError( "geometry must have attribute matching the attributeName argument: " + attributeName + "." ); } if ( geometry.attributes[attributeName].componentDatatype !== ComponentDatatype$1.DOUBLE ) { throw new DeveloperError( "The attribute componentDatatype must be ComponentDatatype.DOUBLE." ); } //>>includeEnd('debug'); var attribute = geometry.attributes[attributeName]; 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$1.FLOAT, componentsPerAttribute: componentsPerAttribute, values: highValues, }); geometry.attributes[attributeLowName] = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: componentsPerAttribute, values: lowValues, }); delete geometry.attributes[attributeName]; return geometry; }; var scratchCartesian3$9 = 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$9); Matrix4.multiplyByPoint(matrix, scratchCartesian3$9, scratchCartesian3$9); Cartesian3.pack(scratchCartesian3$9, 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$9); Matrix3.multiplyByVector(matrix, scratchCartesian3$9, scratchCartesian3$9); scratchCartesian3$9 = Cartesian3.normalize( scratchCartesian3$9, scratchCartesian3$9 ); Cartesian3.pack(scratchCartesian3$9, values, i); } } } var inverseTranspose = new Matrix4(); var normalMatrix = new Matrix3(); /** * Transforms a geometry instance to world coordinates. This changes * the instance's modelMatrix to {@link Matrix4.IDENTITY} and transforms the * following attributes if they are present: position, normal, * tangent, and bitangent. * * @param {GeometryInstance} instance The geometry instance to modify. * @returns {GeometryInstance} The modified instance argument, with its attributes transforms to world coordinates. * * @example * Cesium.GeometryPipeline.transformToWorldCoordinates(instance); */ GeometryPipeline.transformToWorldCoordinates = function (instance) { //>>includeStart('debug', pragmas.debug); if (!defined(instance)) { throw new DeveloperError("instance is required."); } //>>includeEnd('debug'); 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.tangent) || defined(attributes.bitangent) ) { Matrix4.inverse(modelMatrix, inverseTranspose); Matrix4.transpose(inverseTranspose, inverseTranspose); Matrix4.getMatrix3(inverseTranspose, normalMatrix); transformVector(normalMatrix, attributes.normal); transformVector(normalMatrix, attributes.tangent); transformVector(normalMatrix, attributes.bitangent); } 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, propertyName) { var length = instances.length; var attributesInAllGeometries = {}; var attributes0 = instances[0][propertyName].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][propertyName].attributes[name]; if ( !defined(otherAttribute) || attribute.componentDatatype !== otherAttribute.componentDatatype || 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$1.createTypedArray( attribute.componentDatatype, numberOfComponents ), }); } } } return attributesInAllGeometries; } var tempScratch$1 = new Cartesian3(); function combineGeometries(instances, propertyName) { var length = instances.length; var name; var i; var j; var k; var m = instances[0].modelMatrix; var haveIndices = defined(instances[0][propertyName].indices); var primitiveType = instances[0][propertyName].primitiveType; //>>includeStart('debug', pragmas.debug); 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][propertyName].indices) !== haveIndices) { throw new DeveloperError( "All instance geometries must have an indices or not have one." ); } if (instances[i][propertyName].primitiveType !== primitiveType) { throw new DeveloperError( "All instance geometries must have the same primitiveType." ); } } //>>includeEnd('debug'); // Find subset of attributes in all geometries var attributes = findAttributesInAllGeometries(instances, propertyName); 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][propertyName].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][propertyName].indices.length; } var numberOfVertices = Geometry.computeNumberOfVertices( new Geometry({ attributes: attributes, primitiveType: PrimitiveType$1.POINTS, }) ); var destIndices = IndexDatatype$1.createTypedArray( numberOfVertices, numberOfIndices ); var destOffset = 0; var offset = 0; for (i = 0; i < length; ++i) { var sourceIndices = instances[i][propertyName].indices; var sourceIndicesLen = sourceIndices.length; for (k = 0; k < sourceIndicesLen; ++k) { destIndices[destOffset++] = offset + sourceIndices[k]; } offset += Geometry.computeNumberOfVertices(instances[i][propertyName]); } 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][propertyName].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][propertyName].boundingSphere; var tempRadius = Cartesian3.magnitude( Cartesian3.subtract(bs.center, center, tempScratch$1) ) + bs.radius; if (tempRadius > radius) { radius = tempRadius; } } } return new Geometry({ attributes: attributes, indices: indices, primitiveType: primitiveType, boundingSphere: defined(center) ? new BoundingSphere(center, radius) : undefined, }); } /** * 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. *

* If the instances do not have the same attributes, a subset of attributes common * to all instances is used, and the others are ignored. *

*

* This is used by {@link Primitive} to efficiently render a large amount of static data. *

* * @private * * @param {GeometryInstance[]} [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} 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) { * Cesium.GeometryPipeline.transformToWorldCoordinates(instances[i]); * } * var geometries = Cesium.GeometryPipeline.combineInstances(instances); * * @see GeometryPipeline.transformToWorldCoordinates */ GeometryPipeline.combineInstances = function (instances) { //>>includeStart('debug', pragmas.debug); if (!defined(instances) || instances.length < 1) { throw new DeveloperError( "instances is required and must have length greater than zero." ); } //>>includeEnd('debug'); var instanceGeometry = []; var instanceSplitGeometry = []; var length = instances.length; for (var i = 0; i < length; ++i) { var instance = instances[i]; if (defined(instance.geometry)) { instanceGeometry.push(instance); } else if ( defined(instance.westHemisphereGeometry) && defined(instance.eastHemisphereGeometry) ) { instanceSplitGeometry.push(instance); } } var geometries = []; if (instanceGeometry.length > 0) { geometries.push(combineGeometries(instanceGeometry, "geometry")); } if (instanceSplitGeometry.length > 0) { geometries.push( combineGeometries(instanceSplitGeometry, "westHemisphereGeometry") ); geometries.push( combineGeometries(instanceSplitGeometry, "eastHemisphereGeometry") ); } return geometries; }; var normal = new Cartesian3(); var v0 = new Cartesian3(); var v1$1 = new Cartesian3(); var v2$1 = new Cartesian3(); /** * Computes per-vertex normals for a geometry containing TRIANGLES by averaging the normals of * all triangles incident to the vertex. The result is a new normal attribute added to the geometry. * This assumes a counter-clockwise winding order. * * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified geometry argument with the computed normal attribute. * * @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 * Cesium.GeometryPipeline.computeNormal(geometry); */ GeometryPipeline.computeNormal = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } if ( !defined(geometry.attributes.position) || !defined(geometry.attributes.position.values) ) { throw new DeveloperError( "geometry.attributes.position.values is required." ); } if (!defined(geometry.indices)) { throw new DeveloperError("geometry.indices is required."); } if (geometry.indices.length < 2 || geometry.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$1.TRIANGLES) { throw new DeveloperError( "geometry.primitiveType must be PrimitiveType.TRIANGLES." ); } //>>includeEnd('debug'); var indices = geometry.indices; var attributes = geometry.attributes; var vertices = attributes.position.values; var numVertices = 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); var i; for (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$1.x = vertices[i13]; v1$1.y = vertices[i13 + 1]; v1$1.z = vertices[i13 + 2]; v2$1.x = vertices[i23]; v2$1.y = vertices[i23 + 1]; v2$1.z = vertices[i23 + 2]; normalsPerVertex[i0].count++; normalsPerVertex[i1].count++; normalsPerVertex[i2].count++; Cartesian3.subtract(v1$1, v0, v1$1); Cartesian3.subtract(v2$1, v0, v2$1); normalsPerTriangle[j] = Cartesian3.cross(v1$1, v2$1, new Cartesian3()); 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]; Cartesian3.clone(Cartesian3.ZERO, normal); if (vertexNormalData.count > 0) { for (j = 0; j < vertexNormalData.count; j++) { Cartesian3.add( normal, normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal ); } // We can run into an issue where a vertex is used with 2 primitives that have opposite winding order. if ( Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10) ) { Cartesian3.clone( normalsPerTriangle[normalIndices[vertexNormalData.indexOffset]], normal ); } } // We end up with a zero vector probably because of a degenerate triangle if ( Cartesian3.equalsEpsilon(Cartesian3.ZERO, normal, CesiumMath.EPSILON10) ) { // Default to (0,0,1) normal.z = 1.0; } Cartesian3.normalize(normal, normal); normalValues[i3] = normal.x; normalValues[i3 + 1] = normal.y; normalValues[i3 + 2] = normal.z; } geometry.attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normalValues, }); return geometry; }; var normalScratch$4 = new Cartesian3(); var normalScale = new Cartesian3(); var tScratch = new Cartesian3(); /** * Computes per-vertex tangents and bitangents for a geometry containing TRIANGLES. * The result is new tangent and bitangent attributes added to the geometry. * This assumes a counter-clockwise winding order. *

* Based on Computing Tangent Space Basis Vectors * for an Arbitrary Mesh by Eric Lengyel. *

* * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified geometry argument with the computed tangent and bitangent attributes. * * @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 * Cesium.GeometryPipeline.computeTangentAndBiTangent(geometry); */ GeometryPipeline.computeTangentAndBitangent = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug'); var attributes = geometry.attributes; var indices = geometry.indices; //>>includeStart('debug', pragmas.debug); 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$1.TRIANGLES) { throw new DeveloperError( "geometry.primitiveType must be PrimitiveType.TRIANGLES." ); } //>>includeEnd('debug'); 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); var i; for (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 tangentValues = new Float32Array(numVertices * 3); var bitangentValues = 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$4); 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); bitangentValues[i03] = t.x; bitangentValues[i13] = t.y; bitangentValues[i23] = t.z; } geometry.attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangentValues, }); geometry.attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangentValues, }); return geometry; }; var scratchCartesian2$8 = new Cartesian2(); var toEncode1 = new Cartesian3(); var toEncode2 = new Cartesian3(); var toEncode3 = new Cartesian3(); var encodeResult2 = new Cartesian2(); /** * Compresses and packs geometry normal attribute values to save memory. * * @param {Geometry} geometry The geometry to modify. * @returns {Geometry} The modified geometry argument, with its normals compressed and packed. * * @example * geometry = Cesium.GeometryPipeline.compressVertices(geometry); */ GeometryPipeline.compressVertices = function (geometry) { //>>includeStart('debug', pragmas.debug); if (!defined(geometry)) { throw new DeveloperError("geometry is required."); } //>>includeEnd('debug'); var extrudeAttribute = geometry.attributes.extrudeDirection; var i; var numVertices; if (defined(extrudeAttribute)) { //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes var extrudeDirections = extrudeAttribute.values; numVertices = extrudeDirections.length / 3.0; var compressedDirections = new Float32Array(numVertices * 2); var i2 = 0; for (i = 0; i < numVertices; ++i) { Cartesian3.fromArray(extrudeDirections, i * 3.0, toEncode1); if (Cartesian3.equals(toEncode1, Cartesian3.ZERO)) { i2 += 2; continue; } encodeResult2 = AttributeCompression.octEncodeInRange( toEncode1, 65535, encodeResult2 ); compressedDirections[i2++] = encodeResult2.x; compressedDirections[i2++] = encodeResult2.y; } geometry.attributes.compressedAttributes = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: compressedDirections, }); delete geometry.attributes.extrudeDirection; return geometry; } var normalAttribute = geometry.attributes.normal; var stAttribute = geometry.attributes.st; var hasNormal = defined(normalAttribute); var hasSt = defined(stAttribute); if (!hasNormal && !hasSt) { return geometry; } var tangentAttribute = geometry.attributes.tangent; var bitangentAttribute = geometry.attributes.bitangent; var hasTangent = defined(tangentAttribute); var hasBitangent = defined(bitangentAttribute); var normals; var st; var tangents; var bitangents; if (hasNormal) { normals = normalAttribute.values; } if (hasSt) { st = stAttribute.values; } if (hasTangent) { tangents = tangentAttribute.values; } if (hasBitangent) { bitangents = bitangentAttribute.values; } var length = hasNormal ? normals.length : st.length; var numComponents = hasNormal ? 3.0 : 2.0; numVertices = length / numComponents; var compressedLength = numVertices; var numCompressedComponents = hasSt && hasNormal ? 2.0 : 1.0; numCompressedComponents += hasTangent || hasBitangent ? 1.0 : 0.0; compressedLength *= numCompressedComponents; var compressedAttributes = new Float32Array(compressedLength); var normalIndex = 0; for (i = 0; i < numVertices; ++i) { if (hasSt) { Cartesian2.fromArray(st, i * 2.0, scratchCartesian2$8); compressedAttributes[ normalIndex++ ] = AttributeCompression.compressTextureCoordinates(scratchCartesian2$8); } var index = i * 3.0; if (hasNormal && defined(tangents) && defined(bitangents)) { Cartesian3.fromArray(normals, index, toEncode1); Cartesian3.fromArray(tangents, index, toEncode2); Cartesian3.fromArray(bitangents, index, toEncode3); AttributeCompression.octPack( toEncode1, toEncode2, toEncode3, scratchCartesian2$8 ); compressedAttributes[normalIndex++] = scratchCartesian2$8.x; compressedAttributes[normalIndex++] = scratchCartesian2$8.y; } else { if (hasNormal) { Cartesian3.fromArray(normals, index, toEncode1); compressedAttributes[ normalIndex++ ] = AttributeCompression.octEncodeFloat(toEncode1); } if (hasTangent) { Cartesian3.fromArray(tangents, index, toEncode1); compressedAttributes[ normalIndex++ ] = AttributeCompression.octEncodeFloat(toEncode1); } if (hasBitangent) { Cartesian3.fromArray(bitangents, index, toEncode1); compressedAttributes[ normalIndex++ ] = AttributeCompression.octEncodeFloat(toEncode1); } } } geometry.attributes.compressedAttributes = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: numCompressedComponents, values: compressedAttributes, }); if (hasNormal) { delete geometry.attributes.normal; } if (hasSt) { delete geometry.attributes.st; } if (hasBitangent) { delete geometry.attributes.bitangent; } if (hasTangent) { delete geometry.attributes.tangent; } return geometry; }; function indexTriangles(geometry) { if (defined(geometry.indices)) { return geometry; } var numberOfVertices = Geometry.computeNumberOfVertices(geometry); //>>includeStart('debug', pragmas.debug); 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." ); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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); //>>includeStart('debug', pragmas.debug); if (numberOfVertices < 3) { throw new DeveloperError("The number of vertices must be at least three."); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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$1.TRIANGLES; return geometry; } function indexTriangleStrip(geometry) { var numberOfVertices = Geometry.computeNumberOfVertices(geometry); //>>includeStart('debug', pragmas.debug); if (numberOfVertices < 3) { throw new DeveloperError("The number of vertices must be at least 3."); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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$1.TRIANGLES; return geometry; } function indexLines(geometry) { if (defined(geometry.indices)) { return geometry; } var numberOfVertices = Geometry.computeNumberOfVertices(geometry); //>>includeStart('debug', pragmas.debug); 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."); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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); //>>includeStart('debug', pragmas.debug); if (numberOfVertices < 2) { throw new DeveloperError("The number of vertices must be at least two."); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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$1.LINES; return geometry; } function indexLineLoop(geometry) { var numberOfVertices = Geometry.computeNumberOfVertices(geometry); //>>includeStart('debug', pragmas.debug); if (numberOfVertices < 2) { throw new DeveloperError("The number of vertices must be at least two."); } //>>includeEnd('debug'); var indices = IndexDatatype$1.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$1.LINES; return geometry; } function indexPrimitive(geometry) { switch (geometry.primitiveType) { case PrimitiveType$1.TRIANGLE_FAN: return indexTriangleFan(geometry); case PrimitiveType$1.TRIANGLE_STRIP: return indexTriangleStrip(geometry); case PrimitiveType$1.TRIANGLES: return indexTriangles(geometry); case PrimitiveType$1.LINE_STRIP: return indexLineStrip(geometry); case PrimitiveType$1.LINE_LOOP: return indexLineLoop(geometry); case PrimitiveType$1.LINES: return indexLines(geometry); } return geometry; } function offsetPointFromXZPlane(p, isBehind) { if (Math.abs(p.y) < CesiumMath.EPSILON6) { if (isBehind) { p.y = -CesiumMath.EPSILON6; } else { p.y = CesiumMath.EPSILON6; } } } function offsetTriangleFromXZPlane(p0, p1, p2) { if (p0.y !== 0.0 && p1.y !== 0.0 && p2.y !== 0.0) { offsetPointFromXZPlane(p0, p0.y < 0.0); offsetPointFromXZPlane(p1, p1.y < 0.0); offsetPointFromXZPlane(p2, p2.y < 0.0); return; } var p0y = Math.abs(p0.y); var p1y = Math.abs(p1.y); var p2y = Math.abs(p2.y); var sign; if (p0y > p1y) { if (p0y > p2y) { sign = CesiumMath.sign(p0.y); } else { sign = CesiumMath.sign(p2.y); } } else if (p1y > p2y) { sign = CesiumMath.sign(p1.y); } else { sign = CesiumMath.sign(p2.y); } var isBehind = sign < 0.0; offsetPointFromXZPlane(p0, isBehind); offsetPointFromXZPlane(p1, isBehind); offsetPointFromXZPlane(p2, isBehind); } var c3$1 = new Cartesian3(); function getXZIntersectionOffsetPoints(p, p1, u1, v1) { Cartesian3.add( p, Cartesian3.multiplyByScalar( Cartesian3.subtract(p1, p, c3$1), p.y / (p.y - p1.y), c3$1 ), 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; } offsetTriangleFromXZPlane(p0, p1, p2); var p0Behind = p0.y < 0.0; var p1Behind = p1.y < 0.0; var p2Behind = p2.y < 0.0; 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; positions.length = 3; if (numBehind === 1 || numBehind === 2) { positions[3] = u1; positions[4] = u2; positions[5] = q1; positions[6] = q2; positions.length = 7; } return splitTriangleResult; } function updateGeometryAfterSplit(geometry, computeBoundingSphere) { var attributes = geometry.attributes; if (attributes.position.values.length === 0) { return undefined; } for (var property in attributes) { if ( attributes.hasOwnProperty(property) && defined(attributes[property]) && defined(attributes[property].values) ) { var attribute = attributes[property]; attribute.values = ComponentDatatype$1.createTypedArray( attribute.componentDatatype, attribute.values ); } } var numberOfVertices = Geometry.computeNumberOfVertices(geometry); geometry.indices = IndexDatatype$1.createTypedArray( numberOfVertices, geometry.indices ); if (computeBoundingSphere) { geometry.boundingSphere = BoundingSphere.fromVertices( attributes.position.values ); } return geometry; } function copyGeometryForSplit(geometry) { var attributes = geometry.attributes; var copiedAttributes = {}; for (var property in attributes) { if ( attributes.hasOwnProperty(property) && defined(attributes[property]) && defined(attributes[property].values) ) { var attribute = attributes[property]; copiedAttributes[property] = new GeometryAttribute({ componentDatatype: attribute.componentDatatype, componentsPerAttribute: attribute.componentsPerAttribute, normalize: attribute.normalize, values: [], }); } } return new Geometry({ attributes: copiedAttributes, indices: [], primitiveType: geometry.primitiveType, }); } function updateInstanceAfterSplit(instance, westGeometry, eastGeometry) { var computeBoundingSphere = defined(instance.geometry.boundingSphere); westGeometry = updateGeometryAfterSplit(westGeometry, computeBoundingSphere); eastGeometry = updateGeometryAfterSplit(eastGeometry, computeBoundingSphere); if (defined(eastGeometry) && !defined(westGeometry)) { instance.geometry = eastGeometry; } else if (!defined(eastGeometry) && defined(westGeometry)) { instance.geometry = westGeometry; } else { instance.westHemisphereGeometry = westGeometry; instance.eastHemisphereGeometry = eastGeometry; instance.geometry = undefined; } } function generateBarycentricInterpolateFunction( CartesianType, numberOfComponents ) { var v0Scratch = new CartesianType(); var v1Scratch = new CartesianType(); var v2Scratch = new CartesianType(); return function ( i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, normalize ) { var v0 = CartesianType.fromArray( sourceValues, i0 * numberOfComponents, v0Scratch ); var v1 = CartesianType.fromArray( sourceValues, i1 * numberOfComponents, v1Scratch ); var v2 = CartesianType.fromArray( sourceValues, i2 * numberOfComponents, v2Scratch ); CartesianType.multiplyByScalar(v0, coords.x, v0); CartesianType.multiplyByScalar(v1, coords.y, v1); CartesianType.multiplyByScalar(v2, coords.z, v2); var value = CartesianType.add(v0, v1, v0); CartesianType.add(value, v2, value); if (normalize) { CartesianType.normalize(value, value); } CartesianType.pack( value, currentValues, insertedIndex * numberOfComponents ); }; } var interpolateAndPackCartesian4 = generateBarycentricInterpolateFunction( Cartesian4, 4 ); var interpolateAndPackCartesian3 = generateBarycentricInterpolateFunction( Cartesian3, 3 ); var interpolateAndPackCartesian2 = generateBarycentricInterpolateFunction( Cartesian2, 2 ); var interpolateAndPackBoolean = function ( i0, i1, i2, coords, sourceValues, currentValues, insertedIndex ) { var v1 = sourceValues[i0] * coords.x; var v2 = sourceValues[i1] * coords.y; var v3 = sourceValues[i2] * coords.z; currentValues[insertedIndex] = v1 + v2 + v3 > CesiumMath.EPSILON6 ? 1 : 0; }; var p0Scratch = new Cartesian3(); var p1Scratch$2 = new Cartesian3(); var p2Scratch$2 = new Cartesian3(); var barycentricScratch = new Cartesian3(); function computeTriangleAttributes( i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, allAttributes, insertedIndex ) { if ( !defined(normals) && !defined(tangents) && !defined(bitangents) && !defined(texCoords) && !defined(extrudeDirections) && customAttributesLength === 0 ) { return; } var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch); var p1 = Cartesian3.fromArray(positions, i1 * 3, p1Scratch$2); var p2 = Cartesian3.fromArray(positions, i2 * 3, p2Scratch$2); var coords = barycentricCoordinates(point, p0, p1, p2, barycentricScratch); if (defined(normals)) { interpolateAndPackCartesian3( i0, i1, i2, coords, normals, currentAttributes.normal.values, insertedIndex, true ); } if (defined(extrudeDirections)) { var d0 = Cartesian3.fromArray(extrudeDirections, i0 * 3, p0Scratch); var d1 = Cartesian3.fromArray(extrudeDirections, i1 * 3, p1Scratch$2); var d2 = Cartesian3.fromArray(extrudeDirections, i2 * 3, p2Scratch$2); Cartesian3.multiplyByScalar(d0, coords.x, d0); Cartesian3.multiplyByScalar(d1, coords.y, d1); Cartesian3.multiplyByScalar(d2, coords.z, d2); var direction; if ( !Cartesian3.equals(d0, Cartesian3.ZERO) || !Cartesian3.equals(d1, Cartesian3.ZERO) || !Cartesian3.equals(d2, Cartesian3.ZERO) ) { direction = Cartesian3.add(d0, d1, d0); Cartesian3.add(direction, d2, direction); Cartesian3.normalize(direction, direction); } else { direction = p0Scratch; direction.x = 0; direction.y = 0; direction.z = 0; } Cartesian3.pack( direction, currentAttributes.extrudeDirection.values, insertedIndex * 3 ); } if (defined(applyOffset)) { interpolateAndPackBoolean( i0, i1, i2, coords, applyOffset, currentAttributes.applyOffset.values, insertedIndex ); } if (defined(tangents)) { interpolateAndPackCartesian3( i0, i1, i2, coords, tangents, currentAttributes.tangent.values, insertedIndex, true ); } if (defined(bitangents)) { interpolateAndPackCartesian3( i0, i1, i2, coords, bitangents, currentAttributes.bitangent.values, insertedIndex, true ); } if (defined(texCoords)) { interpolateAndPackCartesian2( i0, i1, i2, coords, texCoords, currentAttributes.st.values, insertedIndex ); } if (customAttributesLength > 0) { for (var i = 0; i < customAttributesLength; i++) { var attributeName = customAttributeNames[i]; genericInterpolate( i0, i1, i2, coords, insertedIndex, allAttributes[attributeName], currentAttributes[attributeName] ); } } } function genericInterpolate( i0, i1, i2, coords, insertedIndex, sourceAttribute, currentAttribute ) { var componentsPerAttribute = sourceAttribute.componentsPerAttribute; var sourceValues = sourceAttribute.values; var currentValues = currentAttribute.values; switch (componentsPerAttribute) { case 4: interpolateAndPackCartesian4( i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false ); break; case 3: interpolateAndPackCartesian3( i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false ); break; case 2: interpolateAndPackCartesian2( i0, i1, i2, coords, sourceValues, currentValues, insertedIndex, false ); break; default: currentValues[insertedIndex] = sourceValues[i0] * coords.x + sourceValues[i1] * coords.y + sourceValues[i2] * coords.z; } } function insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, currentIndex, point ) { var insertIndex = currentAttributes.position.values.length / 3; if (currentIndex !== -1) { var prevIndex = indices[currentIndex]; var newIndex = currentIndexMap[prevIndex]; if (newIndex === -1) { currentIndexMap[prevIndex] = insertIndex; currentAttributes.position.values.push(point.x, point.y, point.z); currentIndices.push(insertIndex); return insertIndex; } currentIndices.push(newIndex); return newIndex; } currentAttributes.position.values.push(point.x, point.y, point.z); currentIndices.push(insertIndex); return insertIndex; } var NAMED_ATTRIBUTES = { position: true, normal: true, bitangent: true, tangent: true, st: true, extrudeDirection: true, applyOffset: true, }; function splitLongitudeTriangles(instance) { var geometry = instance.geometry; var attributes = geometry.attributes; var positions = attributes.position.values; var normals = defined(attributes.normal) ? attributes.normal.values : undefined; var bitangents = defined(attributes.bitangent) ? attributes.bitangent.values : undefined; var tangents = defined(attributes.tangent) ? attributes.tangent.values : undefined; var texCoords = defined(attributes.st) ? attributes.st.values : undefined; var extrudeDirections = defined(attributes.extrudeDirection) ? attributes.extrudeDirection.values : undefined; var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined; var indices = geometry.indices; var customAttributeNames = []; for (var attributeName in attributes) { if ( attributes.hasOwnProperty(attributeName) && !NAMED_ATTRIBUTES[attributeName] && defined(attributes[attributeName]) ) { customAttributeNames.push(attributeName); } } var customAttributesLength = customAttributeNames.length; var eastGeometry = copyGeometryForSplit(geometry); var westGeometry = copyGeometryForSplit(geometry); var currentAttributes; var currentIndices; var currentIndexMap; var insertedIndex; var i; var westGeometryIndexMap = []; westGeometryIndexMap.length = positions.length / 3; var eastGeometryIndexMap = []; eastGeometryIndexMap.length = positions.length / 3; for (i = 0; i < westGeometryIndexMap.length; ++i) { westGeometryIndexMap[i] = -1; eastGeometryIndexMap[i] = -1; } var len = indices.length; for (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) && result.positions.length > 3) { var resultPositions = result.positions; var resultIndices = result.indices; var resultLength = resultIndices.length; for (var j = 0; j < resultLength; ++j) { var resultIndex = resultIndices[j]; var point = resultPositions[resultIndex]; if (point.y < 0.0) { currentAttributes = westGeometry.attributes; currentIndices = westGeometry.indices; currentIndexMap = westGeometryIndexMap; } else { currentAttributes = eastGeometry.attributes; currentIndices = eastGeometry.indices; currentIndexMap = eastGeometryIndexMap; } insertedIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, resultIndex < 3 ? i + resultIndex : -1, point ); computeTriangleAttributes( i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex ); } } else { if (defined(result)) { p0 = result.positions[0]; p1 = result.positions[1]; p2 = result.positions[2]; } if (p0.y < 0.0) { currentAttributes = westGeometry.attributes; currentIndices = westGeometry.indices; currentIndexMap = westGeometryIndexMap; } else { currentAttributes = eastGeometry.attributes; currentIndices = eastGeometry.indices; currentIndexMap = eastGeometryIndexMap; } insertedIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, i, p0 ); computeTriangleAttributes( i0, i1, i2, p0, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex ); insertedIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1 ); computeTriangleAttributes( i0, i1, i2, p1, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex ); insertedIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, i + 2, p2 ); computeTriangleAttributes( i0, i1, i2, p2, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex ); } } updateInstanceAfterSplit(instance, westGeometry, eastGeometry); } var xzPlane = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Y); var offsetScratch$c = new Cartesian3(); var offsetPointScratch = new Cartesian3(); function computeLineAttributes( i0, i1, point, positions, insertIndex, currentAttributes, applyOffset ) { if (!defined(applyOffset)) { return; } var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch); if (Cartesian3.equalsEpsilon(p0, point, CesiumMath.EPSILON10)) { currentAttributes.applyOffset.values[insertIndex] = applyOffset[i0]; } else { currentAttributes.applyOffset.values[insertIndex] = applyOffset[i1]; } } function splitLongitudeLines(instance) { var geometry = instance.geometry; var attributes = geometry.attributes; var positions = attributes.position.values; var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined; var indices = geometry.indices; var eastGeometry = copyGeometryForSplit(geometry); var westGeometry = copyGeometryForSplit(geometry); var i; var length = indices.length; var westGeometryIndexMap = []; westGeometryIndexMap.length = positions.length / 3; var eastGeometryIndexMap = []; eastGeometryIndexMap.length = positions.length / 3; for (i = 0; i < westGeometryIndexMap.length; ++i) { westGeometryIndexMap[i] = -1; eastGeometryIndexMap[i] = -1; } for (i = 0; i < length; i += 2) { var i0 = indices[i]; var i1 = indices[i + 1]; var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch); var p1 = Cartesian3.fromArray(positions, i1 * 3, p1Scratch$2); var insertIndex; if (Math.abs(p0.y) < CesiumMath.EPSILON6) { if (p0.y < 0.0) { p0.y = -CesiumMath.EPSILON6; } else { p0.y = CesiumMath.EPSILON6; } } if (Math.abs(p1.y) < CesiumMath.EPSILON6) { if (p1.y < 0.0) { p1.y = -CesiumMath.EPSILON6; } else { p1.y = CesiumMath.EPSILON6; } } var p0Attributes = eastGeometry.attributes; var p0Indices = eastGeometry.indices; var p0IndexMap = eastGeometryIndexMap; var p1Attributes = westGeometry.attributes; var p1Indices = westGeometry.indices; var p1IndexMap = westGeometryIndexMap; var intersection = IntersectionTests.lineSegmentPlane( p0, p1, xzPlane, p2Scratch$2 ); 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, offsetScratch$c ); if (p0.y < 0.0) { Cartesian3.negate(offset, offset); p0Attributes = westGeometry.attributes; p0Indices = westGeometry.indices; p0IndexMap = westGeometryIndexMap; p1Attributes = eastGeometry.attributes; p1Indices = eastGeometry.indices; p1IndexMap = eastGeometryIndexMap; } var offsetPoint = Cartesian3.add( intersection, offset, offsetPointScratch ); insertIndex = insertSplitPoint( p0Attributes, p0Indices, p0IndexMap, indices, i, p0 ); computeLineAttributes( i0, i1, p0, positions, insertIndex, p0Attributes, applyOffset ); insertIndex = insertSplitPoint( p0Attributes, p0Indices, p0IndexMap, indices, -1, offsetPoint ); computeLineAttributes( i0, i1, offsetPoint, positions, insertIndex, p0Attributes, applyOffset ); Cartesian3.negate(offset, offset); Cartesian3.add(intersection, offset, offsetPoint); insertIndex = insertSplitPoint( p1Attributes, p1Indices, p1IndexMap, indices, -1, offsetPoint ); computeLineAttributes( i0, i1, offsetPoint, positions, insertIndex, p1Attributes, applyOffset ); insertIndex = insertSplitPoint( p1Attributes, p1Indices, p1IndexMap, indices, i + 1, p1 ); computeLineAttributes( i0, i1, p1, positions, insertIndex, p1Attributes, applyOffset ); } else { var currentAttributes; var currentIndices; var currentIndexMap; if (p0.y < 0.0) { currentAttributes = westGeometry.attributes; currentIndices = westGeometry.indices; currentIndexMap = westGeometryIndexMap; } else { currentAttributes = eastGeometry.attributes; currentIndices = eastGeometry.indices; currentIndexMap = eastGeometryIndexMap; } insertIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, i, p0 ); computeLineAttributes( i0, i1, p0, positions, insertIndex, currentAttributes, applyOffset ); insertIndex = insertSplitPoint( currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1 ); computeLineAttributes( i0, i1, p1, positions, insertIndex, currentAttributes, applyOffset ); } } updateInstanceAfterSplit(instance, westGeometry, eastGeometry); } var cartesian2Scratch0 = new Cartesian2(); var cartesian2Scratch1 = new Cartesian2(); var cartesian3Scratch0 = new Cartesian3(); var cartesian3Scratch2$1 = new Cartesian3(); var cartesian3Scratch3$1 = new Cartesian3(); var cartesian3Scratch4 = new Cartesian3(); var cartesian3Scratch5 = new Cartesian3(); var cartesian3Scratch6 = new Cartesian3(); var cartesian4Scratch0 = new Cartesian4(); function updateAdjacencyAfterSplit(geometry) { var attributes = geometry.attributes; var positions = attributes.position.values; var prevPositions = attributes.prevPosition.values; var nextPositions = attributes.nextPosition.values; var length = positions.length; for (var j = 0; j < length; j += 3) { var position = Cartesian3.unpack(positions, j, cartesian3Scratch0); if (position.x > 0.0) { continue; } var prevPosition = Cartesian3.unpack(prevPositions, j, cartesian3Scratch2$1); if ( (position.y < 0.0 && prevPosition.y > 0.0) || (position.y > 0.0 && prevPosition.y < 0.0) ) { if (j - 3 > 0) { prevPositions[j] = positions[j - 3]; prevPositions[j + 1] = positions[j - 2]; prevPositions[j + 2] = positions[j - 1]; } else { Cartesian3.pack(position, prevPositions, j); } } var nextPosition = Cartesian3.unpack(nextPositions, j, cartesian3Scratch3$1); if ( (position.y < 0.0 && nextPosition.y > 0.0) || (position.y > 0.0 && nextPosition.y < 0.0) ) { if (j + 3 < length) { nextPositions[j] = positions[j + 3]; nextPositions[j + 1] = positions[j + 4]; nextPositions[j + 2] = positions[j + 5]; } else { Cartesian3.pack(position, nextPositions, j); } } } } var offsetScalar = 5.0 * CesiumMath.EPSILON9; var coplanarOffset = CesiumMath.EPSILON6; function splitLongitudePolyline(instance) { var geometry = instance.geometry; var attributes = geometry.attributes; var positions = attributes.position.values; var prevPositions = attributes.prevPosition.values; var nextPositions = attributes.nextPosition.values; var expandAndWidths = attributes.expandAndWidth.values; var texCoords = defined(attributes.st) ? attributes.st.values : undefined; var colors = defined(attributes.color) ? attributes.color.values : undefined; var eastGeometry = copyGeometryForSplit(geometry); var westGeometry = copyGeometryForSplit(geometry); var i; var j; var index; var intersectionFound = false; var length = positions.length / 3; for (i = 0; i < length; i += 4) { var i0 = i; var i2 = i + 2; var p0 = Cartesian3.fromArray(positions, i0 * 3, cartesian3Scratch0); var p2 = Cartesian3.fromArray(positions, i2 * 3, cartesian3Scratch2$1); // Offset points that are close to the 180 longitude and change the previous/next point // to be the same offset point so it can be projected to 2D. There is special handling in the // shader for when position == prevPosition || position == nextPosition. if (Math.abs(p0.y) < coplanarOffset) { p0.y = coplanarOffset * (p2.y < 0.0 ? -1.0 : 1.0); positions[i * 3 + 1] = p0.y; positions[(i + 1) * 3 + 1] = p0.y; for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) { prevPositions[j] = positions[i * 3]; prevPositions[j + 1] = positions[i * 3 + 1]; prevPositions[j + 2] = positions[i * 3 + 2]; } } // Do the same but for when the line crosses 180 longitude in the opposite direction. if (Math.abs(p2.y) < coplanarOffset) { p2.y = coplanarOffset * (p0.y < 0.0 ? -1.0 : 1.0); positions[(i + 2) * 3 + 1] = p2.y; positions[(i + 3) * 3 + 1] = p2.y; for (j = i0 * 3; j < i0 * 3 + 4 * 3; j += 3) { nextPositions[j] = positions[(i + 2) * 3]; nextPositions[j + 1] = positions[(i + 2) * 3 + 1]; nextPositions[j + 2] = positions[(i + 2) * 3 + 2]; } } var p0Attributes = eastGeometry.attributes; var p0Indices = eastGeometry.indices; var p2Attributes = westGeometry.attributes; var p2Indices = westGeometry.indices; var intersection = IntersectionTests.lineSegmentPlane( p0, p2, xzPlane, cartesian3Scratch4 ); if (defined(intersection)) { intersectionFound = true; // move point on the xz-plane slightly away from the plane var offset = Cartesian3.multiplyByScalar( Cartesian3.UNIT_Y, offsetScalar, cartesian3Scratch5 ); if (p0.y < 0.0) { Cartesian3.negate(offset, offset); p0Attributes = westGeometry.attributes; p0Indices = westGeometry.indices; p2Attributes = eastGeometry.attributes; p2Indices = eastGeometry.indices; } var offsetPoint = Cartesian3.add( intersection, offset, cartesian3Scratch6 ); p0Attributes.position.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z); p0Attributes.position.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p0Attributes.position.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p0Attributes.prevPosition.values.push( prevPositions[i0 * 3], prevPositions[i0 * 3 + 1], prevPositions[i0 * 3 + 2] ); p0Attributes.prevPosition.values.push( prevPositions[i0 * 3 + 3], prevPositions[i0 * 3 + 4], prevPositions[i0 * 3 + 5] ); p0Attributes.prevPosition.values.push(p0.x, p0.y, p0.z, p0.x, p0.y, p0.z); p0Attributes.nextPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p0Attributes.nextPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p0Attributes.nextPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p0Attributes.nextPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); Cartesian3.negate(offset, offset); Cartesian3.add(intersection, offset, offsetPoint); p2Attributes.position.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.position.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.position.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z); p2Attributes.prevPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.prevPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.prevPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.prevPosition.values.push( offsetPoint.x, offsetPoint.y, offsetPoint.z ); p2Attributes.nextPosition.values.push(p2.x, p2.y, p2.z, p2.x, p2.y, p2.z); p2Attributes.nextPosition.values.push( nextPositions[i2 * 3], nextPositions[i2 * 3 + 1], nextPositions[i2 * 3 + 2] ); p2Attributes.nextPosition.values.push( nextPositions[i2 * 3 + 3], nextPositions[i2 * 3 + 4], nextPositions[i2 * 3 + 5] ); var ew0 = Cartesian2.fromArray( expandAndWidths, i0 * 2, cartesian2Scratch0 ); var width = Math.abs(ew0.y); p0Attributes.expandAndWidth.values.push(-1, width, 1, width); p0Attributes.expandAndWidth.values.push(-1, -width, 1, -width); p2Attributes.expandAndWidth.values.push(-1, width, 1, width); p2Attributes.expandAndWidth.values.push(-1, -width, 1, -width); var t = Cartesian3.magnitudeSquared( Cartesian3.subtract(intersection, p0, cartesian3Scratch3$1) ); t /= Cartesian3.magnitudeSquared( Cartesian3.subtract(p2, p0, cartesian3Scratch3$1) ); if (defined(colors)) { var c0 = Cartesian4.fromArray(colors, i0 * 4, cartesian4Scratch0); var c2 = Cartesian4.fromArray(colors, i2 * 4, cartesian4Scratch0); var r = CesiumMath.lerp(c0.x, c2.x, t); var g = CesiumMath.lerp(c0.y, c2.y, t); var b = CesiumMath.lerp(c0.z, c2.z, t); var a = CesiumMath.lerp(c0.w, c2.w, t); for (j = i0 * 4; j < i0 * 4 + 2 * 4; ++j) { p0Attributes.color.values.push(colors[j]); } p0Attributes.color.values.push(r, g, b, a); p0Attributes.color.values.push(r, g, b, a); p2Attributes.color.values.push(r, g, b, a); p2Attributes.color.values.push(r, g, b, a); for (j = i2 * 4; j < i2 * 4 + 2 * 4; ++j) { p2Attributes.color.values.push(colors[j]); } } if (defined(texCoords)) { var s0 = Cartesian2.fromArray(texCoords, i0 * 2, cartesian2Scratch0); var s3 = Cartesian2.fromArray( texCoords, (i + 3) * 2, cartesian2Scratch1 ); var sx = CesiumMath.lerp(s0.x, s3.x, t); for (j = i0 * 2; j < i0 * 2 + 2 * 2; ++j) { p0Attributes.st.values.push(texCoords[j]); } p0Attributes.st.values.push(sx, s0.y); p0Attributes.st.values.push(sx, s3.y); p2Attributes.st.values.push(sx, s0.y); p2Attributes.st.values.push(sx, s3.y); for (j = i2 * 2; j < i2 * 2 + 2 * 2; ++j) { p2Attributes.st.values.push(texCoords[j]); } } index = p0Attributes.position.values.length / 3 - 4; p0Indices.push(index, index + 2, index + 1); p0Indices.push(index + 1, index + 2, index + 3); index = p2Attributes.position.values.length / 3 - 4; p2Indices.push(index, index + 2, index + 1); p2Indices.push(index + 1, index + 2, index + 3); } else { var currentAttributes; var currentIndices; if (p0.y < 0.0) { currentAttributes = westGeometry.attributes; currentIndices = westGeometry.indices; } else { currentAttributes = eastGeometry.attributes; currentIndices = eastGeometry.indices; } currentAttributes.position.values.push(p0.x, p0.y, p0.z); currentAttributes.position.values.push(p0.x, p0.y, p0.z); currentAttributes.position.values.push(p2.x, p2.y, p2.z); currentAttributes.position.values.push(p2.x, p2.y, p2.z); for (j = i * 3; j < i * 3 + 4 * 3; ++j) { currentAttributes.prevPosition.values.push(prevPositions[j]); currentAttributes.nextPosition.values.push(nextPositions[j]); } for (j = i * 2; j < i * 2 + 4 * 2; ++j) { currentAttributes.expandAndWidth.values.push(expandAndWidths[j]); if (defined(texCoords)) { currentAttributes.st.values.push(texCoords[j]); } } if (defined(colors)) { for (j = i * 4; j < i * 4 + 4 * 4; ++j) { currentAttributes.color.values.push(colors[j]); } } index = currentAttributes.position.values.length / 3 - 4; currentIndices.push(index, index + 2, index + 1); currentIndices.push(index + 1, index + 2, index + 3); } } if (intersectionFound) { updateAdjacencyAfterSplit(westGeometry); updateAdjacencyAfterSplit(eastGeometry); } updateInstanceAfterSplit(instance, westGeometry, eastGeometry); } /** * Splits the instances's geometry, by introducing new vertices and indices,that * intersect the International Date Line and Prime Meridian 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. * * @private * * @param {GeometryInstance} instance The instance to modify. * @returns {GeometryInstance} The modified instance argument, with it's geometry split at the International Date Line. * * @example * instance = Cesium.GeometryPipeline.splitLongitude(instance); */ GeometryPipeline.splitLongitude = function (instance) { //>>includeStart('debug', pragmas.debug); if (!defined(instance)) { throw new DeveloperError("instance is required."); } //>>includeEnd('debug'); var geometry = instance.geometry; var boundingSphere = geometry.boundingSphere; if (defined(boundingSphere)) { var minX = boundingSphere.center.x - boundingSphere.radius; if ( minX > 0 || BoundingSphere.intersectPlane(boundingSphere, Plane.ORIGIN_ZX_PLANE) !== Intersect$1.INTERSECTING ) { return instance; } } if (geometry.geometryType !== GeometryType$1.NONE) { switch (geometry.geometryType) { case GeometryType$1.POLYLINES: splitLongitudePolyline(instance); break; case GeometryType$1.TRIANGLES: splitLongitudeTriangles(instance); break; case GeometryType$1.LINES: splitLongitudeLines(instance); break; } } else { indexPrimitive(geometry); if (geometry.primitiveType === PrimitiveType$1.TRIANGLES) { splitLongitudeTriangles(instance); } else if (geometry.primitiveType === PrimitiveType$1.LINES) { splitLongitudeLines(instance); } } return instance; }; var scratchCartesian1$5 = new Cartesian3(); var scratchCartesian2$7 = new Cartesian3(); var scratchCartesian3$8 = new Cartesian3(); var scratchCartesian4$5 = new Cartesian3(); var texCoordScratch = new Cartesian2(); var textureMatrixScratch$1 = new Matrix3(); var tangentMatrixScratch$1 = new Matrix3(); var quaternionScratch$3 = new Quaternion(); var scratchNormal$5 = new Cartesian3(); var scratchTangent$4 = new Cartesian3(); var scratchBitangent$4 = new Cartesian3(); var scratchCartographic$e = new Cartographic(); var projectedCenterScratch = new Cartesian3(); var scratchMinTexCoord = new Cartesian2(); var scratchMaxTexCoord = new Cartesian2(); function computeTopBottomAttributes(positions, options, extrude) { var vertexFormat = options.vertexFormat; var center = options.center; var semiMajorAxis = options.semiMajorAxis; var semiMinorAxis = options.semiMinorAxis; var ellipsoid = options.ellipsoid; var stRotation = options.stRotation; var size = extrude ? (positions.length / 3) * 2 : positions.length / 3; var shadowVolume = options.shadowVolume; var textureCoordinates = vertexFormat.st ? new Float32Array(size * 2) : undefined; var normals = vertexFormat.normal ? new Float32Array(size * 3) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(size * 3) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(size * 3) : undefined; var extrudeNormals = shadowVolume ? new Float32Array(size * 3) : undefined; var textureCoordIndex = 0; // Raise positions to a height above the ellipsoid and compute the // texture coordinates, normals, tangents, and bitangents. var normal = scratchNormal$5; var tangent = scratchTangent$4; var bitangent = scratchBitangent$4; var projection = new GeographicProjection(ellipsoid); var projectedCenter = projection.project( ellipsoid.cartesianToCartographic(center, scratchCartographic$e), projectedCenterScratch ); var geodeticNormal = ellipsoid.scaleToGeodeticSurface( center, scratchCartesian1$5 ); ellipsoid.geodeticSurfaceNormal(geodeticNormal, geodeticNormal); var textureMatrix = textureMatrixScratch$1; var tangentMatrix = tangentMatrixScratch$1; if (stRotation !== 0) { var rotation = Quaternion.fromAxisAngle( geodeticNormal, stRotation, quaternionScratch$3 ); textureMatrix = Matrix3.fromQuaternion(rotation, textureMatrix); rotation = Quaternion.fromAxisAngle( geodeticNormal, -stRotation, quaternionScratch$3 ); tangentMatrix = Matrix3.fromQuaternion(rotation, tangentMatrix); } else { textureMatrix = Matrix3.clone(Matrix3.IDENTITY, textureMatrix); tangentMatrix = Matrix3.clone(Matrix3.IDENTITY, tangentMatrix); } var minTexCoord = Cartesian2.fromElements( Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, scratchMinTexCoord ); var maxTexCoord = Cartesian2.fromElements( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, scratchMaxTexCoord ); var length = positions.length; var bottomOffset = extrude ? length : 0; var stOffset = (bottomOffset / 3) * 2; for (var i = 0; i < length; i += 3) { var i1 = i + 1; var i2 = i + 2; var position = Cartesian3.fromArray(positions, i, scratchCartesian1$5); if (vertexFormat.st) { var rotatedPoint = Matrix3.multiplyByVector( textureMatrix, position, scratchCartesian2$7 ); var projectedPoint = projection.project( ellipsoid.cartesianToCartographic(rotatedPoint, scratchCartographic$e), scratchCartesian3$8 ); Cartesian3.subtract(projectedPoint, projectedCenter, projectedPoint); texCoordScratch.x = (projectedPoint.x + semiMajorAxis) / (2.0 * semiMajorAxis); texCoordScratch.y = (projectedPoint.y + semiMinorAxis) / (2.0 * semiMinorAxis); minTexCoord.x = Math.min(texCoordScratch.x, minTexCoord.x); minTexCoord.y = Math.min(texCoordScratch.y, minTexCoord.y); maxTexCoord.x = Math.max(texCoordScratch.x, maxTexCoord.x); maxTexCoord.y = Math.max(texCoordScratch.y, maxTexCoord.y); if (extrude) { textureCoordinates[textureCoordIndex + stOffset] = texCoordScratch.x; textureCoordinates[textureCoordIndex + 1 + stOffset] = texCoordScratch.y; } textureCoordinates[textureCoordIndex++] = texCoordScratch.x; textureCoordinates[textureCoordIndex++] = texCoordScratch.y; } if ( vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent || shadowVolume ) { normal = ellipsoid.geodeticSurfaceNormal(position, normal); if (shadowVolume) { extrudeNormals[i + bottomOffset] = -normal.x; extrudeNormals[i1 + bottomOffset] = -normal.y; extrudeNormals[i2 + bottomOffset] = -normal.z; } if ( vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent ) { if (vertexFormat.tangent || vertexFormat.bitangent) { tangent = Cartesian3.normalize( Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent), tangent ); Matrix3.multiplyByVector(tangentMatrix, tangent, tangent); } if (vertexFormat.normal) { normals[i] = normal.x; normals[i1] = normal.y; normals[i2] = normal.z; if (extrude) { normals[i + bottomOffset] = -normal.x; normals[i1 + bottomOffset] = -normal.y; normals[i2 + bottomOffset] = -normal.z; } } if (vertexFormat.tangent) { tangents[i] = tangent.x; tangents[i1] = tangent.y; tangents[i2] = tangent.z; if (extrude) { tangents[i + bottomOffset] = -tangent.x; tangents[i1 + bottomOffset] = -tangent.y; tangents[i2 + bottomOffset] = -tangent.z; } } if (vertexFormat.bitangent) { bitangent = Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); bitangents[i] = bitangent.x; bitangents[i1] = bitangent.y; bitangents[i2] = bitangent.z; if (extrude) { bitangents[i + bottomOffset] = bitangent.x; bitangents[i1 + bottomOffset] = bitangent.y; bitangents[i2 + bottomOffset] = bitangent.z; } } } } } if (vertexFormat.st) { length = textureCoordinates.length; for (var k = 0; k < length; k += 2) { textureCoordinates[k] = (textureCoordinates[k] - minTexCoord.x) / (maxTexCoord.x - minTexCoord.x); textureCoordinates[k + 1] = (textureCoordinates[k + 1] - minTexCoord.y) / (maxTexCoord.y - minTexCoord.y); } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { var finalPositions = EllipseGeometryLibrary.raisePositionsToHeight( positions, options, extrude ); attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: finalPositions, }); } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (shadowVolume) { attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: extrudeNormals, }); } if (extrude && defined(options.offsetAttribute)) { var offsetAttribute = new Uint8Array(size); if (options.offsetAttribute === GeometryOffsetAttribute$1.TOP) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else { var offsetValue = options.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, }); } return attributes; } function topIndices(numPts) { // numTriangles in half = 3 + 8 + 12 + ... = -1 + 4 + (4 + 4) + (4 + 4 + 4) + ... = -1 + 4 * (1 + 2 + 3 + ...) // = -1 + 4 * ((n * ( n + 1)) / 2) // total triangles = 2 * numTrangles in half // indices = total triangles * 3; // Substitute numPts for n above var indices = new Array(12 * (numPts * (numPts + 1)) - 6); var indicesIndex = 0; var prevIndex; var numInterior; var positionIndex; var i; var j; // Indices triangles to the 'right' of the north vector prevIndex = 0; positionIndex = 1; for (i = 0; i < 3; i++) { indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } for (i = 2; i < numPts + 1; ++i) { positionIndex = i * (i + 1) - 1; prevIndex = (i - 1) * i - 1; indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; numInterior = 2 * i; for (j = 0; j < numInterior - 1; ++j) { indices[indicesIndex++] = positionIndex; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } // Indices for center column of triangles numInterior = numPts * 2; ++positionIndex; ++prevIndex; for (i = 0; i < numInterior - 1; ++i) { indices[indicesIndex++] = positionIndex; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } indices[indicesIndex++] = positionIndex; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; // Reverse the process creating indices to the 'left' of the north vector ++prevIndex; for (i = numPts - 1; i > 1; --i) { indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; numInterior = 2 * i; for (j = 0; j < numInterior - 1; ++j) { indices[indicesIndex++] = positionIndex; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = positionIndex++; } for (i = 0; i < 3; i++) { indices[indicesIndex++] = prevIndex++; indices[indicesIndex++] = prevIndex; indices[indicesIndex++] = positionIndex; } return indices; } var boundingSphereCenter$1 = new Cartesian3(); function computeEllipse$1(options) { var center = options.center; boundingSphereCenter$1 = Cartesian3.multiplyByScalar( options.ellipsoid.geodeticSurfaceNormal(center, boundingSphereCenter$1), options.height, boundingSphereCenter$1 ); boundingSphereCenter$1 = Cartesian3.add( center, boundingSphereCenter$1, boundingSphereCenter$1 ); var boundingSphere = new BoundingSphere( boundingSphereCenter$1, options.semiMajorAxis ); var cep = EllipseGeometryLibrary.computeEllipsePositions( options, true, false ); var positions = cep.positions; var numPts = cep.numPts; var attributes = computeTopBottomAttributes(positions, options, false); var indices = topIndices(numPts); indices = IndexDatatype$1.createTypedArray(positions.length / 3, indices); return { boundingSphere: boundingSphere, attributes: attributes, indices: indices, }; } function computeWallAttributes(positions, options) { var vertexFormat = options.vertexFormat; var center = options.center; var semiMajorAxis = options.semiMajorAxis; var semiMinorAxis = options.semiMinorAxis; var ellipsoid = options.ellipsoid; var height = options.height; var extrudedHeight = options.extrudedHeight; var stRotation = options.stRotation; var size = (positions.length / 3) * 2; var finalPositions = new Float64Array(size * 3); var textureCoordinates = vertexFormat.st ? new Float32Array(size * 2) : undefined; var normals = vertexFormat.normal ? new Float32Array(size * 3) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(size * 3) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(size * 3) : undefined; var shadowVolume = options.shadowVolume; var extrudeNormals = shadowVolume ? new Float32Array(size * 3) : undefined; var textureCoordIndex = 0; // Raise positions to a height above the ellipsoid and compute the // texture coordinates, normals, tangents, and bitangents. var normal = scratchNormal$5; var tangent = scratchTangent$4; var bitangent = scratchBitangent$4; var projection = new GeographicProjection(ellipsoid); var projectedCenter = projection.project( ellipsoid.cartesianToCartographic(center, scratchCartographic$e), projectedCenterScratch ); var geodeticNormal = ellipsoid.scaleToGeodeticSurface( center, scratchCartesian1$5 ); ellipsoid.geodeticSurfaceNormal(geodeticNormal, geodeticNormal); var rotation = Quaternion.fromAxisAngle( geodeticNormal, stRotation, quaternionScratch$3 ); var textureMatrix = Matrix3.fromQuaternion(rotation, textureMatrixScratch$1); var minTexCoord = Cartesian2.fromElements( Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, scratchMinTexCoord ); var maxTexCoord = Cartesian2.fromElements( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, scratchMaxTexCoord ); var length = positions.length; var stOffset = (length / 3) * 2; for (var i = 0; i < length; i += 3) { var i1 = i + 1; var i2 = i + 2; var position = Cartesian3.fromArray(positions, i, scratchCartesian1$5); var extrudedPosition; if (vertexFormat.st) { var rotatedPoint = Matrix3.multiplyByVector( textureMatrix, position, scratchCartesian2$7 ); var projectedPoint = projection.project( ellipsoid.cartesianToCartographic(rotatedPoint, scratchCartographic$e), scratchCartesian3$8 ); Cartesian3.subtract(projectedPoint, projectedCenter, projectedPoint); texCoordScratch.x = (projectedPoint.x + semiMajorAxis) / (2.0 * semiMajorAxis); texCoordScratch.y = (projectedPoint.y + semiMinorAxis) / (2.0 * semiMinorAxis); minTexCoord.x = Math.min(texCoordScratch.x, minTexCoord.x); minTexCoord.y = Math.min(texCoordScratch.y, minTexCoord.y); maxTexCoord.x = Math.max(texCoordScratch.x, maxTexCoord.x); maxTexCoord.y = Math.max(texCoordScratch.y, maxTexCoord.y); textureCoordinates[textureCoordIndex + stOffset] = texCoordScratch.x; textureCoordinates[textureCoordIndex + 1 + stOffset] = texCoordScratch.y; textureCoordinates[textureCoordIndex++] = texCoordScratch.x; textureCoordinates[textureCoordIndex++] = texCoordScratch.y; } position = ellipsoid.scaleToGeodeticSurface(position, position); extrudedPosition = Cartesian3.clone(position, scratchCartesian2$7); normal = ellipsoid.geodeticSurfaceNormal(position, normal); if (shadowVolume) { extrudeNormals[i + length] = -normal.x; extrudeNormals[i1 + length] = -normal.y; extrudeNormals[i2 + length] = -normal.z; } var scaledNormal = Cartesian3.multiplyByScalar( normal, height, scratchCartesian4$5 ); position = Cartesian3.add(position, scaledNormal, position); scaledNormal = Cartesian3.multiplyByScalar( normal, extrudedHeight, scaledNormal ); extrudedPosition = Cartesian3.add( extrudedPosition, scaledNormal, extrudedPosition ); if (vertexFormat.position) { finalPositions[i + length] = extrudedPosition.x; finalPositions[i1 + length] = extrudedPosition.y; finalPositions[i2 + length] = extrudedPosition.z; finalPositions[i] = position.x; finalPositions[i1] = position.y; finalPositions[i2] = position.z; } if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) { bitangent = Cartesian3.clone(normal, bitangent); var next = Cartesian3.fromArray( positions, (i + 3) % length, scratchCartesian4$5 ); Cartesian3.subtract(next, position, next); var bottom = Cartesian3.subtract( extrudedPosition, position, scratchCartesian3$8 ); normal = Cartesian3.normalize( Cartesian3.cross(bottom, next, normal), normal ); if (vertexFormat.normal) { normals[i] = normal.x; normals[i1] = normal.y; normals[i2] = normal.z; normals[i + length] = normal.x; normals[i1 + length] = normal.y; normals[i2 + length] = normal.z; } if (vertexFormat.tangent) { tangent = Cartesian3.normalize( Cartesian3.cross(bitangent, normal, tangent), tangent ); tangents[i] = tangent.x; tangents[i1] = tangent.y; tangents[i2] = tangent.z; tangents[i + length] = tangent.x; tangents[i + 1 + length] = tangent.y; tangents[i + 2 + length] = tangent.z; } if (vertexFormat.bitangent) { bitangents[i] = bitangent.x; bitangents[i1] = bitangent.y; bitangents[i2] = bitangent.z; bitangents[i + length] = bitangent.x; bitangents[i1 + length] = bitangent.y; bitangents[i2 + length] = bitangent.z; } } } if (vertexFormat.st) { length = textureCoordinates.length; for (var k = 0; k < length; k += 2) { textureCoordinates[k] = (textureCoordinates[k] - minTexCoord.x) / (maxTexCoord.x - minTexCoord.x); textureCoordinates[k + 1] = (textureCoordinates[k + 1] - minTexCoord.y) / (maxTexCoord.y - minTexCoord.y); } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: finalPositions, }); } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (shadowVolume) { attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: extrudeNormals, }); } if (defined(options.offsetAttribute)) { var offsetAttribute = new Uint8Array(size); if (options.offsetAttribute === GeometryOffsetAttribute$1.TOP) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else { var offsetValue = options.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, }); } return attributes; } function computeWallIndices(positions) { var length = positions.length / 3; var indices = IndexDatatype$1.createTypedArray(length, length * 6); var index = 0; for (var i = 0; i < length; i++) { var UL = i; var LL = i + length; var UR = (UL + 1) % length; var LR = UR + length; indices[index++] = UL; indices[index++] = LL; indices[index++] = UR; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; } return indices; } var topBoundingSphere$3 = new BoundingSphere(); var bottomBoundingSphere$3 = new BoundingSphere(); function computeExtrudedEllipse$1(options) { var center = options.center; var ellipsoid = options.ellipsoid; var semiMajorAxis = options.semiMajorAxis; var scaledNormal = Cartesian3.multiplyByScalar( ellipsoid.geodeticSurfaceNormal(center, scratchCartesian1$5), options.height, scratchCartesian1$5 ); topBoundingSphere$3.center = Cartesian3.add( center, scaledNormal, topBoundingSphere$3.center ); topBoundingSphere$3.radius = semiMajorAxis; scaledNormal = Cartesian3.multiplyByScalar( ellipsoid.geodeticSurfaceNormal(center, scaledNormal), options.extrudedHeight, scaledNormal ); bottomBoundingSphere$3.center = Cartesian3.add( center, scaledNormal, bottomBoundingSphere$3.center ); bottomBoundingSphere$3.radius = semiMajorAxis; var cep = EllipseGeometryLibrary.computeEllipsePositions(options, true, true); var positions = cep.positions; var numPts = cep.numPts; var outerPositions = cep.outerPositions; var boundingSphere = BoundingSphere.union( topBoundingSphere$3, bottomBoundingSphere$3 ); var topBottomAttributes = computeTopBottomAttributes( positions, options, true ); var indices = topIndices(numPts); var length = indices.length; indices.length = length * 2; var posLength = positions.length / 3; for (var i = 0; i < length; i += 3) { indices[i + length] = indices[i + 2] + posLength; indices[i + 1 + length] = indices[i + 1] + posLength; indices[i + 2 + length] = indices[i] + posLength; } var topBottomIndices = IndexDatatype$1.createTypedArray( (posLength * 2) / 3, indices ); var topBottomGeo = new Geometry({ attributes: topBottomAttributes, indices: topBottomIndices, primitiveType: PrimitiveType$1.TRIANGLES, }); var wallAttributes = computeWallAttributes(outerPositions, options); indices = computeWallIndices(outerPositions); var wallIndices = IndexDatatype$1.createTypedArray( (outerPositions.length * 2) / 3, indices ); var wallGeo = new Geometry({ attributes: wallAttributes, indices: wallIndices, primitiveType: PrimitiveType$1.TRIANGLES, }); var geo = GeometryPipeline.combineInstances([ new GeometryInstance({ geometry: topBottomGeo, }), new GeometryInstance({ geometry: wallGeo, }), ]); return { boundingSphere: boundingSphere, attributes: geo[0].attributes, indices: geo[0].indices, }; } function computeRectangle$3( center, semiMajorAxis, semiMinorAxis, rotation, granularity, ellipsoid, result ) { var cep = EllipseGeometryLibrary.computeEllipsePositions( { center: center, semiMajorAxis: semiMajorAxis, semiMinorAxis: semiMinorAxis, rotation: rotation, granularity: granularity, }, false, true ); var positionsFlat = cep.outerPositions; var positionsCount = positionsFlat.length / 3; var positions = new Array(positionsCount); for (var i = 0; i < positionsCount; ++i) { positions[i] = Cartesian3.fromArray(positionsFlat, i * 3); } var rectangle = Rectangle.fromCartesianArray(positions, ellipsoid, result); // Rectangle width goes beyond 180 degrees when the ellipse crosses a pole. // When this happens, make the rectangle into a "circle" around the pole if (rectangle.width > CesiumMath.PI) { rectangle.north = rectangle.north > 0.0 ? CesiumMath.PI_OVER_TWO - CesiumMath.EPSILON7 : rectangle.north; rectangle.south = rectangle.south < 0.0 ? CesiumMath.EPSILON7 - CesiumMath.PI_OVER_TWO : rectangle.south; rectangle.east = CesiumMath.PI; rectangle.west = -CesiumMath.PI; } return rectangle; } /** * A description of an ellipse on an ellipsoid. Ellipse geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @alias EllipseGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.center The ellipse's center point in the fixed frame. * @param {Number} options.semiMajorAxis The length of the ellipse's semi-major axis in meters. * @param {Number} options.semiMinorAxis The length of the ellipse's semi-minor axis in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the ellipse will be on. * @param {Number} [options.height=0.0] The distance in meters between the ellipse and the ellipsoid surface. * @param {Number} [options.extrudedHeight] The distance in meters between the ellipse's extruded face and the ellipsoid surface. * @param {Number} [options.rotation=0.0] The angle of rotation counter-clockwise from north. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates counter-clockwise from north. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The angular distance between points on the ellipse in radians. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @exception {DeveloperError} semiMajorAxis and semiMinorAxis must be greater than zero. * @exception {DeveloperError} semiMajorAxis must be greater than or equal to the semiMinorAxis. * @exception {DeveloperError} granularity must be greater than zero. * * * @example * // Create an ellipse. * var ellipse = new Cesium.EllipseGeometry({ * center : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), * semiMajorAxis : 500000.0, * semiMinorAxis : 300000.0, * rotation : Cesium.Math.toRadians(60.0) * }); * var geometry = Cesium.EllipseGeometry.createGeometry(ellipse); * * @see EllipseGeometry.createGeometry */ function EllipseGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var center = options.center; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var semiMajorAxis = options.semiMajorAxis; var semiMinorAxis = options.semiMinorAxis; var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); //>>includeStart('debug', pragmas.debug); Check.defined("options.center", center); Check.typeOf.number("options.semiMajorAxis", semiMajorAxis); Check.typeOf.number("options.semiMinorAxis", semiMinorAxis); if (semiMajorAxis < semiMinorAxis) { throw new DeveloperError( "semiMajorAxis must be greater than or equal to the semiMinorAxis." ); } if (granularity <= 0.0) { throw new DeveloperError("granularity must be greater than zero."); } //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._center = Cartesian3.clone(center); this._semiMajorAxis = semiMajorAxis; this._semiMinorAxis = semiMinorAxis; this._ellipsoid = Ellipsoid.clone(ellipsoid); this._rotation = defaultValue(options.rotation, 0.0); this._stRotation = defaultValue(options.stRotation, 0.0); this._height = Math.max(extrudedHeight, height); this._granularity = granularity; this._vertexFormat = VertexFormat.clone(vertexFormat); this._extrudedHeight = Math.min(extrudedHeight, height); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = "createEllipseGeometry"; this._offsetAttribute = options.offsetAttribute; this._rectangle = undefined; this._textureCoordinateRotationPoints = undefined; } /** * The number of elements used to pack the object into an array. * @type {Number} */ EllipseGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 9; /** * Stores the provided instance into the provided array. * * @param {EllipseGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ EllipseGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.defined("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._center, array, startingIndex); startingIndex += Cartesian3.packedLength; Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._semiMajorAxis; array[startingIndex++] = value._semiMinorAxis; array[startingIndex++] = value._rotation; array[startingIndex++] = value._stRotation; array[startingIndex++] = value._height; array[startingIndex++] = value._granularity; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchCenter$5 = new Cartesian3(); var scratchEllipsoid$e = new Ellipsoid(); var scratchVertexFormat$b = new VertexFormat(); var scratchOptions$l = { center: scratchCenter$5, ellipsoid: scratchEllipsoid$e, vertexFormat: scratchVertexFormat$b, semiMajorAxis: undefined, semiMinorAxis: undefined, rotation: undefined, stRotation: undefined, height: undefined, granularity: undefined, extrudedHeight: undefined, shadowVolume: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {EllipseGeometry} [result] The object into which to store the result. * @returns {EllipseGeometry} The modified result parameter or a new EllipseGeometry instance if one was not provided. */ EllipseGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var center = Cartesian3.unpack(array, startingIndex, scratchCenter$5); startingIndex += Cartesian3.packedLength; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$e); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$b ); startingIndex += VertexFormat.packedLength; var semiMajorAxis = array[startingIndex++]; var semiMinorAxis = array[startingIndex++]; var rotation = array[startingIndex++]; var stRotation = array[startingIndex++]; var height = array[startingIndex++]; var granularity = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var shadowVolume = array[startingIndex++] === 1.0; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$l.height = height; scratchOptions$l.extrudedHeight = extrudedHeight; scratchOptions$l.granularity = granularity; scratchOptions$l.stRotation = stRotation; scratchOptions$l.rotation = rotation; scratchOptions$l.semiMajorAxis = semiMajorAxis; scratchOptions$l.semiMinorAxis = semiMinorAxis; scratchOptions$l.shadowVolume = shadowVolume; scratchOptions$l.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new EllipseGeometry(scratchOptions$l); } result._center = Cartesian3.clone(center, result._center); result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._semiMajorAxis = semiMajorAxis; result._semiMinorAxis = semiMinorAxis; result._rotation = rotation; result._stRotation = stRotation; result._height = height; result._granularity = granularity; result._extrudedHeight = extrudedHeight; result._shadowVolume = shadowVolume; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the bounding rectangle based on the provided options * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.center The ellipse's center point in the fixed frame. * @param {Number} options.semiMajorAxis The length of the ellipse's semi-major axis in meters. * @param {Number} options.semiMinorAxis The length of the ellipse's semi-minor axis in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the ellipse will be on. * @param {Number} [options.rotation=0.0] The angle of rotation counter-clockwise from north. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The angular distance between points on the ellipse in radians. * @param {Rectangle} [result] An object in which to store the result * * @returns {Rectangle} The result rectangle */ EllipseGeometry.computeRectangle = function (options, result) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var center = options.center; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var semiMajorAxis = options.semiMajorAxis; var semiMinorAxis = options.semiMinorAxis; var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var rotation = defaultValue(options.rotation, 0.0); //>>includeStart('debug', pragmas.debug); Check.defined("options.center", center); Check.typeOf.number("options.semiMajorAxis", semiMajorAxis); Check.typeOf.number("options.semiMinorAxis", semiMinorAxis); if (semiMajorAxis < semiMinorAxis) { throw new DeveloperError( "semiMajorAxis must be greater than or equal to the semiMinorAxis." ); } if (granularity <= 0.0) { throw new DeveloperError("granularity must be greater than zero."); } //>>includeEnd('debug'); return computeRectangle$3( center, semiMajorAxis, semiMinorAxis, rotation, granularity, ellipsoid, result ); }; /** * Computes the geometric representation of a ellipse on an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {EllipseGeometry} ellipseGeometry A description of the ellipse. * @returns {Geometry|undefined} The computed vertices and indices. */ EllipseGeometry.createGeometry = function (ellipseGeometry) { if ( ellipseGeometry._semiMajorAxis <= 0.0 || ellipseGeometry._semiMinorAxis <= 0.0 ) { return; } var height = ellipseGeometry._height; var extrudedHeight = ellipseGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( height, extrudedHeight, 0, CesiumMath.EPSILON2 ); ellipseGeometry._center = ellipseGeometry._ellipsoid.scaleToGeodeticSurface( ellipseGeometry._center, ellipseGeometry._center ); var options = { center: ellipseGeometry._center, semiMajorAxis: ellipseGeometry._semiMajorAxis, semiMinorAxis: ellipseGeometry._semiMinorAxis, ellipsoid: ellipseGeometry._ellipsoid, rotation: ellipseGeometry._rotation, height: height, granularity: ellipseGeometry._granularity, vertexFormat: ellipseGeometry._vertexFormat, stRotation: ellipseGeometry._stRotation, }; var geometry; if (extrude) { options.extrudedHeight = extrudedHeight; options.shadowVolume = ellipseGeometry._shadowVolume; options.offsetAttribute = ellipseGeometry._offsetAttribute; geometry = computeExtrudedEllipse$1(options); } else { geometry = computeEllipse$1(options); if (defined(ellipseGeometry._offsetAttribute)) { var length = geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = ellipseGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } } return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: geometry.boundingSphere, offsetAttribute: ellipseGeometry._offsetAttribute, }); }; /** * @private */ EllipseGeometry.createShadowVolume = function ( ellipseGeometry, minHeightFunc, maxHeightFunc ) { var granularity = ellipseGeometry._granularity; var ellipsoid = ellipseGeometry._ellipsoid; var minHeight = minHeightFunc(granularity, ellipsoid); var maxHeight = maxHeightFunc(granularity, ellipsoid); return new EllipseGeometry({ center: ellipseGeometry._center, semiMajorAxis: ellipseGeometry._semiMajorAxis, semiMinorAxis: ellipseGeometry._semiMinorAxis, ellipsoid: ellipsoid, rotation: ellipseGeometry._rotation, stRotation: ellipseGeometry._stRotation, granularity: granularity, extrudedHeight: minHeight, height: maxHeight, vertexFormat: VertexFormat.POSITION_ONLY, shadowVolume: true, }); }; function textureCoordinateRotationPoints$2(ellipseGeometry) { var stRotation = -ellipseGeometry._stRotation; if (stRotation === 0.0) { return [0, 0, 0, 1, 1, 0]; } var cep = EllipseGeometryLibrary.computeEllipsePositions( { center: ellipseGeometry._center, semiMajorAxis: ellipseGeometry._semiMajorAxis, semiMinorAxis: ellipseGeometry._semiMinorAxis, rotation: ellipseGeometry._rotation, granularity: ellipseGeometry._granularity, }, false, true ); var positionsFlat = cep.outerPositions; var positionsCount = positionsFlat.length / 3; var positions = new Array(positionsCount); for (var i = 0; i < positionsCount; ++i) { positions[i] = Cartesian3.fromArray(positionsFlat, i * 3); } var ellipsoid = ellipseGeometry._ellipsoid; var boundingRectangle = ellipseGeometry.rectangle; return Geometry._textureCoordinateRotationPoints( positions, stRotation, ellipsoid, boundingRectangle ); } Object.defineProperties(EllipseGeometry.prototype, { /** * @private */ rectangle: { get: function () { if (!defined(this._rectangle)) { this._rectangle = computeRectangle$3( this._center, this._semiMajorAxis, this._semiMinorAxis, this._rotation, this._granularity, this._ellipsoid ); } return this._rectangle; }, }, /** * For remapping texture coordinates when rendering EllipseGeometries as GroundPrimitives. * @private */ textureCoordinateRotationPoints: { get: function () { if (!defined(this._textureCoordinateRotationPoints)) { this._textureCoordinateRotationPoints = textureCoordinateRotationPoints$2( this ); } return this._textureCoordinateRotationPoints; }, }, }); /** * A description of a circle on the ellipsoid. Circle geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @alias CircleGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.center The circle's center point in the fixed frame. * @param {Number} options.radius The radius in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the circle will be on. * @param {Number} [options.height=0.0] The distance in meters between the circle and the ellipsoid surface. * @param {Number} [options.granularity=0.02] The angular distance between points on the circle in radians. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Number} [options.extrudedHeight=0.0] The distance in meters between the circle's extruded face and the ellipsoid surface. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * * @exception {DeveloperError} radius must be greater than zero. * @exception {DeveloperError} granularity must be greater than zero. * * @see CircleGeometry.createGeometry * @see Packable * * @example * // Create a circle. * var circle = new Cesium.CircleGeometry({ * center : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), * radius : 100000.0 * }); * var geometry = Cesium.CircleGeometry.createGeometry(circle); */ function CircleGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radius = options.radius; //>>includeStart('debug', pragmas.debug); Check.typeOf.number("radius", radius); //>>includeEnd('debug'); var ellipseGeometryOptions = { center: options.center, semiMajorAxis: radius, semiMinorAxis: radius, ellipsoid: options.ellipsoid, height: options.height, extrudedHeight: options.extrudedHeight, granularity: options.granularity, vertexFormat: options.vertexFormat, stRotation: options.stRotation, shadowVolume: options.shadowVolume, }; this._ellipseGeometry = new EllipseGeometry(ellipseGeometryOptions); this._workerName = "createCircleGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ CircleGeometry.packedLength = EllipseGeometry.packedLength; /** * Stores the provided instance into the provided array. * * @param {CircleGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CircleGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); //>>includeEnd('debug'); return EllipseGeometry.pack(value._ellipseGeometry, array, startingIndex); }; var scratchEllipseGeometry$1 = new EllipseGeometry({ center: new Cartesian3(), semiMajorAxis: 1.0, semiMinorAxis: 1.0, }); var scratchOptions$k = { center: new Cartesian3(), radius: undefined, ellipsoid: Ellipsoid.clone(Ellipsoid.UNIT_SPHERE), height: undefined, extrudedHeight: undefined, granularity: undefined, vertexFormat: new VertexFormat(), stRotation: undefined, semiMajorAxis: undefined, semiMinorAxis: undefined, shadowVolume: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CircleGeometry} [result] The object into which to store the result. * @returns {CircleGeometry} The modified result parameter or a new CircleGeometry instance if one was not provided. */ CircleGeometry.unpack = function (array, startingIndex, result) { var ellipseGeometry = EllipseGeometry.unpack( array, startingIndex, scratchEllipseGeometry$1 ); scratchOptions$k.center = Cartesian3.clone( ellipseGeometry._center, scratchOptions$k.center ); scratchOptions$k.ellipsoid = Ellipsoid.clone( ellipseGeometry._ellipsoid, scratchOptions$k.ellipsoid ); scratchOptions$k.height = ellipseGeometry._height; scratchOptions$k.extrudedHeight = ellipseGeometry._extrudedHeight; scratchOptions$k.granularity = ellipseGeometry._granularity; scratchOptions$k.vertexFormat = VertexFormat.clone( ellipseGeometry._vertexFormat, scratchOptions$k.vertexFormat ); scratchOptions$k.stRotation = ellipseGeometry._stRotation; scratchOptions$k.shadowVolume = ellipseGeometry._shadowVolume; if (!defined(result)) { scratchOptions$k.radius = ellipseGeometry._semiMajorAxis; return new CircleGeometry(scratchOptions$k); } scratchOptions$k.semiMajorAxis = ellipseGeometry._semiMajorAxis; scratchOptions$k.semiMinorAxis = ellipseGeometry._semiMinorAxis; result._ellipseGeometry = new EllipseGeometry(scratchOptions$k); return result; }; /** * Computes the geometric representation of a circle on an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {CircleGeometry} circleGeometry A description of the circle. * @returns {Geometry|undefined} The computed vertices and indices. */ CircleGeometry.createGeometry = function (circleGeometry) { return EllipseGeometry.createGeometry(circleGeometry._ellipseGeometry); }; /** * @private */ CircleGeometry.createShadowVolume = function ( circleGeometry, minHeightFunc, maxHeightFunc ) { var granularity = circleGeometry._ellipseGeometry._granularity; var ellipsoid = circleGeometry._ellipseGeometry._ellipsoid; var minHeight = minHeightFunc(granularity, ellipsoid); var maxHeight = maxHeightFunc(granularity, ellipsoid); return new CircleGeometry({ center: circleGeometry._ellipseGeometry._center, radius: circleGeometry._ellipseGeometry._semiMajorAxis, ellipsoid: ellipsoid, stRotation: circleGeometry._ellipseGeometry._stRotation, granularity: granularity, extrudedHeight: minHeight, height: maxHeight, vertexFormat: VertexFormat.POSITION_ONLY, shadowVolume: true, }); }; Object.defineProperties(CircleGeometry.prototype, { /** * @private */ rectangle: { get: function () { return this._ellipseGeometry.rectangle; }, }, /** * For remapping texture coordinates when rendering CircleGeometries as GroundPrimitives. * @private */ textureCoordinateRotationPoints: { get: function () { return this._ellipseGeometry.textureCoordinateRotationPoints; }, }, }); var scratchCartesian1$4 = new Cartesian3(); var boundingSphereCenter = new Cartesian3(); function computeEllipse(options) { var center = options.center; boundingSphereCenter = Cartesian3.multiplyByScalar( options.ellipsoid.geodeticSurfaceNormal(center, boundingSphereCenter), options.height, boundingSphereCenter ); boundingSphereCenter = Cartesian3.add( center, boundingSphereCenter, boundingSphereCenter ); var boundingSphere = new BoundingSphere( boundingSphereCenter, options.semiMajorAxis ); var positions = EllipseGeometryLibrary.computeEllipsePositions( options, false, true ).outerPositions; var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: EllipseGeometryLibrary.raisePositionsToHeight( positions, options, false ), }), }); var length = positions.length / 3; var indices = IndexDatatype$1.createTypedArray(length, length * 2); var index = 0; for (var i = 0; i < length; ++i) { indices[index++] = i; indices[index++] = (i + 1) % length; } return { boundingSphere: boundingSphere, attributes: attributes, indices: indices, }; } var topBoundingSphere$2 = new BoundingSphere(); var bottomBoundingSphere$2 = new BoundingSphere(); function computeExtrudedEllipse(options) { var center = options.center; var ellipsoid = options.ellipsoid; var semiMajorAxis = options.semiMajorAxis; var scaledNormal = Cartesian3.multiplyByScalar( ellipsoid.geodeticSurfaceNormal(center, scratchCartesian1$4), options.height, scratchCartesian1$4 ); topBoundingSphere$2.center = Cartesian3.add( center, scaledNormal, topBoundingSphere$2.center ); topBoundingSphere$2.radius = semiMajorAxis; scaledNormal = Cartesian3.multiplyByScalar( ellipsoid.geodeticSurfaceNormal(center, scaledNormal), options.extrudedHeight, scaledNormal ); bottomBoundingSphere$2.center = Cartesian3.add( center, scaledNormal, bottomBoundingSphere$2.center ); bottomBoundingSphere$2.radius = semiMajorAxis; var positions = EllipseGeometryLibrary.computeEllipsePositions( options, false, true ).outerPositions; var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: EllipseGeometryLibrary.raisePositionsToHeight( positions, options, true ), }), }); positions = attributes.position.values; var boundingSphere = BoundingSphere.union( topBoundingSphere$2, bottomBoundingSphere$2 ); var length = positions.length / 3; if (defined(options.offsetAttribute)) { var applyOffset = new Uint8Array(length); if (options.offsetAttribute === GeometryOffsetAttribute$1.TOP) { applyOffset = arrayFill(applyOffset, 1, 0, length / 2); } else { var offsetValue = options.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; applyOffset = arrayFill(applyOffset, offsetValue); } attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } var numberOfVerticalLines = defaultValue(options.numberOfVerticalLines, 16); numberOfVerticalLines = CesiumMath.clamp( numberOfVerticalLines, 0, length / 2 ); var indices = IndexDatatype$1.createTypedArray( length, length * 2 + numberOfVerticalLines * 2 ); length /= 2; var index = 0; var i; for (i = 0; i < length; ++i) { indices[index++] = i; indices[index++] = (i + 1) % length; indices[index++] = i + length; indices[index++] = ((i + 1) % length) + length; } var numSide; if (numberOfVerticalLines > 0) { var numSideLines = Math.min(numberOfVerticalLines, length); numSide = Math.round(length / numSideLines); var maxI = Math.min(numSide * numberOfVerticalLines, length); for (i = 0; i < maxI; i += numSide) { indices[index++] = i; indices[index++] = i + length; } } return { boundingSphere: boundingSphere, attributes: attributes, indices: indices, }; } /** * A description of the outline of an ellipse on an ellipsoid. * * @alias EllipseOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.center The ellipse's center point in the fixed frame. * @param {Number} options.semiMajorAxis The length of the ellipse's semi-major axis in meters. * @param {Number} options.semiMinorAxis The length of the ellipse's semi-minor axis in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the ellipse will be on. * @param {Number} [options.height=0.0] The distance in meters between the ellipse and the ellipsoid surface. * @param {Number} [options.extrudedHeight] The distance in meters between the ellipse's extruded face and the ellipsoid surface. * @param {Number} [options.rotation=0.0] The angle from north (counter-clockwise) in radians. * @param {Number} [options.granularity=0.02] The angular distance between points on the ellipse in radians. * @param {Number} [options.numberOfVerticalLines=16] Number of lines to draw between the top and bottom surface of an extruded ellipse. * * @exception {DeveloperError} semiMajorAxis and semiMinorAxis must be greater than zero. * @exception {DeveloperError} semiMajorAxis must be greater than or equal to the semiMinorAxis. * @exception {DeveloperError} granularity must be greater than zero. * * @see EllipseOutlineGeometry.createGeometry * * @example * var ellipse = new Cesium.EllipseOutlineGeometry({ * center : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), * semiMajorAxis : 500000.0, * semiMinorAxis : 300000.0, * rotation : Cesium.Math.toRadians(60.0) * }); * var geometry = Cesium.EllipseOutlineGeometry.createGeometry(ellipse); */ function EllipseOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var center = options.center; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var semiMajorAxis = options.semiMajorAxis; var semiMinorAxis = options.semiMinorAxis; var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); //>>includeStart('debug', pragmas.debug); if (!defined(center)) { throw new DeveloperError("center is required."); } if (!defined(semiMajorAxis)) { throw new DeveloperError("semiMajorAxis is required."); } if (!defined(semiMinorAxis)) { throw new DeveloperError("semiMinorAxis is required."); } if (semiMajorAxis < semiMinorAxis) { throw new DeveloperError( "semiMajorAxis must be greater than or equal to the semiMinorAxis." ); } if (granularity <= 0.0) { throw new DeveloperError("granularity must be greater than zero."); } //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._center = Cartesian3.clone(center); this._semiMajorAxis = semiMajorAxis; this._semiMinorAxis = semiMinorAxis; this._ellipsoid = Ellipsoid.clone(ellipsoid); this._rotation = defaultValue(options.rotation, 0.0); this._height = Math.max(extrudedHeight, height); this._granularity = granularity; this._extrudedHeight = Math.min(extrudedHeight, height); this._numberOfVerticalLines = Math.max( defaultValue(options.numberOfVerticalLines, 16), 0 ); this._offsetAttribute = options.offsetAttribute; this._workerName = "createEllipseOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ EllipseOutlineGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + 8; /** * Stores the provided instance into the provided array. * * @param {EllipseOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ EllipseOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._center, array, startingIndex); startingIndex += Cartesian3.packedLength; Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._semiMajorAxis; array[startingIndex++] = value._semiMinorAxis; array[startingIndex++] = value._rotation; array[startingIndex++] = value._height; array[startingIndex++] = value._granularity; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._numberOfVerticalLines; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchCenter$4 = new Cartesian3(); var scratchEllipsoid$d = new Ellipsoid(); var scratchOptions$j = { center: scratchCenter$4, ellipsoid: scratchEllipsoid$d, semiMajorAxis: undefined, semiMinorAxis: undefined, rotation: undefined, height: undefined, granularity: undefined, extrudedHeight: undefined, numberOfVerticalLines: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {EllipseOutlineGeometry} [result] The object into which to store the result. * @returns {EllipseOutlineGeometry} The modified result parameter or a new EllipseOutlineGeometry instance if one was not provided. */ EllipseOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var center = Cartesian3.unpack(array, startingIndex, scratchCenter$4); startingIndex += Cartesian3.packedLength; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$d); startingIndex += Ellipsoid.packedLength; var semiMajorAxis = array[startingIndex++]; var semiMinorAxis = array[startingIndex++]; var rotation = array[startingIndex++]; var height = array[startingIndex++]; var granularity = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var numberOfVerticalLines = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$j.height = height; scratchOptions$j.extrudedHeight = extrudedHeight; scratchOptions$j.granularity = granularity; scratchOptions$j.rotation = rotation; scratchOptions$j.semiMajorAxis = semiMajorAxis; scratchOptions$j.semiMinorAxis = semiMinorAxis; scratchOptions$j.numberOfVerticalLines = numberOfVerticalLines; scratchOptions$j.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new EllipseOutlineGeometry(scratchOptions$j); } result._center = Cartesian3.clone(center, result._center); result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._semiMajorAxis = semiMajorAxis; result._semiMinorAxis = semiMinorAxis; result._rotation = rotation; result._height = height; result._granularity = granularity; result._extrudedHeight = extrudedHeight; result._numberOfVerticalLines = numberOfVerticalLines; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of an outline of an ellipse on an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {EllipseOutlineGeometry} ellipseGeometry A description of the ellipse. * @returns {Geometry|undefined} The computed vertices and indices. */ EllipseOutlineGeometry.createGeometry = function (ellipseGeometry) { if ( ellipseGeometry._semiMajorAxis <= 0.0 || ellipseGeometry._semiMinorAxis <= 0.0 ) { return; } var height = ellipseGeometry._height; var extrudedHeight = ellipseGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( height, extrudedHeight, 0, CesiumMath.EPSILON2 ); ellipseGeometry._center = ellipseGeometry._ellipsoid.scaleToGeodeticSurface( ellipseGeometry._center, ellipseGeometry._center ); var options = { center: ellipseGeometry._center, semiMajorAxis: ellipseGeometry._semiMajorAxis, semiMinorAxis: ellipseGeometry._semiMinorAxis, ellipsoid: ellipseGeometry._ellipsoid, rotation: ellipseGeometry._rotation, height: height, granularity: ellipseGeometry._granularity, numberOfVerticalLines: ellipseGeometry._numberOfVerticalLines, }; var geometry; if (extrude) { options.extrudedHeight = extrudedHeight; options.offsetAttribute = ellipseGeometry._offsetAttribute; geometry = computeExtrudedEllipse(options); } else { geometry = computeEllipse(options); if (defined(ellipseGeometry._offsetAttribute)) { var length = geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = ellipseGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } } return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: geometry.boundingSphere, offsetAttribute: ellipseGeometry._offsetAttribute, }); }; /** * A description of the outline of a circle on the ellipsoid. * * @alias CircleOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3} options.center The circle's center point in the fixed frame. * @param {Number} options.radius The radius in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid the circle will be on. * @param {Number} [options.height=0.0] The distance in meters between the circle and the ellipsoid surface. * @param {Number} [options.granularity=0.02] The angular distance between points on the circle in radians. * @param {Number} [options.extrudedHeight=0.0] The distance in meters between the circle's extruded face and the ellipsoid surface. * @param {Number} [options.numberOfVerticalLines=16] Number of lines to draw between the top and bottom of an extruded circle. * * @exception {DeveloperError} radius must be greater than zero. * @exception {DeveloperError} granularity must be greater than zero. * * @see CircleOutlineGeometry.createGeometry * @see Packable * * @example * // Create a circle. * var circle = new Cesium.CircleOutlineGeometry({ * center : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883), * radius : 100000.0 * }); * var geometry = Cesium.CircleOutlineGeometry.createGeometry(circle); */ function CircleOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radius = options.radius; //>>includeStart('debug', pragmas.debug); Check.typeOf.number("radius", radius); //>>includeEnd('debug'); var ellipseGeometryOptions = { center: options.center, semiMajorAxis: radius, semiMinorAxis: radius, ellipsoid: options.ellipsoid, height: options.height, extrudedHeight: options.extrudedHeight, granularity: options.granularity, numberOfVerticalLines: options.numberOfVerticalLines, }; this._ellipseGeometry = new EllipseOutlineGeometry(ellipseGeometryOptions); this._workerName = "createCircleOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ CircleOutlineGeometry.packedLength = EllipseOutlineGeometry.packedLength; /** * Stores the provided instance into the provided array. * * @param {CircleOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CircleOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); //>>includeEnd('debug'); return EllipseOutlineGeometry.pack( value._ellipseGeometry, array, startingIndex ); }; var scratchEllipseGeometry = new EllipseOutlineGeometry({ center: new Cartesian3(), semiMajorAxis: 1.0, semiMinorAxis: 1.0, }); var scratchOptions$i = { center: new Cartesian3(), radius: undefined, ellipsoid: Ellipsoid.clone(Ellipsoid.UNIT_SPHERE), height: undefined, extrudedHeight: undefined, granularity: undefined, numberOfVerticalLines: undefined, semiMajorAxis: undefined, semiMinorAxis: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CircleOutlineGeometry} [result] The object into which to store the result. * @returns {CircleOutlineGeometry} The modified result parameter or a new CircleOutlineGeometry instance if one was not provided. */ CircleOutlineGeometry.unpack = function (array, startingIndex, result) { var ellipseGeometry = EllipseOutlineGeometry.unpack( array, startingIndex, scratchEllipseGeometry ); scratchOptions$i.center = Cartesian3.clone( ellipseGeometry._center, scratchOptions$i.center ); scratchOptions$i.ellipsoid = Ellipsoid.clone( ellipseGeometry._ellipsoid, scratchOptions$i.ellipsoid ); scratchOptions$i.height = ellipseGeometry._height; scratchOptions$i.extrudedHeight = ellipseGeometry._extrudedHeight; scratchOptions$i.granularity = ellipseGeometry._granularity; scratchOptions$i.numberOfVerticalLines = ellipseGeometry._numberOfVerticalLines; if (!defined(result)) { scratchOptions$i.radius = ellipseGeometry._semiMajorAxis; return new CircleOutlineGeometry(scratchOptions$i); } scratchOptions$i.semiMajorAxis = ellipseGeometry._semiMajorAxis; scratchOptions$i.semiMinorAxis = ellipseGeometry._semiMinorAxis; result._ellipseGeometry = new EllipseOutlineGeometry(scratchOptions$i); return result; }; /** * Computes the geometric representation of an outline of a circle on an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {CircleOutlineGeometry} circleGeometry A description of the circle. * @returns {Geometry|undefined} The computed vertices and indices. */ CircleOutlineGeometry.createGeometry = function (circleGeometry) { return EllipseOutlineGeometry.createGeometry(circleGeometry._ellipseGeometry); }; /** * Constants used by {@link Clock#tick} to determine behavior * when {@link Clock#startTime} or {@link Clock#stopTime} is reached. * * @enum {Number} * * @see Clock * @see ClockStep */ var ClockRange = { /** * {@link Clock#tick} will always advances the clock in its current direction. * * @type {Number} * @constant */ UNBOUNDED: 0, /** * When {@link Clock#startTime} or {@link Clock#stopTime} is reached, * {@link Clock#tick} will not advance {@link Clock#currentTime} any further. * * @type {Number} * @constant */ CLAMPED: 1, /** * When {@link Clock#stopTime} is reached, {@link Clock#tick} will advance * {@link Clock#currentTime} to the opposite end of the interval. When * time is moving backwards, {@link Clock#tick} will not advance past * {@link Clock#startTime} * * @type {Number} * @constant */ LOOP_STOP: 2, }; var ClockRange$1 = Object.freeze(ClockRange); /** * Constants to determine how much time advances with each call * to {@link Clock#tick}. * * @enum {Number} * * @see Clock * @see ClockRange */ var ClockStep = { /** * {@link Clock#tick} advances the current time by a fixed step, * which is the number of seconds specified by {@link Clock#multiplier}. * * @type {Number} * @constant */ TICK_DEPENDENT: 0, /** * {@link Clock#tick} advances the current time by the amount of system * time elapsed since the previous call multiplied by {@link Clock#multiplier}. * * @type {Number} * @constant */ SYSTEM_CLOCK_MULTIPLIER: 1, /** * {@link Clock#tick} sets the clock to the current system time; * ignoring all other settings. * * @type {Number} * @constant */ SYSTEM_CLOCK: 2, }; var ClockStep$1 = Object.freeze(ClockStep); /** * Gets a timestamp that can be used in measuring the time between events. Timestamps * are expressed in milliseconds, but it is not specified what the milliseconds are * measured from. This function uses performance.now() if it is available, or Date.now() * otherwise. * * @function getTimestamp * * @returns {Number} The timestamp in milliseconds since some unspecified reference time. */ var getTimestamp; if ( typeof performance !== "undefined" && typeof performance.now === "function" && isFinite(performance.now()) ) { getTimestamp = function () { return performance.now(); }; } else { getTimestamp = function () { return Date.now(); }; } var getTimestamp$1 = getTimestamp; /** * A simple clock for keeping track of simulated time. * * @alias Clock * @constructor * * @param {Object} [options] Object with the following properties: * @param {JulianDate} [options.startTime] The start time of the clock. * @param {JulianDate} [options.stopTime] The stop time of the clock. * @param {JulianDate} [options.currentTime] The current time. * @param {Number} [options.multiplier=1.0] Determines how much time advances when {@link Clock#tick} is called, negative values allow for advancing backwards. * @param {ClockStep} [options.clockStep=ClockStep.SYSTEM_CLOCK_MULTIPLIER] Determines if calls to {@link Clock#tick} are frame dependent or system clock dependent. * @param {ClockRange} [options.clockRange=ClockRange.UNBOUNDED] Determines how the clock should behave when {@link Clock#startTime} or {@link Clock#stopTime} is reached. * @param {Boolean} [options.canAnimate=true] Indicates whether {@link Clock#tick} can advance time. This could be false if data is being buffered, for example. The clock will only tick when both {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true. * @param {Boolean} [options.shouldAnimate=false] Indicates whether {@link Clock#tick} should attempt to advance time. The clock will only tick when both {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true. * * @exception {DeveloperError} startTime must come before stopTime. * * * @example * // Create a clock that loops on Christmas day 2013 and runs in real-time. * var clock = new Cesium.Clock({ * startTime : Cesium.JulianDate.fromIso8601("2013-12-25"), * currentTime : Cesium.JulianDate.fromIso8601("2013-12-25"), * stopTime : Cesium.JulianDate.fromIso8601("2013-12-26"), * clockRange : Cesium.ClockRange.LOOP_STOP, * clockStep : Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER * }); * * @see ClockStep * @see ClockRange * @see JulianDate */ function Clock(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var currentTime = options.currentTime; var startTime = options.startTime; var stopTime = options.stopTime; if (!defined(currentTime)) { // if not specified, current time is the start time, // or if that is not specified, 1 day before the stop time, // or if that is not specified, then now. if (defined(startTime)) { currentTime = JulianDate.clone(startTime); } else if (defined(stopTime)) { currentTime = JulianDate.addDays(stopTime, -1.0, new JulianDate()); } else { currentTime = JulianDate.now(); } } else { currentTime = JulianDate.clone(currentTime); } if (!defined(startTime)) { // if not specified, start time is the current time // (as determined above) startTime = JulianDate.clone(currentTime); } else { startTime = JulianDate.clone(startTime); } if (!defined(stopTime)) { // if not specified, stop time is 1 day after the start time // (as determined above) stopTime = JulianDate.addDays(startTime, 1.0, new JulianDate()); } else { stopTime = JulianDate.clone(stopTime); } //>>includeStart('debug', pragmas.debug); if (JulianDate.greaterThan(startTime, stopTime)) { throw new DeveloperError("startTime must come before stopTime."); } //>>includeEnd('debug'); /** * The start time of the clock. * @type {JulianDate} */ this.startTime = startTime; /** * The stop time of the clock. * @type {JulianDate} */ this.stopTime = stopTime; /** * Determines how the clock should behave when * {@link Clock#startTime} or {@link Clock#stopTime} * is reached. * @type {ClockRange} * @default {@link ClockRange.UNBOUNDED} */ this.clockRange = defaultValue(options.clockRange, ClockRange$1.UNBOUNDED); /** * Indicates whether {@link Clock#tick} can advance time. This could be false if data is being buffered, * for example. The clock will only advance time when both * {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true. * @type {Boolean} * @default true */ this.canAnimate = defaultValue(options.canAnimate, true); /** * An {@link Event} that is fired whenever {@link Clock#tick} is called. * @type {Event} */ this.onTick = new Event(); /** * An {@link Event} that is fired whenever {@link Clock#stopTime} is reached. * @type {Event} */ this.onStop = new Event(); this._currentTime = undefined; this._multiplier = undefined; this._clockStep = undefined; this._shouldAnimate = undefined; this._lastSystemTime = getTimestamp$1(); // set values using the property setters to // make values consistent. this.currentTime = currentTime; this.multiplier = defaultValue(options.multiplier, 1.0); this.shouldAnimate = defaultValue(options.shouldAnimate, false); this.clockStep = defaultValue( options.clockStep, ClockStep$1.SYSTEM_CLOCK_MULTIPLIER ); } Object.defineProperties(Clock.prototype, { /** * The current time. * Changing this property will change * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}. * @memberof Clock.prototype * @type {JulianDate} */ currentTime: { get: function () { return this._currentTime; }, set: function (value) { if (JulianDate.equals(this._currentTime, value)) { return; } if (this._clockStep === ClockStep$1.SYSTEM_CLOCK) { this._clockStep = ClockStep$1.SYSTEM_CLOCK_MULTIPLIER; } this._currentTime = value; }, }, /** * Gets or sets how much time advances when {@link Clock#tick} is called. Negative values allow for advancing backwards. * If {@link Clock#clockStep} is set to {@link ClockStep.TICK_DEPENDENT}, this is the number of seconds to advance. * If {@link Clock#clockStep} is set to {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}, this value is multiplied by the * elapsed system time since the last call to {@link Clock#tick}. * Changing this property will change * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}. * @memberof Clock.prototype * @type {Number} * @default 1.0 */ multiplier: { get: function () { return this._multiplier; }, set: function (value) { if (this._multiplier === value) { return; } if (this._clockStep === ClockStep$1.SYSTEM_CLOCK) { this._clockStep = ClockStep$1.SYSTEM_CLOCK_MULTIPLIER; } this._multiplier = value; }, }, /** * Determines if calls to {@link Clock#tick} are frame dependent or system clock dependent. * Changing this property to {@link ClockStep.SYSTEM_CLOCK} will set * {@link Clock#multiplier} to 1.0, {@link Clock#shouldAnimate} to true, and * {@link Clock#currentTime} to the current system clock time. * @memberof Clock.prototype * @type ClockStep * @default {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER} */ clockStep: { get: function () { return this._clockStep; }, set: function (value) { if (value === ClockStep$1.SYSTEM_CLOCK) { this._multiplier = 1.0; this._shouldAnimate = true; this._currentTime = JulianDate.now(); } this._clockStep = value; }, }, /** * Indicates whether {@link Clock#tick} should attempt to advance time. * The clock will only advance time when both * {@link Clock#canAnimate} and {@link Clock#shouldAnimate} are true. * Changing this property will change * {@link Clock#clockStep} from {@link ClockStep.SYSTEM_CLOCK} to * {@link ClockStep.SYSTEM_CLOCK_MULTIPLIER}. * @memberof Clock.prototype * @type {Boolean} * @default false */ shouldAnimate: { get: function () { return this._shouldAnimate; }, set: function (value) { if (this._shouldAnimate === value) { return; } if (this._clockStep === ClockStep$1.SYSTEM_CLOCK) { this._clockStep = ClockStep$1.SYSTEM_CLOCK_MULTIPLIER; } this._shouldAnimate = value; }, }, }); /** * Advances the clock from the current time based on the current configuration options. * tick should be called every frame, regardless of whether animation is taking place * or not. To control animation, use the {@link Clock#shouldAnimate} property. * * @returns {JulianDate} The new value of the {@link Clock#currentTime} property. */ Clock.prototype.tick = function () { var currentSystemTime = getTimestamp$1(); var currentTime = JulianDate.clone(this._currentTime); if (this.canAnimate && this._shouldAnimate) { var clockStep = this._clockStep; if (clockStep === ClockStep$1.SYSTEM_CLOCK) { currentTime = JulianDate.now(currentTime); } else { var multiplier = this._multiplier; if (clockStep === ClockStep$1.TICK_DEPENDENT) { currentTime = JulianDate.addSeconds( currentTime, multiplier, currentTime ); } else { var milliseconds = currentSystemTime - this._lastSystemTime; currentTime = JulianDate.addSeconds( currentTime, multiplier * (milliseconds / 1000.0), currentTime ); } var clockRange = this.clockRange; var startTime = this.startTime; var stopTime = this.stopTime; if (clockRange === ClockRange$1.CLAMPED) { if (JulianDate.lessThan(currentTime, startTime)) { currentTime = JulianDate.clone(startTime, currentTime); } else if (JulianDate.greaterThan(currentTime, stopTime)) { currentTime = JulianDate.clone(stopTime, currentTime); this.onStop.raiseEvent(this); } } else if (clockRange === ClockRange$1.LOOP_STOP) { if (JulianDate.lessThan(currentTime, startTime)) { currentTime = JulianDate.clone(startTime, currentTime); } while (JulianDate.greaterThan(currentTime, stopTime)) { currentTime = JulianDate.addSeconds( startTime, JulianDate.secondsDifference(currentTime, stopTime), currentTime ); this.onStop.raiseEvent(this); } } } } this._currentTime = currentTime; this._lastSystemTime = currentSystemTime; this.onTick.raiseEvent(this); return currentTime; }; 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 0 (no intensity) to 1.0 (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 */ function Color(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 Color instance from a {@link Cartesian4}. x, y, z, * and w map to red, green, blue, and alpha, respectively. * * @param {Cartesian4} cartesian The source cartesian. * @param {Color} [result] The object onto which to store the result. * @returns {Color} The modified result parameter or a new Color instance if one was not provided. */ Color.fromCartesian4 = function (cartesian, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian", cartesian); //>>includeEnd('debug'); if (!defined(result)) { return new Color(cartesian.x, cartesian.y, cartesian.z, cartesian.w); } result.red = cartesian.x; result.green = cartesian.y; result.blue = cartesian.z; result.alpha = cartesian.w; return result; }; /** * 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. * * @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. * @param {Color} [result] The object onto which to store the result. * @returns {Color} The modified result parameter or a new Color instance if one was not provided. */ Color.fromBytes = function (red, green, blue, alpha, result) { 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)); if (!defined(result)) { return new Color(red, green, blue, alpha); } result.red = red; result.green = green; result.blue = blue; result.alpha = alpha; return result; }; /** * Creates a new Color that has the same red, green, and blue components * of the specified color, but with the specified alpha value. * * @param {Color} color The base color * @param {Number} alpha The new alpha component. * @param {Color} [result] The object onto which to store the result. * @returns {Color} The modified result parameter or a new Color instance if one was not provided. * * @example var translucentRed = Cesium.Color.fromAlpha(Cesium.Color.RED, 0.9); */ Color.fromAlpha = function (color, alpha, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("color", color); Check.typeOf.number("alpha", alpha); //>>includeEnd('debug'); if (!defined(result)) { return new Color(color.red, color.green, color.blue, alpha); } result.red = color.red; result.green = color.green; result.blue = color.blue; result.alpha = alpha; return result; }; 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. * * @param {Number} rgba A single numeric unsigned 32-bit RGBA value. * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. * @returns {Color} The color object. * * @example * var color = Cesium.Color.fromRgba(0x67ADDFFF); * * @see Color#toRgba */ Color.fromRgba = function (rgba, result) { // scratchUint32Array and scratchUint8Array share an underlying array buffer scratchUint32Array[0] = rgba; return Color.fromBytes( scratchUint8Array[0], scratchUint8Array[1], scratchUint8Array[2], scratchUint8Array[3], result ); }; /** * Creates a Color instance from hue, saturation, and lightness. * * @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 * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. * @returns {Color} The color object. * * @see {@link http://www.w3.org/TR/css3-color/#hsl-color|CSS color values} */ Color.fromHsl = function (hue, saturation, lightness, alpha, result) { 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); } if (!defined(result)) { return new Color(red, green, blue, alpha); } result.red = red; result.green = green; result.blue = blue; result.alpha = alpha; return result; }; /** * 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. * * @param {Object} [options] Object with the following properties: * @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 = Cesium.Color.fromRandom(); * * //Create a random shade of yellow. * var color = Cesium.Color.fromRandom({ * red : 1.0, * green : 1.0, * alpha : 1.0 * }); * * //Create a random bright color. * var color = Cesium.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); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThanOrEquals("minimumRed", minimumRed, maximumRed); //>>includeEnd('debug'); 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); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThanOrEquals( "minimumGreen", minimumGreen, maximumGreen ); //>>includeEnd('debug'); 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); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThanOrEquals( "minimumBlue", minimumBlue, maximumBlue ); //>>includeEnd('debug'); 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); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThanOrEquals( "minumumAlpha", minimumAlpha, maximumAlpha ); //>>includeEnd('debug'); 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; }; //#rgba var rgbaMatcher = /^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i; //#rrggbbaa var rrggbbaaMatcher = /^#([0-9a-f]{2})([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() or hsla() 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. * * @param {String} color The CSS color value in #rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl(), or hsla() format. * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. * @returns {Color} The color object, or undefined if the string was not a valid CSS color. * * * @example * var cesiumBlue = Cesium.Color.fromCssColorString('#67ADDF'); * var green = Cesium.Color.fromCssColorString('green'); * * @see {@link http://www.w3.org/TR/css3-color|CSS color values} */ Color.fromCssColorString = function (color, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("color", color); //>>includeEnd('debug'); if (!defined(result)) { result = new Color(); } // Remove all whitespaces from the color string color = color.replace(/\s/g, ""); var namedColor = Color[color.toUpperCase()]; if (defined(namedColor)) { Color.clone(namedColor, result); return result; } var matches = rgbaMatcher.exec(color); if (matches !== null) { result.red = parseInt(matches[1], 16) / 15; result.green = parseInt(matches[2], 16) / 15.0; result.blue = parseInt(matches[3], 16) / 15.0; result.alpha = parseInt(defaultValue(matches[4], "f"), 16) / 15.0; return result; } matches = rrggbbaaMatcher.exec(color); if (matches !== null) { result.red = parseInt(matches[1], 16) / 255.0; result.green = parseInt(matches[2], 16) / 255.0; result.blue = parseInt(matches[3], 16) / 255.0; result.alpha = parseInt(defaultValue(matches[4], "ff"), 16) / 255.0; return result; } matches = rgbParenthesesMatcher.exec(color); if (matches !== null) { result.red = parseFloat(matches[1]) / ("%" === matches[1].substr(-1) ? 100.0 : 255.0); result.green = parseFloat(matches[2]) / ("%" === matches[2].substr(-1) ? 100.0 : 255.0); result.blue = parseFloat(matches[3]) / ("%" === matches[3].substr(-1) ? 100.0 : 255.0); result.alpha = parseFloat(defaultValue(matches[4], "1.0")); return result; } 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")), result ); } result = undefined; return result; }; /** * 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. * * @param {Color} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ Color.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.red; array[startingIndex++] = value.green; array[startingIndex++] = value.blue; array[startingIndex] = value.alpha; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} 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. * @returns {Color} The modified result parameter or a new Color instance if one was not provided. */ Color.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); 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. * * @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. * * @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. * * @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. * * @param {Color} left The first Color to compare for equality. * @param {Color} right The second Color to compare for equality. * @returns {Boolean} true if the Colors are equal; otherwise, false. */ 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) ); }; /** * @private */ Color.equalsArray = function (color, array, offset) { return ( color.red === array[offset] && color.green === array[offset + 1] && color.blue === array[offset + 2] && color.alpha === array[offset + 3] ); }; /** * Returns a duplicate of a Color instance. * * @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. * * @param {Color} other The Color to compare for equality. * @returns {Boolean} true if the Colors are equal; otherwise, false. */ Color.prototype.equals = function (other) { return Color.equals(this, other); }; /** * Returns true if this Color equals other componentwise within the specified epsilon. * * @param {Color} other The Color to compare for equality. * @param {Number} [epsilon=0.0] The epsilon to use for equality testing. * @returns {Boolean} true if the Colors are equal within the specified epsilon; otherwise, false. */ 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)'. * * @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. * * @returns {String} The CSS equivalent of this color. * * @see {@link http://www.w3.org/TR/css3-color/#rgba-color|CSS RGB or RGBA color values} */ 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 + ")"; }; /** * Creates a string containing CSS hex string color value for this color. * * @returns {String} The CSS hex string equivalent of this color. */ Color.prototype.toCssHexString = function () { var r = Color.floatToByte(this.red).toString(16); if (r.length < 2) { r = "0" + r; } var g = Color.floatToByte(this.green).toString(16); if (g.length < 2) { g = "0" + g; } var b = Color.floatToByte(this.blue).toString(16); if (b.length < 2) { b = "0" + b; } if (this.alpha < 1) { var hexAlpha = Color.floatToByte(this.alpha).toString(16); if (hexAlpha.length < 2) { hexAlpha = "0" + hexAlpha; } return "#" + r + g + b + hexAlpha; } return "#" + r + g + b; }; /** * Converts this color to an array of red, green, blue, and alpha values * that are in the range of 0 to 255. * * @param {Number[]} [result] The array to store the result in, if undefined a new instance will be created. * @returns {Number[]} 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. * * @returns {Number} A single numeric unsigned 32-bit RGBA value. * * * @example * var rgba = Cesium.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]; }; /** * Brightens this color by the provided magnitude. * * @param {Number} magnitude A positive number indicating the amount to brighten. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. * * @example * var brightBlue = Cesium.Color.BLUE.brighten(0.5, new Cesium.Color()); */ Color.prototype.brighten = function (magnitude, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("magnitude", magnitude); Check.typeOf.number.greaterThanOrEquals("magnitude", magnitude, 0.0); Check.typeOf.object("result", result); //>>includeEnd('debug'); magnitude = 1.0 - magnitude; result.red = 1.0 - (1.0 - this.red) * magnitude; result.green = 1.0 - (1.0 - this.green) * magnitude; result.blue = 1.0 - (1.0 - this.blue) * magnitude; result.alpha = this.alpha; return result; }; /** * Darkens this color by the provided magnitude. * * @param {Number} magnitude A positive number indicating the amount to darken. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. * * @example * var darkBlue = Cesium.Color.BLUE.darken(0.5, new Cesium.Color()); */ Color.prototype.darken = function (magnitude, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("magnitude", magnitude); Check.typeOf.number.greaterThanOrEquals("magnitude", magnitude, 0.0); Check.typeOf.object("result", result); //>>includeEnd('debug'); magnitude = 1.0 - magnitude; result.red = this.red * magnitude; result.green = this.green * magnitude; result.blue = this.blue * magnitude; result.alpha = this.alpha; return result; }; /** * Creates a new Color that has the same red, green, and blue components * as this Color, but with the specified alpha value. * * @param {Number} alpha The new alpha component. * @param {Color} [result] The object onto which to store the result. * @returns {Color} The modified result parameter or a new Color instance if one was not provided. * * @example var translucentRed = Cesium.Color.RED.withAlpha(0.9); */ Color.prototype.withAlpha = function (alpha, result) { return Color.fromAlpha(this, alpha, result); }; /** * Computes the componentwise sum of two Colors. * * @param {Color} left The first Color. * @param {Color} right The second Color. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.add = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = left.red + right.red; result.green = left.green + right.green; result.blue = left.blue + right.blue; result.alpha = left.alpha + right.alpha; return result; }; /** * Computes the componentwise difference of two Colors. * * @param {Color} left The first Color. * @param {Color} right The second Color. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.subtract = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = left.red - right.red; result.green = left.green - right.green; result.blue = left.blue - right.blue; result.alpha = left.alpha - right.alpha; return result; }; /** * Computes the componentwise product of two Colors. * * @param {Color} left The first Color. * @param {Color} right The second Color. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.multiply = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = left.red * right.red; result.green = left.green * right.green; result.blue = left.blue * right.blue; result.alpha = left.alpha * right.alpha; return result; }; /** * Computes the componentwise quotient of two Colors. * * @param {Color} left The first Color. * @param {Color} right The second Color. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.divide = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = left.red / right.red; result.green = left.green / right.green; result.blue = left.blue / right.blue; result.alpha = left.alpha / right.alpha; return result; }; /** * Computes the componentwise modulus of two Colors. * * @param {Color} left The first Color. * @param {Color} right The second Color. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.mod = function (left, right, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); Check.typeOf.object("right", right); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = left.red % right.red; result.green = left.green % right.green; result.blue = left.blue % right.blue; result.alpha = left.alpha % right.alpha; return result; }; /** * Computes the linear interpolation or extrapolation at t between the provided colors. * * @param {Color} start The color corresponding to t at 0.0. * @param {Color} end The color corresponding to t at 1.0. * @param {Number} t The point along t at which to interpolate. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.lerp = function (start, end, t, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("start", start); Check.typeOf.object("end", end); Check.typeOf.number("t", t); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = CesiumMath.lerp(start.red, end.red, t); result.green = CesiumMath.lerp(start.green, end.green, t); result.blue = CesiumMath.lerp(start.blue, end.blue, t); result.alpha = CesiumMath.lerp(start.alpha, end.alpha, t); return result; }; /** * Multiplies the provided Color componentwise by the provided scalar. * * @param {Color} color The Color to be scaled. * @param {Number} scalar The scalar to multiply with. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.multiplyByScalar = function (color, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("color", color); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = color.red * scalar; result.green = color.green * scalar; result.blue = color.blue * scalar; result.alpha = color.alpha * scalar; return result; }; /** * Divides the provided Color componentwise by the provided scalar. * * @param {Color} color The Color to be divided. * @param {Number} scalar The scalar to divide with. * @param {Color} result The object onto which to store the result. * @returns {Color} The modified result parameter. */ Color.divideByScalar = function (color, scalar, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("color", color); Check.typeOf.number("scalar", scalar); Check.typeOf.object("result", result); //>>includeEnd('debug'); result.red = color.red / scalar; result.green = color.green / scalar; result.blue = color.blue / scalar; result.alpha = color.alpha / scalar; return result; }; /** * An immutable Color instance initialized to CSS color #F0F8FF * * * @constant * @type {Color} */ Color.ALICEBLUE = Object.freeze(Color.fromCssColorString("#F0F8FF")); /** * An immutable Color instance initialized to CSS color #FAEBD7 * * * @constant * @type {Color} */ Color.ANTIQUEWHITE = Object.freeze(Color.fromCssColorString("#FAEBD7")); /** * An immutable Color instance initialized to CSS color #00FFFF * * * @constant * @type {Color} */ Color.AQUA = Object.freeze(Color.fromCssColorString("#00FFFF")); /** * An immutable Color instance initialized to CSS color #7FFFD4 * * * @constant * @type {Color} */ Color.AQUAMARINE = Object.freeze(Color.fromCssColorString("#7FFFD4")); /** * An immutable Color instance initialized to CSS color #F0FFFF * * * @constant * @type {Color} */ Color.AZURE = Object.freeze(Color.fromCssColorString("#F0FFFF")); /** * An immutable Color instance initialized to CSS color #F5F5DC * * * @constant * @type {Color} */ Color.BEIGE = Object.freeze(Color.fromCssColorString("#F5F5DC")); /** * An immutable Color instance initialized to CSS color #FFE4C4 * * * @constant * @type {Color} */ Color.BISQUE = Object.freeze(Color.fromCssColorString("#FFE4C4")); /** * An immutable Color instance initialized to CSS color #000000 * * * @constant * @type {Color} */ Color.BLACK = Object.freeze(Color.fromCssColorString("#000000")); /** * An immutable Color instance initialized to CSS color #FFEBCD * * * @constant * @type {Color} */ Color.BLANCHEDALMOND = Object.freeze(Color.fromCssColorString("#FFEBCD")); /** * An immutable Color instance initialized to CSS color #0000FF * * * @constant * @type {Color} */ Color.BLUE = Object.freeze(Color.fromCssColorString("#0000FF")); /** * An immutable Color instance initialized to CSS color #8A2BE2 * * * @constant * @type {Color} */ Color.BLUEVIOLET = Object.freeze(Color.fromCssColorString("#8A2BE2")); /** * An immutable Color instance initialized to CSS color #A52A2A * * * @constant * @type {Color} */ Color.BROWN = Object.freeze(Color.fromCssColorString("#A52A2A")); /** * An immutable Color instance initialized to CSS color #DEB887 * * * @constant * @type {Color} */ Color.BURLYWOOD = Object.freeze(Color.fromCssColorString("#DEB887")); /** * An immutable Color instance initialized to CSS color #5F9EA0 * * * @constant * @type {Color} */ Color.CADETBLUE = Object.freeze(Color.fromCssColorString("#5F9EA0")); /** * An immutable Color instance initialized to CSS color #7FFF00 * * * @constant * @type {Color} */ Color.CHARTREUSE = Object.freeze(Color.fromCssColorString("#7FFF00")); /** * An immutable Color instance initialized to CSS color #D2691E * * * @constant * @type {Color} */ Color.CHOCOLATE = Object.freeze(Color.fromCssColorString("#D2691E")); /** * An immutable Color instance initialized to CSS color #FF7F50 * * * @constant * @type {Color} */ Color.CORAL = Object.freeze(Color.fromCssColorString("#FF7F50")); /** * An immutable Color instance initialized to CSS color #6495ED * * * @constant * @type {Color} */ Color.CORNFLOWERBLUE = Object.freeze(Color.fromCssColorString("#6495ED")); /** * An immutable Color instance initialized to CSS color #FFF8DC * * * @constant * @type {Color} */ Color.CORNSILK = Object.freeze(Color.fromCssColorString("#FFF8DC")); /** * An immutable Color instance initialized to CSS color #DC143C * * * @constant * @type {Color} */ Color.CRIMSON = Object.freeze(Color.fromCssColorString("#DC143C")); /** * An immutable Color instance initialized to CSS color #00FFFF * * * @constant * @type {Color} */ Color.CYAN = Object.freeze(Color.fromCssColorString("#00FFFF")); /** * An immutable Color instance initialized to CSS color #00008B * * * @constant * @type {Color} */ Color.DARKBLUE = Object.freeze(Color.fromCssColorString("#00008B")); /** * An immutable Color instance initialized to CSS color #008B8B * * * @constant * @type {Color} */ Color.DARKCYAN = Object.freeze(Color.fromCssColorString("#008B8B")); /** * An immutable Color instance initialized to CSS color #B8860B * * * @constant * @type {Color} */ Color.DARKGOLDENROD = Object.freeze(Color.fromCssColorString("#B8860B")); /** * An immutable Color instance initialized to CSS color #A9A9A9 * * * @constant * @type {Color} */ Color.DARKGRAY = Object.freeze(Color.fromCssColorString("#A9A9A9")); /** * An immutable Color instance initialized to CSS color #006400 * * * @constant * @type {Color} */ Color.DARKGREEN = Object.freeze(Color.fromCssColorString("#006400")); /** * An immutable Color instance initialized to CSS color #A9A9A9 * * * @constant * @type {Color} */ Color.DARKGREY = Color.DARKGRAY; /** * An immutable Color instance initialized to CSS color #BDB76B * * * @constant * @type {Color} */ Color.DARKKHAKI = Object.freeze(Color.fromCssColorString("#BDB76B")); /** * An immutable Color instance initialized to CSS color #8B008B * * * @constant * @type {Color} */ Color.DARKMAGENTA = Object.freeze(Color.fromCssColorString("#8B008B")); /** * An immutable Color instance initialized to CSS color #556B2F * * * @constant * @type {Color} */ Color.DARKOLIVEGREEN = Object.freeze(Color.fromCssColorString("#556B2F")); /** * An immutable Color instance initialized to CSS color #FF8C00 * * * @constant * @type {Color} */ Color.DARKORANGE = Object.freeze(Color.fromCssColorString("#FF8C00")); /** * An immutable Color instance initialized to CSS color #9932CC * * * @constant * @type {Color} */ Color.DARKORCHID = Object.freeze(Color.fromCssColorString("#9932CC")); /** * An immutable Color instance initialized to CSS color #8B0000 * * * @constant * @type {Color} */ Color.DARKRED = Object.freeze(Color.fromCssColorString("#8B0000")); /** * An immutable Color instance initialized to CSS color #E9967A * * * @constant * @type {Color} */ Color.DARKSALMON = Object.freeze(Color.fromCssColorString("#E9967A")); /** * An immutable Color instance initialized to CSS color #8FBC8F * * * @constant * @type {Color} */ Color.DARKSEAGREEN = Object.freeze(Color.fromCssColorString("#8FBC8F")); /** * An immutable Color instance initialized to CSS color #483D8B * * * @constant * @type {Color} */ Color.DARKSLATEBLUE = Object.freeze(Color.fromCssColorString("#483D8B")); /** * An immutable Color instance initialized to CSS color #2F4F4F * * * @constant * @type {Color} */ Color.DARKSLATEGRAY = Object.freeze(Color.fromCssColorString("#2F4F4F")); /** * An immutable Color instance initialized to CSS color #2F4F4F * * * @constant * @type {Color} */ Color.DARKSLATEGREY = Color.DARKSLATEGRAY; /** * An immutable Color instance initialized to CSS color #00CED1 * * * @constant * @type {Color} */ Color.DARKTURQUOISE = Object.freeze(Color.fromCssColorString("#00CED1")); /** * An immutable Color instance initialized to CSS color #9400D3 * * * @constant * @type {Color} */ Color.DARKVIOLET = Object.freeze(Color.fromCssColorString("#9400D3")); /** * An immutable Color instance initialized to CSS color #FF1493 * * * @constant * @type {Color} */ Color.DEEPPINK = Object.freeze(Color.fromCssColorString("#FF1493")); /** * An immutable Color instance initialized to CSS color #00BFFF * * * @constant * @type {Color} */ Color.DEEPSKYBLUE = Object.freeze(Color.fromCssColorString("#00BFFF")); /** * An immutable Color instance initialized to CSS color #696969 * * * @constant * @type {Color} */ Color.DIMGRAY = Object.freeze(Color.fromCssColorString("#696969")); /** * An immutable Color instance initialized to CSS color #696969 * * * @constant * @type {Color} */ Color.DIMGREY = Color.DIMGRAY; /** * An immutable Color instance initialized to CSS color #1E90FF * * * @constant * @type {Color} */ Color.DODGERBLUE = Object.freeze(Color.fromCssColorString("#1E90FF")); /** * An immutable Color instance initialized to CSS color #B22222 * * * @constant * @type {Color} */ Color.FIREBRICK = Object.freeze(Color.fromCssColorString("#B22222")); /** * An immutable Color instance initialized to CSS color #FFFAF0 * * * @constant * @type {Color} */ Color.FLORALWHITE = Object.freeze(Color.fromCssColorString("#FFFAF0")); /** * An immutable Color instance initialized to CSS color #228B22 * * * @constant * @type {Color} */ Color.FORESTGREEN = Object.freeze(Color.fromCssColorString("#228B22")); /** * An immutable Color instance initialized to CSS color #FF00FF * * * @constant * @type {Color} */ Color.FUCHSIA = Object.freeze(Color.fromCssColorString("#FF00FF")); /** * An immutable Color instance initialized to CSS color #DCDCDC * * * @constant * @type {Color} */ Color.GAINSBORO = Object.freeze(Color.fromCssColorString("#DCDCDC")); /** * An immutable Color instance initialized to CSS color #F8F8FF * * * @constant * @type {Color} */ Color.GHOSTWHITE = Object.freeze(Color.fromCssColorString("#F8F8FF")); /** * An immutable Color instance initialized to CSS color #FFD700 * * * @constant * @type {Color} */ Color.GOLD = Object.freeze(Color.fromCssColorString("#FFD700")); /** * An immutable Color instance initialized to CSS color #DAA520 * * * @constant * @type {Color} */ Color.GOLDENROD = Object.freeze(Color.fromCssColorString("#DAA520")); /** * An immutable Color instance initialized to CSS color #808080 * * * @constant * @type {Color} */ Color.GRAY = Object.freeze(Color.fromCssColorString("#808080")); /** * An immutable Color instance initialized to CSS color #008000 * * * @constant * @type {Color} */ Color.GREEN = Object.freeze(Color.fromCssColorString("#008000")); /** * An immutable Color instance initialized to CSS color #ADFF2F * * * @constant * @type {Color} */ Color.GREENYELLOW = Object.freeze(Color.fromCssColorString("#ADFF2F")); /** * An immutable Color instance initialized to CSS color #808080 * * * @constant * @type {Color} */ Color.GREY = Color.GRAY; /** * An immutable Color instance initialized to CSS color #F0FFF0 * * * @constant * @type {Color} */ Color.HONEYDEW = Object.freeze(Color.fromCssColorString("#F0FFF0")); /** * An immutable Color instance initialized to CSS color #FF69B4 * * * @constant * @type {Color} */ Color.HOTPINK = Object.freeze(Color.fromCssColorString("#FF69B4")); /** * An immutable Color instance initialized to CSS color #CD5C5C * * * @constant * @type {Color} */ Color.INDIANRED = Object.freeze(Color.fromCssColorString("#CD5C5C")); /** * An immutable Color instance initialized to CSS color #4B0082 * * * @constant * @type {Color} */ Color.INDIGO = Object.freeze(Color.fromCssColorString("#4B0082")); /** * An immutable Color instance initialized to CSS color #FFFFF0 * * * @constant * @type {Color} */ Color.IVORY = Object.freeze(Color.fromCssColorString("#FFFFF0")); /** * An immutable Color instance initialized to CSS color #F0E68C * * * @constant * @type {Color} */ Color.KHAKI = Object.freeze(Color.fromCssColorString("#F0E68C")); /** * An immutable Color instance initialized to CSS color #E6E6FA * * * @constant * @type {Color} */ Color.LAVENDER = Object.freeze(Color.fromCssColorString("#E6E6FA")); /** * An immutable Color instance initialized to CSS color #FFF0F5 * * * @constant * @type {Color} */ Color.LAVENDAR_BLUSH = Object.freeze(Color.fromCssColorString("#FFF0F5")); /** * An immutable Color instance initialized to CSS color #7CFC00 * * * @constant * @type {Color} */ Color.LAWNGREEN = Object.freeze(Color.fromCssColorString("#7CFC00")); /** * An immutable Color instance initialized to CSS color #FFFACD * * * @constant * @type {Color} */ Color.LEMONCHIFFON = Object.freeze(Color.fromCssColorString("#FFFACD")); /** * An immutable Color instance initialized to CSS color #ADD8E6 * * * @constant * @type {Color} */ Color.LIGHTBLUE = Object.freeze(Color.fromCssColorString("#ADD8E6")); /** * An immutable Color instance initialized to CSS color #F08080 * * * @constant * @type {Color} */ Color.LIGHTCORAL = Object.freeze(Color.fromCssColorString("#F08080")); /** * An immutable Color instance initialized to CSS color #E0FFFF * * * @constant * @type {Color} */ Color.LIGHTCYAN = Object.freeze(Color.fromCssColorString("#E0FFFF")); /** * An immutable Color instance initialized to CSS color #FAFAD2 * * * @constant * @type {Color} */ Color.LIGHTGOLDENRODYELLOW = Object.freeze(Color.fromCssColorString("#FAFAD2")); /** * An immutable Color instance initialized to CSS color #D3D3D3 * * * @constant * @type {Color} */ Color.LIGHTGRAY = Object.freeze(Color.fromCssColorString("#D3D3D3")); /** * An immutable Color instance initialized to CSS color #90EE90 * * * @constant * @type {Color} */ Color.LIGHTGREEN = Object.freeze(Color.fromCssColorString("#90EE90")); /** * An immutable Color instance initialized to CSS color #D3D3D3 * * * @constant * @type {Color} */ Color.LIGHTGREY = Color.LIGHTGRAY; /** * An immutable Color instance initialized to CSS color #FFB6C1 * * * @constant * @type {Color} */ Color.LIGHTPINK = Object.freeze(Color.fromCssColorString("#FFB6C1")); /** * An immutable Color instance initialized to CSS color #20B2AA * * * @constant * @type {Color} */ Color.LIGHTSEAGREEN = Object.freeze(Color.fromCssColorString("#20B2AA")); /** * An immutable Color instance initialized to CSS color #87CEFA * * * @constant * @type {Color} */ Color.LIGHTSKYBLUE = Object.freeze(Color.fromCssColorString("#87CEFA")); /** * An immutable Color instance initialized to CSS color #778899 * * * @constant * @type {Color} */ Color.LIGHTSLATEGRAY = Object.freeze(Color.fromCssColorString("#778899")); /** * An immutable Color instance initialized to CSS color #778899 * * * @constant * @type {Color} */ Color.LIGHTSLATEGREY = Color.LIGHTSLATEGRAY; /** * An immutable Color instance initialized to CSS color #B0C4DE * * * @constant * @type {Color} */ Color.LIGHTSTEELBLUE = Object.freeze(Color.fromCssColorString("#B0C4DE")); /** * An immutable Color instance initialized to CSS color #FFFFE0 * * * @constant * @type {Color} */ Color.LIGHTYELLOW = Object.freeze(Color.fromCssColorString("#FFFFE0")); /** * An immutable Color instance initialized to CSS color #00FF00 * * * @constant * @type {Color} */ Color.LIME = Object.freeze(Color.fromCssColorString("#00FF00")); /** * An immutable Color instance initialized to CSS color #32CD32 * * * @constant * @type {Color} */ Color.LIMEGREEN = Object.freeze(Color.fromCssColorString("#32CD32")); /** * An immutable Color instance initialized to CSS color #FAF0E6 * * * @constant * @type {Color} */ Color.LINEN = Object.freeze(Color.fromCssColorString("#FAF0E6")); /** * An immutable Color instance initialized to CSS color #FF00FF * * * @constant * @type {Color} */ Color.MAGENTA = Object.freeze(Color.fromCssColorString("#FF00FF")); /** * An immutable Color instance initialized to CSS color #800000 * * * @constant * @type {Color} */ Color.MAROON = Object.freeze(Color.fromCssColorString("#800000")); /** * An immutable Color instance initialized to CSS color #66CDAA * * * @constant * @type {Color} */ Color.MEDIUMAQUAMARINE = Object.freeze(Color.fromCssColorString("#66CDAA")); /** * An immutable Color instance initialized to CSS color #0000CD * * * @constant * @type {Color} */ Color.MEDIUMBLUE = Object.freeze(Color.fromCssColorString("#0000CD")); /** * An immutable Color instance initialized to CSS color #BA55D3 * * * @constant * @type {Color} */ Color.MEDIUMORCHID = Object.freeze(Color.fromCssColorString("#BA55D3")); /** * An immutable Color instance initialized to CSS color #9370DB * * * @constant * @type {Color} */ Color.MEDIUMPURPLE = Object.freeze(Color.fromCssColorString("#9370DB")); /** * An immutable Color instance initialized to CSS color #3CB371 * * * @constant * @type {Color} */ Color.MEDIUMSEAGREEN = Object.freeze(Color.fromCssColorString("#3CB371")); /** * An immutable Color instance initialized to CSS color #7B68EE * * * @constant * @type {Color} */ Color.MEDIUMSLATEBLUE = Object.freeze(Color.fromCssColorString("#7B68EE")); /** * An immutable Color instance initialized to CSS color #00FA9A * * * @constant * @type {Color} */ Color.MEDIUMSPRINGGREEN = Object.freeze(Color.fromCssColorString("#00FA9A")); /** * An immutable Color instance initialized to CSS color #48D1CC * * * @constant * @type {Color} */ Color.MEDIUMTURQUOISE = Object.freeze(Color.fromCssColorString("#48D1CC")); /** * An immutable Color instance initialized to CSS color #C71585 * * * @constant * @type {Color} */ Color.MEDIUMVIOLETRED = Object.freeze(Color.fromCssColorString("#C71585")); /** * An immutable Color instance initialized to CSS color #191970 * * * @constant * @type {Color} */ Color.MIDNIGHTBLUE = Object.freeze(Color.fromCssColorString("#191970")); /** * An immutable Color instance initialized to CSS color #F5FFFA * * * @constant * @type {Color} */ Color.MINTCREAM = Object.freeze(Color.fromCssColorString("#F5FFFA")); /** * An immutable Color instance initialized to CSS color #FFE4E1 * * * @constant * @type {Color} */ Color.MISTYROSE = Object.freeze(Color.fromCssColorString("#FFE4E1")); /** * An immutable Color instance initialized to CSS color #FFE4B5 * * * @constant * @type {Color} */ Color.MOCCASIN = Object.freeze(Color.fromCssColorString("#FFE4B5")); /** * An immutable Color instance initialized to CSS color #FFDEAD * * * @constant * @type {Color} */ Color.NAVAJOWHITE = Object.freeze(Color.fromCssColorString("#FFDEAD")); /** * An immutable Color instance initialized to CSS color #000080 * * * @constant * @type {Color} */ Color.NAVY = Object.freeze(Color.fromCssColorString("#000080")); /** * An immutable Color instance initialized to CSS color #FDF5E6 * * * @constant * @type {Color} */ Color.OLDLACE = Object.freeze(Color.fromCssColorString("#FDF5E6")); /** * An immutable Color instance initialized to CSS color #808000 * * * @constant * @type {Color} */ Color.OLIVE = Object.freeze(Color.fromCssColorString("#808000")); /** * An immutable Color instance initialized to CSS color #6B8E23 * * * @constant * @type {Color} */ Color.OLIVEDRAB = Object.freeze(Color.fromCssColorString("#6B8E23")); /** * An immutable Color instance initialized to CSS color #FFA500 * * * @constant * @type {Color} */ Color.ORANGE = Object.freeze(Color.fromCssColorString("#FFA500")); /** * An immutable Color instance initialized to CSS color #FF4500 * * * @constant * @type {Color} */ Color.ORANGERED = Object.freeze(Color.fromCssColorString("#FF4500")); /** * An immutable Color instance initialized to CSS color #DA70D6 * * * @constant * @type {Color} */ Color.ORCHID = Object.freeze(Color.fromCssColorString("#DA70D6")); /** * An immutable Color instance initialized to CSS color #EEE8AA * * * @constant * @type {Color} */ Color.PALEGOLDENROD = Object.freeze(Color.fromCssColorString("#EEE8AA")); /** * An immutable Color instance initialized to CSS color #98FB98 * * * @constant * @type {Color} */ Color.PALEGREEN = Object.freeze(Color.fromCssColorString("#98FB98")); /** * An immutable Color instance initialized to CSS color #AFEEEE * * * @constant * @type {Color} */ Color.PALETURQUOISE = Object.freeze(Color.fromCssColorString("#AFEEEE")); /** * An immutable Color instance initialized to CSS color #DB7093 * * * @constant * @type {Color} */ Color.PALEVIOLETRED = Object.freeze(Color.fromCssColorString("#DB7093")); /** * An immutable Color instance initialized to CSS color #FFEFD5 * * * @constant * @type {Color} */ Color.PAPAYAWHIP = Object.freeze(Color.fromCssColorString("#FFEFD5")); /** * An immutable Color instance initialized to CSS color #FFDAB9 * * * @constant * @type {Color} */ Color.PEACHPUFF = Object.freeze(Color.fromCssColorString("#FFDAB9")); /** * An immutable Color instance initialized to CSS color #CD853F * * * @constant * @type {Color} */ Color.PERU = Object.freeze(Color.fromCssColorString("#CD853F")); /** * An immutable Color instance initialized to CSS color #FFC0CB * * * @constant * @type {Color} */ Color.PINK = Object.freeze(Color.fromCssColorString("#FFC0CB")); /** * An immutable Color instance initialized to CSS color #DDA0DD * * * @constant * @type {Color} */ Color.PLUM = Object.freeze(Color.fromCssColorString("#DDA0DD")); /** * An immutable Color instance initialized to CSS color #B0E0E6 * * * @constant * @type {Color} */ Color.POWDERBLUE = Object.freeze(Color.fromCssColorString("#B0E0E6")); /** * An immutable Color instance initialized to CSS color #800080 * * * @constant * @type {Color} */ Color.PURPLE = Object.freeze(Color.fromCssColorString("#800080")); /** * An immutable Color instance initialized to CSS color #FF0000 * * * @constant * @type {Color} */ Color.RED = Object.freeze(Color.fromCssColorString("#FF0000")); /** * An immutable Color instance initialized to CSS color #BC8F8F * * * @constant * @type {Color} */ Color.ROSYBROWN = Object.freeze(Color.fromCssColorString("#BC8F8F")); /** * An immutable Color instance initialized to CSS color #4169E1 * * * @constant * @type {Color} */ Color.ROYALBLUE = Object.freeze(Color.fromCssColorString("#4169E1")); /** * An immutable Color instance initialized to CSS color #8B4513 * * * @constant * @type {Color} */ Color.SADDLEBROWN = Object.freeze(Color.fromCssColorString("#8B4513")); /** * An immutable Color instance initialized to CSS color #FA8072 * * * @constant * @type {Color} */ Color.SALMON = Object.freeze(Color.fromCssColorString("#FA8072")); /** * An immutable Color instance initialized to CSS color #F4A460 * * * @constant * @type {Color} */ Color.SANDYBROWN = Object.freeze(Color.fromCssColorString("#F4A460")); /** * An immutable Color instance initialized to CSS color #2E8B57 * * * @constant * @type {Color} */ Color.SEAGREEN = Object.freeze(Color.fromCssColorString("#2E8B57")); /** * An immutable Color instance initialized to CSS color #FFF5EE * * * @constant * @type {Color} */ Color.SEASHELL = Object.freeze(Color.fromCssColorString("#FFF5EE")); /** * An immutable Color instance initialized to CSS color #A0522D * * * @constant * @type {Color} */ Color.SIENNA = Object.freeze(Color.fromCssColorString("#A0522D")); /** * An immutable Color instance initialized to CSS color #C0C0C0 * * * @constant * @type {Color} */ Color.SILVER = Object.freeze(Color.fromCssColorString("#C0C0C0")); /** * An immutable Color instance initialized to CSS color #87CEEB * * * @constant * @type {Color} */ Color.SKYBLUE = Object.freeze(Color.fromCssColorString("#87CEEB")); /** * An immutable Color instance initialized to CSS color #6A5ACD * * * @constant * @type {Color} */ Color.SLATEBLUE = Object.freeze(Color.fromCssColorString("#6A5ACD")); /** * An immutable Color instance initialized to CSS color #708090 * * * @constant * @type {Color} */ Color.SLATEGRAY = Object.freeze(Color.fromCssColorString("#708090")); /** * An immutable Color instance initialized to CSS color #708090 * * * @constant * @type {Color} */ Color.SLATEGREY = Color.SLATEGRAY; /** * An immutable Color instance initialized to CSS color #FFFAFA * * * @constant * @type {Color} */ Color.SNOW = Object.freeze(Color.fromCssColorString("#FFFAFA")); /** * An immutable Color instance initialized to CSS color #00FF7F * * * @constant * @type {Color} */ Color.SPRINGGREEN = Object.freeze(Color.fromCssColorString("#00FF7F")); /** * An immutable Color instance initialized to CSS color #4682B4 * * * @constant * @type {Color} */ Color.STEELBLUE = Object.freeze(Color.fromCssColorString("#4682B4")); /** * An immutable Color instance initialized to CSS color #D2B48C * * * @constant * @type {Color} */ Color.TAN = Object.freeze(Color.fromCssColorString("#D2B48C")); /** * An immutable Color instance initialized to CSS color #008080 * * * @constant * @type {Color} */ Color.TEAL = Object.freeze(Color.fromCssColorString("#008080")); /** * An immutable Color instance initialized to CSS color #D8BFD8 * * * @constant * @type {Color} */ Color.THISTLE = Object.freeze(Color.fromCssColorString("#D8BFD8")); /** * An immutable Color instance initialized to CSS color #FF6347 * * * @constant * @type {Color} */ Color.TOMATO = Object.freeze(Color.fromCssColorString("#FF6347")); /** * An immutable Color instance initialized to CSS color #40E0D0 * * * @constant * @type {Color} */ Color.TURQUOISE = Object.freeze(Color.fromCssColorString("#40E0D0")); /** * An immutable Color instance initialized to CSS color #EE82EE * * * @constant * @type {Color} */ Color.VIOLET = Object.freeze(Color.fromCssColorString("#EE82EE")); /** * An immutable Color instance initialized to CSS color #F5DEB3 * * * @constant * @type {Color} */ Color.WHEAT = Object.freeze(Color.fromCssColorString("#F5DEB3")); /** * An immutable Color instance initialized to CSS color #FFFFFF * * * @constant * @type {Color} */ Color.WHITE = Object.freeze(Color.fromCssColorString("#FFFFFF")); /** * An immutable Color instance initialized to CSS color #F5F5F5 * * * @constant * @type {Color} */ Color.WHITESMOKE = Object.freeze(Color.fromCssColorString("#F5F5F5")); /** * An immutable Color instance initialized to CSS color #FFFF00 * * * @constant * @type {Color} */ Color.YELLOW = Object.freeze(Color.fromCssColorString("#FFFF00")); /** * An immutable Color instance initialized to CSS color #9ACD32 * * * @constant * @type {Color} */ Color.YELLOWGREEN = Object.freeze(Color.fromCssColorString("#9ACD32")); /** * An immutable Color instance initialized to CSS transparent. * * * @constant * @type {Color} */ Color.TRANSPARENT = Object.freeze(new Color(0, 0, 0, 0)); /** * Value and type information for per-instance geometry color. * * @alias ColorGeometryInstanceAttribute * @constructor * * @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. * * * @example * var instance = new Cesium.GeometryInstance({ * geometry : Cesium.BoxGeometry.fromDimensions({ * dimensions : new Cesium.Cartesian3(1000000.0, 1000000.0, 500000.0) * }), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(0.0, 0.0)), new Cesium.Cartesian3(0.0, 0.0, 1000000.0), new Cesium.Matrix4()), * id : 'box', * attributes : { * color : new Cesium.ColorGeometryInstanceAttribute(red, green, blue, alpha) * } * }); * * @see GeometryInstance * @see GeometryInstanceAttribute */ function ColorGeometryInstanceAttribute(red, green, blue, alpha) { red = defaultValue(red, 1.0); green = defaultValue(green, 1.0); blue = defaultValue(blue, 1.0); alpha = defaultValue(alpha, 1.0); /** * The values for the attributes stored in a typed array. * * @type Uint8Array * * @default [255, 255, 255, 255] */ this.value = new Uint8Array([ Color.floatToByte(red), Color.floatToByte(green), Color.floatToByte(blue), Color.floatToByte(alpha), ]); } Object.defineProperties(ColorGeometryInstanceAttribute.prototype, { /** * The datatype of each component in the attribute, e.g., individual elements in * {@link ColorGeometryInstanceAttribute#value}. * * @memberof ColorGeometryInstanceAttribute.prototype * * @type {ComponentDatatype} * @readonly * * @default {@link ComponentDatatype.UNSIGNED_BYTE} */ componentDatatype: { get: function () { return ComponentDatatype$1.UNSIGNED_BYTE; }, }, /** * The number of components in the attributes, i.e., {@link ColorGeometryInstanceAttribute#value}. * * @memberof ColorGeometryInstanceAttribute.prototype * * @type {Number} * @readonly * * @default 4 */ componentsPerAttribute: { get: function () { return 4; }, }, /** * When true and componentDatatype 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. * * @memberof ColorGeometryInstanceAttribute.prototype * * @type {Boolean} * @readonly * * @default true */ normalize: { get: function () { return true; }, }, }); /** * Creates a new {@link ColorGeometryInstanceAttribute} instance given the provided {@link Color}. * * @param {Color} color The color. * @returns {ColorGeometryInstanceAttribute} The new {@link ColorGeometryInstanceAttribute} instance. * * @example * var instance = new Cesium.GeometryInstance({ * geometry : geometry, * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.CORNFLOWERBLUE), * } * }); */ ColorGeometryInstanceAttribute.fromColor = function (color) { //>>includeStart('debug', pragmas.debug); if (!defined(color)) { throw new DeveloperError("color is required."); } //>>includeEnd('debug'); return new ColorGeometryInstanceAttribute( color.red, color.green, color.blue, color.alpha ); }; /** * Converts a color to a typed array that can be used to assign a color attribute. * * @param {Color} color The color. * @param {Uint8Array} [result] The array to store the result in, if undefined a new instance will be created. * * @returns {Uint8Array} The modified result parameter or a new instance if result was undefined. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA, attributes.color); */ ColorGeometryInstanceAttribute.toValue = function (color, result) { //>>includeStart('debug', pragmas.debug); if (!defined(color)) { throw new DeveloperError("color is required."); } //>>includeEnd('debug'); if (!defined(result)) { return new Uint8Array(color.toBytes()); } return color.toBytes(result); }; /** * Compares the provided ColorGeometryInstanceAttributes and returns * true if they are equal, false otherwise. * * @param {ColorGeometryInstanceAttribute} [left] The first ColorGeometryInstanceAttribute. * @param {ColorGeometryInstanceAttribute} [right] The second ColorGeometryInstanceAttribute. * @returns {Boolean} true if left and right are equal, false otherwise. */ ColorGeometryInstanceAttribute.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.value[0] === right.value[0] && left.value[1] === right.value[1] && left.value[2] === right.value[2] && left.value[3] === right.value[3]) ); }; /** * Describes a compressed texture and contains a compressed texture buffer. * @alias CompressedTextureBuffer * @constructor * * @param {PixelFormat} internalFormat The pixel format of the compressed texture. * @param {Number} width The width of the texture. * @param {Number} height The height of the texture. * @param {Uint8Array} buffer The compressed texture buffer. */ function CompressedTextureBuffer(internalFormat, width, height, buffer) { this._format = internalFormat; this._width = width; this._height = height; this._buffer = buffer; } Object.defineProperties(CompressedTextureBuffer.prototype, { /** * The format of the compressed texture. * @type PixelFormat * @readonly * @memberof CompressedTextureBuffer.prototype */ internalFormat: { get: function () { return this._format; }, }, /** * The width of the texture. * @type Number * @readonly * @memberof CompressedTextureBuffer.prototype */ width: { get: function () { return this._width; }, }, /** * The height of the texture. * @type Number * @readonly * @memberof CompressedTextureBuffer.prototype */ height: { get: function () { return this._height; }, }, /** * The compressed texture buffer. * @type Uint8Array * @readonly * @memberof CompressedTextureBuffer.prototype */ bufferView: { get: function () { return this._buffer; }, }, }); /** * Creates a shallow clone of a compressed texture buffer. * * @param {CompressedTextureBuffer} object The compressed texture buffer to be cloned. * @return {CompressedTextureBuffer} A shallow clone of the compressed texture buffer. */ CompressedTextureBuffer.clone = function (object) { if (!defined(object)) { return undefined; } return new CompressedTextureBuffer( object._format, object._width, object._height, object._buffer ); }; /** * Creates a shallow clone of this compressed texture buffer. * * @return {CompressedTextureBuffer} A shallow clone of the compressed texture buffer. */ CompressedTextureBuffer.prototype.clone = function () { return CompressedTextureBuffer.clone(this); }; var removeDuplicatesEpsilon = CesiumMath.EPSILON10; /** * Removes adjacent duplicate values in an array of values. * * @param {Array.<*>} [values] The array of values. * @param {Function} equalsEpsilon Function to compare values with an epsilon. Boolean equalsEpsilon(left, right, epsilon). * @param {Boolean} [wrapAround=false] Compare the last value in the array against the first value. * @returns {Array.<*>|undefined} A new array of values with no adjacent duplicate values or the input array if no duplicates were found. * * @example * // Returns [(1.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0), (1.0, 1.0, 1.0)] * var values = [ * new Cesium.Cartesian3(1.0, 1.0, 1.0), * new Cesium.Cartesian3(1.0, 1.0, 1.0), * new Cesium.Cartesian3(2.0, 2.0, 2.0), * new Cesium.Cartesian3(3.0, 3.0, 3.0), * new Cesium.Cartesian3(1.0, 1.0, 1.0)]; * var nonDuplicatevalues = Cesium.PolylinePipeline.removeDuplicates(values, Cartesian3.equalsEpsilon); * * @example * // Returns [(1.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0)] * var values = [ * new Cesium.Cartesian3(1.0, 1.0, 1.0), * new Cesium.Cartesian3(1.0, 1.0, 1.0), * new Cesium.Cartesian3(2.0, 2.0, 2.0), * new Cesium.Cartesian3(3.0, 3.0, 3.0), * new Cesium.Cartesian3(1.0, 1.0, 1.0)]; * var nonDuplicatevalues = Cesium.PolylinePipeline.removeDuplicates(values, Cartesian3.equalsEpsilon, true); * * @private */ function arrayRemoveDuplicates(values, equalsEpsilon, wrapAround) { //>>includeStart('debug', pragmas.debug); Check.defined("equalsEpsilon", equalsEpsilon); //>>includeEnd('debug'); if (!defined(values)) { return undefined; } wrapAround = defaultValue(wrapAround, false); var length = values.length; if (length < 2) { return values; } var i; var v0; var v1; for (i = 1; i < length; ++i) { v0 = values[i - 1]; v1 = values[i]; if (equalsEpsilon(v0, v1, removeDuplicatesEpsilon)) { break; } } if (i === length) { if ( wrapAround && equalsEpsilon( values[0], values[values.length - 1], removeDuplicatesEpsilon ) ) { return values.slice(1); } return values; } var cleanedvalues = values.slice(0, i); for (; i < length; ++i) { // v0 is set by either the previous loop, or the previous clean point. v1 = values[i]; if (!equalsEpsilon(v0, v1, removeDuplicatesEpsilon)) { cleanedvalues.push(v1); v0 = v1; } } if ( wrapAround && cleanedvalues.length > 1 && equalsEpsilon( cleanedvalues[0], cleanedvalues[cleanedvalues.length - 1], removeDuplicatesEpsilon ) ) { cleanedvalues.shift(); } return cleanedvalues; } /** * @private */ var CoplanarPolygonGeometryLibrary = {}; var scratchIntersectionPoint = new Cartesian3(); var scratchXAxis = new Cartesian3(); var scratchYAxis = new Cartesian3(); var scratchZAxis = new Cartesian3(); var obbScratch = new OrientedBoundingBox(); CoplanarPolygonGeometryLibrary.validOutline = function (positions) { //>>includeStart('debug', pragmas.debug); Check.defined("positions", positions); //>>includeEnd('debug'); var orientedBoundingBox = OrientedBoundingBox.fromPoints( positions, obbScratch ); var halfAxes = orientedBoundingBox.halfAxes; var xAxis = Matrix3.getColumn(halfAxes, 0, scratchXAxis); var yAxis = Matrix3.getColumn(halfAxes, 1, scratchYAxis); var zAxis = Matrix3.getColumn(halfAxes, 2, scratchZAxis); var xMag = Cartesian3.magnitude(xAxis); var yMag = Cartesian3.magnitude(yAxis); var zMag = Cartesian3.magnitude(zAxis); // If all the points are on a line return undefined because we can't draw a polygon return !( (xMag === 0 && (yMag === 0 || zMag === 0)) || (yMag === 0 && zMag === 0) ); }; // call after removeDuplicates CoplanarPolygonGeometryLibrary.computeProjectTo2DArguments = function ( positions, centerResult, planeAxis1Result, planeAxis2Result ) { //>>includeStart('debug', pragmas.debug); Check.defined("positions", positions); Check.defined("centerResult", centerResult); Check.defined("planeAxis1Result", planeAxis1Result); Check.defined("planeAxis2Result", planeAxis2Result); //>>includeEnd('debug'); var orientedBoundingBox = OrientedBoundingBox.fromPoints( positions, obbScratch ); var halfAxes = orientedBoundingBox.halfAxes; var xAxis = Matrix3.getColumn(halfAxes, 0, scratchXAxis); var yAxis = Matrix3.getColumn(halfAxes, 1, scratchYAxis); var zAxis = Matrix3.getColumn(halfAxes, 2, scratchZAxis); var xMag = Cartesian3.magnitude(xAxis); var yMag = Cartesian3.magnitude(yAxis); var zMag = Cartesian3.magnitude(zAxis); var min = Math.min(xMag, yMag, zMag); // If all the points are on a line return undefined because we can't draw a polygon if ( (xMag === 0 && (yMag === 0 || zMag === 0)) || (yMag === 0 && zMag === 0) ) { return false; } var planeAxis1; var planeAxis2; if (min === yMag || min === zMag) { planeAxis1 = xAxis; } if (min === xMag) { planeAxis1 = yAxis; } else if (min === zMag) { planeAxis2 = yAxis; } if (min === xMag || min === yMag) { planeAxis2 = zAxis; } Cartesian3.normalize(planeAxis1, planeAxis1Result); Cartesian3.normalize(planeAxis2, planeAxis2Result); Cartesian3.clone(orientedBoundingBox.center, centerResult); return true; }; function projectTo2D(position, center, axis1, axis2, result) { var v = Cartesian3.subtract(position, center, scratchIntersectionPoint); var x = Cartesian3.dot(axis1, v); var y = Cartesian3.dot(axis2, v); return Cartesian2.fromElements(x, y, result); } CoplanarPolygonGeometryLibrary.createProjectPointsTo2DFunction = function ( center, axis1, axis2 ) { return function (positions) { var positionResults = new Array(positions.length); for (var i = 0; i < positions.length; i++) { positionResults[i] = projectTo2D(positions[i], center, axis1, axis2); } return positionResults; }; }; CoplanarPolygonGeometryLibrary.createProjectPointTo2DFunction = function ( center, axis1, axis2 ) { return function (position, result) { return projectTo2D(position, center, axis1, axis2, result); }; }; function calculateM(ellipticity, major, latitude) { if (ellipticity === 0.0) { // sphere return major * latitude; } var e2 = ellipticity * ellipticity; var e4 = e2 * e2; var e6 = e4 * e2; var e8 = e6 * e2; var e10 = e8 * e2; var e12 = e10 * e2; var phi = latitude; var sin2Phi = Math.sin(2 * phi); var sin4Phi = Math.sin(4 * phi); var sin6Phi = Math.sin(6 * phi); var sin8Phi = Math.sin(8 * phi); var sin10Phi = Math.sin(10 * phi); var sin12Phi = Math.sin(12 * phi); return ( major * ((1 - e2 / 4 - (3 * e4) / 64 - (5 * e6) / 256 - (175 * e8) / 16384 - (441 * e10) / 65536 - (4851 * e12) / 1048576) * phi - ((3 * e2) / 8 + (3 * e4) / 32 + (45 * e6) / 1024 + (105 * e8) / 4096 + (2205 * e10) / 131072 + (6237 * e12) / 524288) * sin2Phi + ((15 * e4) / 256 + (45 * e6) / 1024 + (525 * e8) / 16384 + (1575 * e10) / 65536 + (155925 * e12) / 8388608) * sin4Phi - ((35 * e6) / 3072 + (175 * e8) / 12288 + (3675 * e10) / 262144 + (13475 * e12) / 1048576) * sin6Phi + ((315 * e8) / 131072 + (2205 * e10) / 524288 + (43659 * e12) / 8388608) * sin8Phi - ((693 * e10) / 1310720 + (6237 * e12) / 5242880) * sin10Phi + ((1001 * e12) / 8388608) * sin12Phi) ); } function calculateInverseM(M, ellipticity, major) { var d = M / major; if (ellipticity === 0.0) { // sphere return d; } var d2 = d * d; var d3 = d2 * d; var d4 = d3 * d; var e = ellipticity; var e2 = e * e; var e4 = e2 * e2; var e6 = e4 * e2; var e8 = e6 * e2; var e10 = e8 * e2; var e12 = e10 * e2; var sin2D = Math.sin(2 * d); var cos2D = Math.cos(2 * d); var sin4D = Math.sin(4 * d); var cos4D = Math.cos(4 * d); var sin6D = Math.sin(6 * d); var cos6D = Math.cos(6 * d); var sin8D = Math.sin(8 * d); var cos8D = Math.cos(8 * d); var sin10D = Math.sin(10 * d); var cos10D = Math.cos(10 * d); var sin12D = Math.sin(12 * d); return ( d + (d * e2) / 4 + (7 * d * e4) / 64 + (15 * d * e6) / 256 + (579 * d * e8) / 16384 + (1515 * d * e10) / 65536 + (16837 * d * e12) / 1048576 + ((3 * d * e4) / 16 + (45 * d * e6) / 256 - (d * (32 * d2 - 561) * e8) / 4096 - (d * (232 * d2 - 1677) * e10) / 16384 + (d * (399985 - 90560 * d2 + 512 * d4) * e12) / 5242880) * cos2D + ((21 * d * e6) / 256 + (483 * d * e8) / 4096 - (d * (224 * d2 - 1969) * e10) / 16384 - (d * (33152 * d2 - 112599) * e12) / 1048576) * cos4D + ((151 * d * e8) / 4096 + (4681 * d * e10) / 65536 + (1479 * d * e12) / 16384 - (453 * d3 * e12) / 32768) * cos6D + ((1097 * d * e10) / 65536 + (42783 * d * e12) / 1048576) * cos8D + ((8011 * d * e12) / 1048576) * cos10D + ((3 * e2) / 8 + (3 * e4) / 16 + (213 * e6) / 2048 - (3 * d2 * e6) / 64 + (255 * e8) / 4096 - (33 * d2 * e8) / 512 + (20861 * e10) / 524288 - (33 * d2 * e10) / 512 + (d4 * e10) / 1024 + (28273 * e12) / 1048576 - (471 * d2 * e12) / 8192 + (9 * d4 * e12) / 4096) * sin2D + ((21 * e4) / 256 + (21 * e6) / 256 + (533 * e8) / 8192 - (21 * d2 * e8) / 512 + (197 * e10) / 4096 - (315 * d2 * e10) / 4096 + (584039 * e12) / 16777216 - (12517 * d2 * e12) / 131072 + (7 * d4 * e12) / 2048) * sin4D + ((151 * e6) / 6144 + (151 * e8) / 4096 + (5019 * e10) / 131072 - (453 * d2 * e10) / 16384 + (26965 * e12) / 786432 - (8607 * d2 * e12) / 131072) * sin6D + ((1097 * e8) / 131072 + (1097 * e10) / 65536 + (225797 * e12) / 10485760 - (1097 * d2 * e12) / 65536) * sin8D + ((8011 * e10) / 2621440 + (8011 * e12) / 1048576) * sin10D + ((293393 * e12) / 251658240) * sin12D ); } function calculateSigma(ellipticity, latitude) { if (ellipticity === 0.0) { // sphere return Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))); } var eSinL = ellipticity * Math.sin(latitude); return ( Math.log(Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + latitude))) - (ellipticity / 2.0) * Math.log((1 + eSinL) / (1 - eSinL)) ); } function calculateHeading( ellipsoidRhumbLine, firstLongitude, firstLatitude, secondLongitude, secondLatitude ) { var sigma1 = calculateSigma(ellipsoidRhumbLine._ellipticity, firstLatitude); var sigma2 = calculateSigma(ellipsoidRhumbLine._ellipticity, secondLatitude); return Math.atan2( CesiumMath.negativePiToPi(secondLongitude - firstLongitude), sigma2 - sigma1 ); } function calculateArcLength( ellipsoidRhumbLine, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude ) { var heading = ellipsoidRhumbLine._heading; var deltaLongitude = secondLongitude - firstLongitude; var distance = 0.0; //Check to see if the rhumb line has constant latitude //This equation will diverge if heading gets close to 90 degrees if ( CesiumMath.equalsEpsilon( Math.abs(heading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8 ) ) { //If heading is close to 90 degrees if (major === minor) { distance = major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude); } else { var sinPhi = Math.sin(firstLatitude); distance = (major * Math.cos(firstLatitude) * CesiumMath.negativePiToPi(deltaLongitude)) / Math.sqrt(1 - ellipsoidRhumbLine._ellipticitySquared * sinPhi * sinPhi); } } else { var M1 = calculateM(ellipsoidRhumbLine._ellipticity, major, firstLatitude); var M2 = calculateM(ellipsoidRhumbLine._ellipticity, major, secondLatitude); distance = (M2 - M1) / Math.cos(heading); } return Math.abs(distance); } var scratchCart1$1 = new Cartesian3(); var scratchCart2$2 = new Cartesian3(); function computeProperties$1(ellipsoidRhumbLine, start, end, ellipsoid) { var firstCartesian = Cartesian3.normalize( ellipsoid.cartographicToCartesian(start, scratchCart2$2), scratchCart1$1 ); var lastCartesian = Cartesian3.normalize( ellipsoid.cartographicToCartesian(end, scratchCart2$2), scratchCart2$2 ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals( "value", Math.abs( Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI ), 0.0125 ); //>>includeEnd('debug'); var major = ellipsoid.maximumRadius; var minor = ellipsoid.minimumRadius; var majorSquared = major * major; var minorSquared = minor * minor; ellipsoidRhumbLine._ellipticitySquared = (majorSquared - minorSquared) / majorSquared; ellipsoidRhumbLine._ellipticity = Math.sqrt( ellipsoidRhumbLine._ellipticitySquared ); ellipsoidRhumbLine._start = Cartographic.clone( start, ellipsoidRhumbLine._start ); ellipsoidRhumbLine._start.height = 0; ellipsoidRhumbLine._end = Cartographic.clone(end, ellipsoidRhumbLine._end); ellipsoidRhumbLine._end.height = 0; ellipsoidRhumbLine._heading = calculateHeading( ellipsoidRhumbLine, start.longitude, start.latitude, end.longitude, end.latitude ); ellipsoidRhumbLine._distance = calculateArcLength( ellipsoidRhumbLine, ellipsoid.maximumRadius, ellipsoid.minimumRadius, start.longitude, start.latitude, end.longitude, end.latitude ); } function interpolateUsingSurfaceDistance( start, heading, distance, major, ellipticity, result ) { var ellipticitySquared = ellipticity * ellipticity; var longitude; var latitude; var deltaLongitude; //Check to see if the rhumb line has constant latitude //This won't converge if heading is close to 90 degrees if ( Math.abs(CesiumMath.PI_OVER_TWO - Math.abs(heading)) > CesiumMath.EPSILON8 ) { //Calculate latitude of the second point var M1 = calculateM(ellipticity, major, start.latitude); var deltaM = distance * Math.cos(heading); var M2 = M1 + deltaM; latitude = calculateInverseM(M2, ellipticity, major); //Now find the longitude of the second point var sigma1 = calculateSigma(ellipticity, start.latitude); var sigma2 = calculateSigma(ellipticity, latitude); deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); } else { //If heading is close to 90 degrees latitude = start.latitude; var localRad; if (ellipticity === 0.0) { // sphere localRad = major * Math.cos(start.latitude); } else { var sinPhi = Math.sin(start.latitude); localRad = (major * Math.cos(start.latitude)) / Math.sqrt(1 - ellipticitySquared * sinPhi * sinPhi); } deltaLongitude = distance / localRad; if (heading > 0.0) { longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); } else { longitude = CesiumMath.negativePiToPi(start.longitude - deltaLongitude); } } if (defined(result)) { result.longitude = longitude; result.latitude = latitude; result.height = 0; return result; } return new Cartographic(longitude, latitude, 0); } /** * Initializes a rhumb line on the ellipsoid connecting the two provided planetodetic points. * * @alias EllipsoidRhumbLine * @constructor * * @param {Cartographic} [start] The initial planetodetic point on the path. * @param {Cartographic} [end] The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. * * @exception {DeveloperError} angle between start and end must be at least 0.0125 radians. */ function EllipsoidRhumbLine(start, end, ellipsoid) { var e = defaultValue(ellipsoid, Ellipsoid.WGS84); this._ellipsoid = e; this._start = new Cartographic(); this._end = new Cartographic(); this._heading = undefined; this._distance = undefined; this._ellipticity = undefined; this._ellipticitySquared = undefined; if (defined(start) && defined(end)) { computeProperties$1(this, start, end, e); } } Object.defineProperties(EllipsoidRhumbLine.prototype, { /** * Gets the ellipsoid. * @memberof EllipsoidRhumbLine.prototype * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets the surface distance between the start and end point * @memberof EllipsoidRhumbLine.prototype * @type {Number} * @readonly */ surfaceDistance: { get: function () { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); return this._distance; }, }, /** * Gets the initial planetodetic point on the path. * @memberof EllipsoidRhumbLine.prototype * @type {Cartographic} * @readonly */ start: { get: function () { return this._start; }, }, /** * Gets the final planetodetic point on the path. * @memberof EllipsoidRhumbLine.prototype * @type {Cartographic} * @readonly */ end: { get: function () { return this._end; }, }, /** * Gets the heading from the start point to the end point. * @memberof EllipsoidRhumbLine.prototype * @type {Number} * @readonly */ heading: { get: function () { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); return this._heading; }, }, }); /** * Create a rhumb line using an initial position with a heading and distance. * * @param {Cartographic} start The initial planetodetic point on the path. * @param {Number} heading The heading in radians. * @param {Number} distance The rhumb line distance between the start and end point. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rhumb line lies. * @param {EllipsoidRhumbLine} [result] The object in which to store the result. * @returns {EllipsoidRhumbLine} The EllipsoidRhumbLine object. */ EllipsoidRhumbLine.fromStartHeadingDistance = function ( start, heading, distance, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("start", start); Check.defined("heading", heading); Check.defined("distance", distance); Check.typeOf.number.greaterThan("distance", distance, 0.0); //>>includeEnd('debug'); var e = defaultValue(ellipsoid, Ellipsoid.WGS84); var major = e.maximumRadius; var minor = e.minimumRadius; var majorSquared = major * major; var minorSquared = minor * minor; var ellipticity = Math.sqrt((majorSquared - minorSquared) / majorSquared); heading = CesiumMath.negativePiToPi(heading); var end = interpolateUsingSurfaceDistance( start, heading, distance, e.maximumRadius, ellipticity ); if ( !defined(result) || (defined(ellipsoid) && !ellipsoid.equals(result.ellipsoid)) ) { return new EllipsoidRhumbLine(start, end, e); } result.setEndPoints(start, end); return result; }; /** * Sets the start and end points of the rhumb line. * * @param {Cartographic} start The initial planetodetic point on the path. * @param {Cartographic} end The final planetodetic point on the path. */ EllipsoidRhumbLine.prototype.setEndPoints = function (start, end) { //>>includeStart('debug', pragmas.debug); Check.defined("start", start); Check.defined("end", end); //>>includeEnd('debug'); computeProperties$1(this, start, end, this._ellipsoid); }; /** * Provides the location of a point at the indicated portion along the rhumb line. * * @param {Number} fraction The portion of the distance between the initial and final points. * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the rhumb line. */ EllipsoidRhumbLine.prototype.interpolateUsingFraction = function ( fraction, result ) { return this.interpolateUsingSurfaceDistance( fraction * this._distance, result ); }; /** * Provides the location of a point at the indicated distance along the rhumb line. * * @param {Number} distance The distance from the inital point to the point of interest along the rhumbLine. * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the rhumb line. * * @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance */ EllipsoidRhumbLine.prototype.interpolateUsingSurfaceDistance = function ( distance, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("distance", distance); if (!defined(this._distance) || this._distance === 0.0) { throw new DeveloperError( "EllipsoidRhumbLine must have distinct start and end set." ); } //>>includeEnd('debug'); return interpolateUsingSurfaceDistance( this._start, this._heading, distance, this._ellipsoid.maximumRadius, this._ellipticity, result ); }; /** * Provides the location of a point at the indicated longitude along the rhumb line. * If the longitude is outside the range of start and end points, the first intersection with the longitude from the start point in the direction of the heading is returned. This follows the spiral property of a rhumb line. * * @param {Number} intersectionLongitude The longitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection or infinite intersections. * * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. */ EllipsoidRhumbLine.prototype.findIntersectionWithLongitude = function ( intersectionLongitude, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("intersectionLongitude", intersectionLongitude); if (!defined(this._distance) || this._distance === 0.0) { throw new DeveloperError( "EllipsoidRhumbLine must have distinct start and end set." ); } //>>includeEnd('debug'); var ellipticity = this._ellipticity; var heading = this._heading; var absHeading = Math.abs(heading); var start = this._start; intersectionLongitude = CesiumMath.negativePiToPi(intersectionLongitude); if ( CesiumMath.equalsEpsilon( Math.abs(intersectionLongitude), Math.PI, CesiumMath.EPSILON14 ) ) { intersectionLongitude = CesiumMath.sign(start.longitude) * Math.PI; } if (!defined(result)) { result = new Cartographic(); } // If heading is -PI/2 or PI/2, this is an E-W rhumb line // If heading is 0 or PI, this is an N-S rhumb line if (Math.abs(CesiumMath.PI_OVER_TWO - absHeading) <= CesiumMath.EPSILON8) { result.longitude = intersectionLongitude; result.latitude = start.latitude; result.height = 0; return result; } else if ( CesiumMath.equalsEpsilon( Math.abs(CesiumMath.PI_OVER_TWO - absHeading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8 ) ) { if ( CesiumMath.equalsEpsilon( intersectionLongitude, start.longitude, CesiumMath.EPSILON12 ) ) { return undefined; } result.longitude = intersectionLongitude; result.latitude = CesiumMath.PI_OVER_TWO * CesiumMath.sign(CesiumMath.PI_OVER_TWO - heading); result.height = 0; return result; } // Use iterative solver from Equation 9 from http://edwilliams.org/ellipsoid/ellipsoid.pdf var phi1 = start.latitude; var eSinPhi1 = ellipticity * Math.sin(phi1); var leftComponent = Math.tan(0.5 * (CesiumMath.PI_OVER_TWO + phi1)) * Math.exp((intersectionLongitude - start.longitude) / Math.tan(heading)); var denominator = (1 + eSinPhi1) / (1 - eSinPhi1); var newPhi = start.latitude; var phi; do { phi = newPhi; var eSinPhi = ellipticity * Math.sin(phi); var numerator = (1 + eSinPhi) / (1 - eSinPhi); newPhi = 2 * Math.atan( leftComponent * Math.pow(numerator / denominator, ellipticity / 2) ) - CesiumMath.PI_OVER_TWO; } while (!CesiumMath.equalsEpsilon(newPhi, phi, CesiumMath.EPSILON12)); result.longitude = intersectionLongitude; result.latitude = newPhi; result.height = 0; return result; }; /** * Provides the location of a point at the indicated latitude along the rhumb line. * If the latitude is outside the range of start and end points, the first intersection with the latitude from that start point in the direction of the heading is returned. This follows the spiral property of a rhumb line. * * @param {Number} intersectionLatitude The latitude, in radians, at which to find the intersection point from the starting point using the heading. * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the intersection point along the rhumb line, undefined if there is no intersection or infinite intersections. * * @exception {DeveloperError} start and end must be set before calling function findIntersectionWithLongitude. */ EllipsoidRhumbLine.prototype.findIntersectionWithLatitude = function ( intersectionLatitude, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("intersectionLatitude", intersectionLatitude); if (!defined(this._distance) || this._distance === 0.0) { throw new DeveloperError( "EllipsoidRhumbLine must have distinct start and end set." ); } //>>includeEnd('debug'); var ellipticity = this._ellipticity; var heading = this._heading; var start = this._start; // If start and end have same latitude, return undefined since it's either no intersection or infinite intersections if ( CesiumMath.equalsEpsilon( Math.abs(heading), CesiumMath.PI_OVER_TWO, CesiumMath.EPSILON8 ) ) { return; } // Can be solved using the same equations from interpolateUsingSurfaceDistance var sigma1 = calculateSigma(ellipticity, start.latitude); var sigma2 = calculateSigma(ellipticity, intersectionLatitude); var deltaLongitude = Math.tan(heading) * (sigma2 - sigma1); var longitude = CesiumMath.negativePiToPi(start.longitude + deltaLongitude); if (defined(result)) { result.longitude = longitude; result.latitude = intersectionLatitude; result.height = 0; return result; } return new Cartographic(longitude, intersectionLatitude, 0); }; function earcut(data, holeIndices, dim) { dim = dim || 2; var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, outerNode = linkedList(data, 0, outerLen, dim, true), triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; var minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (var i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 1 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize); return triangles; } // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { var i, last; if (clockwise === (signedArea(data, start, end, dim) > 0)) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; var p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); var stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim); triangles.push(ear.i / dim); triangles.push(next.i / dim); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can't find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn't work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can't be an ear // now make sure we don't have other points inside the potential ear var p = ear.next.next; while (p !== ear.prev) { if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { var a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can't be an ear // triangle bbox; min & max are calculated like this for speed var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x), minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y), maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x), maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y); // z-order range for the current triangle bbox; var minZ = zOrder(minTX, minTY, minX, minY, invSize), maxZ = zOrder(maxTX, maxTY, minX, minY, invSize); var p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { var p = start; do { var a = p.prev, b = p.next.next; if (!equals(a, b) && intersects$1(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim); triangles.push(p.i / dim); triangles.push(b.i / dim); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two var a = start; do { var b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal var c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize); earcutLinked(c, triangles, dim, minX, minY, invSize); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { var queue = [], i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { outerNode = findHoleBridge(hole, outerNode); if (outerNode) { var b = splitPolygon(outerNode, hole); filterPoints(b, b.next); } } // David Eberly's algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { var p = outerNode, hx = hole.x, hy = hole.y, qx = -Infinity, m; // find a segment intersected by a ray from the hole's leftmost point to the left; // segment's endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; if (x === hx) { if (hy === p.y) return p; if (hy === p.next.y) return p.next; } m = p.x < p.next.x ? p : p.next; } } p = p.next; } while (p !== outerNode); if (!m) return null; if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { var p = start; do { if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham's linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || (qSize > 0 && q)) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e; else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = 32767 * (x - minX) * invSize; y = 32767 * (y - minY) * invSize; x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 2)) & 0x33333333; x = (x | (x << 1)) & 0x55555555; y = (y | (y << 8)) & 0x00FF00FF; y = (y | (y << 4)) & 0x0F0F0F0F; y = (y | (y << 2)) & 0x33333333; y = (y | (y << 1)) & 0x55555555; return x | (y << 1); } // find the leftmost node of a polygon ring function getLeftmost(start) { var p = start, leftmost = start; do { if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects$1(p1, q1, p2, q2) { var o1 = sign(area(p1, q1, p2)); var o2 = sign(area(p1, q1, q2)); var o3 = sign(area(p2, q2, p1)); var o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { var p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects$1(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { var p = a, inside = false, px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { var a2 = new Node$2(a.i, a.x, a.y), b2 = new Node$2(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { var p = new Node$2(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node$2(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = null; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } // return a percentage difference between the polygon area and its triangulation area; // used to verify correctness of triangulation earcut.deviation = function (data, holeIndices, dim, triangles) { var hasHoles = holeIndices && holeIndices.length; var outerLen = hasHoles ? holeIndices[0] * dim : data.length; var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim)); if (hasHoles) { for (var i = 0, len = holeIndices.length; i < len; i++) { var start = holeIndices[i] * dim; var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; polygonArea -= Math.abs(signedArea(data, start, end, dim)); } } var trianglesArea = 0; for (i = 0; i < triangles.length; i += 3) { var a = triangles[i] * dim; var b = triangles[i + 1] * dim; var c = triangles[i + 2] * dim; trianglesArea += Math.abs( (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1])); } return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea); }; function signedArea(data, start, end, dim) { var sum = 0; for (var i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts earcut.flatten = function (data) { var dim = data[0][0].length, result = {vertices: [], holes: [], dimensions: dim}, holeIndex = 0; for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j++) { for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); } if (i > 0) { holeIndex += data[i - 1].length; result.holes.push(holeIndex); } } return result; }; /** * Winding order defines the order of vertices for a triangle to be considered front-facing. * * @enum {Number} */ var WindingOrder = { /** * Vertices are in clockwise order. * * @type {Number} * @constant */ CLOCKWISE: WebGLConstants$1.CW, /** * Vertices are in counter-clockwise order. * * @type {Number} * @constant */ COUNTER_CLOCKWISE: WebGLConstants$1.CCW, }; /** * @private */ WindingOrder.validate = function (windingOrder) { return ( windingOrder === WindingOrder.CLOCKWISE || windingOrder === WindingOrder.COUNTER_CLOCKWISE ); }; var WindingOrder$1 = Object.freeze(WindingOrder); var scaleToGeodeticHeightN = new Cartesian3(); var scaleToGeodeticHeightP = new Cartesian3(); /** * @private */ var PolygonPipeline = {}; /** * @exception {DeveloperError} At least three positions are required. */ PolygonPipeline.computeArea2D = function (positions) { //>>includeStart('debug', pragmas.debug); Check.defined("positions", positions); Check.typeOf.number.greaterThanOrEquals( "positions.length", positions.length, 3 ); //>>includeEnd('debug'); var length = positions.length; var area = 0.0; for (var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) { var v0 = positions[i0]; var v1 = positions[i1]; area += v0.x * v1.y - v1.x * v0.y; } return area * 0.5; }; /** * @returns {WindingOrder} The winding order. * * @exception {DeveloperError} At least three positions are required. */ PolygonPipeline.computeWindingOrder2D = function (positions) { var area = PolygonPipeline.computeArea2D(positions); return area > 0.0 ? WindingOrder$1.COUNTER_CLOCKWISE : WindingOrder$1.CLOCKWISE; }; /** * Triangulate a polygon. * * @param {Cartesian2[]} positions Cartesian2 array containing the vertices of the polygon * @param {Number[]} [holes] An array of the staring indices of the holes. * @returns {Number[]} Index array representing triangles that fill the polygon */ PolygonPipeline.triangulate = function (positions, holes) { //>>includeStart('debug', pragmas.debug); Check.defined("positions", positions); //>>includeEnd('debug'); var flattenedPositions = Cartesian2.packArray(positions); return earcut(flattenedPositions, holes, 2); }; var subdivisionV0Scratch = new Cartesian3(); var subdivisionV1Scratch = new Cartesian3(); var subdivisionV2Scratch = new Cartesian3(); var subdivisionS0Scratch = new Cartesian3(); var subdivisionS1Scratch = new Cartesian3(); var subdivisionS2Scratch = new Cartesian3(); var subdivisionMidScratch = new Cartesian3(); /** * Subdivides positions and raises points to the surface of the ellipsoid. * * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon. * @param {Number[]} indices An array of indices that determines the triangles in the polygon. * @param {Number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * * @exception {DeveloperError} At least three indices are required. * @exception {DeveloperError} The number of indices must be divisable by three. * @exception {DeveloperError} Granularity must be greater than zero. */ PolygonPipeline.computeSubdivision = function ( ellipsoid, positions, indices, granularity ) { granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("ellipsoid", ellipsoid); Check.defined("positions", positions); Check.defined("indices", indices); Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3); Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0); Check.typeOf.number.greaterThan("granularity", granularity, 0.0); //>>includeEnd('debug'); // triangles that need (or might need) to be subdivided. var triangles = indices.slice(0); // New positions due to edge splits are appended to the positions list. var i; var length = positions.length; var subdividedPositions = new Array(length * 3); var q = 0; for (i = 0; i < length; i++) { var item = positions[i]; subdividedPositions[q++] = item.x; subdividedPositions[q++] = item.y; subdividedPositions[q++] = item.z; } var subdividedIndices = []; // Used to make sure shared edges are not split more than once. var edges = {}; var radius = ellipsoid.maximumRadius; var minDistance = CesiumMath.chordLength(granularity, radius); var minDistanceSqrd = minDistance * minDistance; while (triangles.length > 0) { var i2 = triangles.pop(); var i1 = triangles.pop(); var i0 = triangles.pop(); var v0 = Cartesian3.fromArray( subdividedPositions, i0 * 3, subdivisionV0Scratch ); var v1 = Cartesian3.fromArray( subdividedPositions, i1 * 3, subdivisionV1Scratch ); var v2 = Cartesian3.fromArray( subdividedPositions, i2 * 3, subdivisionV2Scratch ); var s0 = Cartesian3.multiplyByScalar( Cartesian3.normalize(v0, subdivisionS0Scratch), radius, subdivisionS0Scratch ); var s1 = Cartesian3.multiplyByScalar( Cartesian3.normalize(v1, subdivisionS1Scratch), radius, subdivisionS1Scratch ); var s2 = Cartesian3.multiplyByScalar( Cartesian3.normalize(v2, subdivisionS2Scratch), radius, subdivisionS2Scratch ); var g0 = Cartesian3.magnitudeSquared( Cartesian3.subtract(s0, s1, subdivisionMidScratch) ); var g1 = Cartesian3.magnitudeSquared( Cartesian3.subtract(s1, s2, subdivisionMidScratch) ); var g2 = Cartesian3.magnitudeSquared( Cartesian3.subtract(s2, s0, subdivisionMidScratch) ); var max = Math.max(g0, g1, g2); var edge; var mid; // if the max length squared of a triangle edge is greater than the chord length of squared // of the granularity, subdivide the triangle if (max > minDistanceSqrd) { if (g0 === max) { edge = Math.min(i0, i1) + " " + Math.max(i0, i1); i = edges[edge]; if (!defined(i)) { mid = Cartesian3.add(v0, v1, subdivisionMidScratch); Cartesian3.multiplyByScalar(mid, 0.5, mid); subdividedPositions.push(mid.x, mid.y, mid.z); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i0, i, i2); triangles.push(i, i1, i2); } else if (g1 === max) { edge = Math.min(i1, i2) + " " + Math.max(i1, i2); i = edges[edge]; if (!defined(i)) { mid = Cartesian3.add(v1, v2, subdivisionMidScratch); Cartesian3.multiplyByScalar(mid, 0.5, mid); subdividedPositions.push(mid.x, mid.y, mid.z); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i1, i, i0); triangles.push(i, i2, i0); } else if (g2 === max) { edge = Math.min(i2, i0) + " " + Math.max(i2, i0); i = edges[edge]; if (!defined(i)) { mid = Cartesian3.add(v2, v0, subdivisionMidScratch); Cartesian3.multiplyByScalar(mid, 0.5, mid); subdividedPositions.push(mid.x, mid.y, mid.z); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i2, i, i1); triangles.push(i, i0, i1); } } else { subdividedIndices.push(i0); subdividedIndices.push(i1); subdividedIndices.push(i2); } } return new Geometry({ attributes: { position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: subdividedPositions, }), }, indices: subdividedIndices, primitiveType: PrimitiveType$1.TRIANGLES, }); }; var subdivisionC0Scratch = new Cartographic(); var subdivisionC1Scratch = new Cartographic(); var subdivisionC2Scratch = new Cartographic(); var subdivisionCartographicScratch = new Cartographic(); /** * Subdivides positions on rhumb lines and raises points to the surface of the ellipsoid. * * @param {Ellipsoid} ellipsoid The ellipsoid the polygon in on. * @param {Cartesian3[]} positions An array of {@link Cartesian3} positions of the polygon. * @param {Number[]} indices An array of indices that determines the triangles in the polygon. * @param {Number} [granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. * * @exception {DeveloperError} At least three indices are required. * @exception {DeveloperError} The number of indices must be divisable by three. * @exception {DeveloperError} Granularity must be greater than zero. */ PolygonPipeline.computeRhumbLineSubdivision = function ( ellipsoid, positions, indices, granularity ) { granularity = defaultValue(granularity, CesiumMath.RADIANS_PER_DEGREE); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("ellipsoid", ellipsoid); Check.defined("positions", positions); Check.defined("indices", indices); Check.typeOf.number.greaterThanOrEquals("indices.length", indices.length, 3); Check.typeOf.number.equals("indices.length % 3", "0", indices.length % 3, 0); Check.typeOf.number.greaterThan("granularity", granularity, 0.0); //>>includeEnd('debug'); // triangles that need (or might need) to be subdivided. var triangles = indices.slice(0); // New positions due to edge splits are appended to the positions list. var i; var length = positions.length; var subdividedPositions = new Array(length * 3); var q = 0; for (i = 0; i < length; i++) { var item = positions[i]; subdividedPositions[q++] = item.x; subdividedPositions[q++] = item.y; subdividedPositions[q++] = item.z; } var subdividedIndices = []; // Used to make sure shared edges are not split more than once. var edges = {}; var radius = ellipsoid.maximumRadius; var minDistance = CesiumMath.chordLength(granularity, radius); var rhumb0 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); var rhumb1 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); var rhumb2 = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); while (triangles.length > 0) { var i2 = triangles.pop(); var i1 = triangles.pop(); var i0 = triangles.pop(); var v0 = Cartesian3.fromArray( subdividedPositions, i0 * 3, subdivisionV0Scratch ); var v1 = Cartesian3.fromArray( subdividedPositions, i1 * 3, subdivisionV1Scratch ); var v2 = Cartesian3.fromArray( subdividedPositions, i2 * 3, subdivisionV2Scratch ); var c0 = ellipsoid.cartesianToCartographic(v0, subdivisionC0Scratch); var c1 = ellipsoid.cartesianToCartographic(v1, subdivisionC1Scratch); var c2 = ellipsoid.cartesianToCartographic(v2, subdivisionC2Scratch); rhumb0.setEndPoints(c0, c1); var g0 = rhumb0.surfaceDistance; rhumb1.setEndPoints(c1, c2); var g1 = rhumb1.surfaceDistance; rhumb2.setEndPoints(c2, c0); var g2 = rhumb2.surfaceDistance; var max = Math.max(g0, g1, g2); var edge; var mid; var midHeight; var midCartesian3; // if the max length squared of a triangle edge is greater than granularity, subdivide the triangle if (max > minDistance) { if (g0 === max) { edge = Math.min(i0, i1) + " " + Math.max(i0, i1); i = edges[edge]; if (!defined(i)) { mid = rhumb0.interpolateUsingFraction( 0.5, subdivisionCartographicScratch ); midHeight = (c0.height + c1.height) * 0.5; midCartesian3 = Cartesian3.fromRadians( mid.longitude, mid.latitude, midHeight, ellipsoid, subdivisionMidScratch ); subdividedPositions.push( midCartesian3.x, midCartesian3.y, midCartesian3.z ); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i0, i, i2); triangles.push(i, i1, i2); } else if (g1 === max) { edge = Math.min(i1, i2) + " " + Math.max(i1, i2); i = edges[edge]; if (!defined(i)) { mid = rhumb1.interpolateUsingFraction( 0.5, subdivisionCartographicScratch ); midHeight = (c1.height + c2.height) * 0.5; midCartesian3 = Cartesian3.fromRadians( mid.longitude, mid.latitude, midHeight, ellipsoid, subdivisionMidScratch ); subdividedPositions.push( midCartesian3.x, midCartesian3.y, midCartesian3.z ); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i1, i, i0); triangles.push(i, i2, i0); } else if (g2 === max) { edge = Math.min(i2, i0) + " " + Math.max(i2, i0); i = edges[edge]; if (!defined(i)) { mid = rhumb2.interpolateUsingFraction( 0.5, subdivisionCartographicScratch ); midHeight = (c2.height + c0.height) * 0.5; midCartesian3 = Cartesian3.fromRadians( mid.longitude, mid.latitude, midHeight, ellipsoid, subdivisionMidScratch ); subdividedPositions.push( midCartesian3.x, midCartesian3.y, midCartesian3.z ); i = subdividedPositions.length / 3 - 1; edges[edge] = i; } triangles.push(i2, i, i1); triangles.push(i, i0, i1); } } else { subdividedIndices.push(i0); subdividedIndices.push(i1); subdividedIndices.push(i2); } } return new Geometry({ attributes: { position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: subdividedPositions, }), }, indices: subdividedIndices, primitiveType: PrimitiveType$1.TRIANGLES, }); }; /** * Scales each position of a geometry's position attribute to a height, in place. * * @param {Number[]} positions The array of numbers representing the positions to be scaled * @param {Number} [height=0.0] The desired height to add to the positions * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @param {Boolean} [scaleToSurface=true] true if the positions need to be scaled to the surface before the height is added. * @returns {Number[]} The input array of positions, scaled to height */ PolygonPipeline.scaleToGeodeticHeight = function ( positions, height, ellipsoid, scaleToSurface ) { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var n = scaleToGeodeticHeightN; var p = scaleToGeodeticHeightP; height = defaultValue(height, 0.0); scaleToSurface = defaultValue(scaleToSurface, true); if (defined(positions)) { var length = positions.length; for (var i = 0; i < length; i += 3) { Cartesian3.fromArray(positions, i, p); if (scaleToSurface) { p = ellipsoid.scaleToGeodeticSurface(p, p); } if (height !== 0) { n = ellipsoid.geodeticSurfaceNormal(p, n); Cartesian3.multiplyByScalar(n, height, n); Cartesian3.add(p, n, p); } positions[i] = p.x; positions[i + 1] = p.y; positions[i + 2] = p.z; } } return positions; }; /** * A queue that can enqueue items at the end, and dequeue items from the front. * * @alias Queue * @constructor */ function Queue() { this._array = []; this._offset = 0; this._length = 0; } Object.defineProperties(Queue.prototype, { /** * The length of the queue. * * @memberof Queue.prototype * * @type {Number} * @readonly */ length: { get: function () { return this._length; }, }, }); /** * Enqueues the specified item. * * @param {*} item The item to enqueue. */ Queue.prototype.enqueue = function (item) { this._array.push(item); this._length++; }; /** * Dequeues an item. Returns undefined if the queue is empty. * * @returns {*} The the dequeued item. */ Queue.prototype.dequeue = function () { if (this._length === 0) { return undefined; } var array = this._array; var offset = this._offset; var item = array[offset]; array[offset] = undefined; offset++; if (offset > 10 && offset * 2 > array.length) { //compact array this._array = array.slice(offset); offset = 0; } this._offset = offset; this._length--; return item; }; /** * Returns the item at the front of the queue. Returns undefined if the queue is empty. * * @returns {*} The item at the front of the queue. */ Queue.prototype.peek = function () { if (this._length === 0) { return undefined; } return this._array[this._offset]; }; /** * Check whether this queue contains the specified item. * * @param {*} item The item to search for. */ Queue.prototype.contains = function (item) { return this._array.indexOf(item) !== -1; }; /** * Remove all items from the queue. */ Queue.prototype.clear = function () { this._array.length = this._offset = this._length = 0; }; /** * Sort the items in the queue in-place. * * @param {Queue.Comparator} compareFunction A function that defines the sort order. */ Queue.prototype.sort = function (compareFunction) { if (this._offset > 0) { //compact array this._array = this._array.slice(this._offset); this._offset = 0; } this._array.sort(compareFunction); }; /** * @private */ var PolygonGeometryLibrary = {}; PolygonGeometryLibrary.computeHierarchyPackedLength = function ( polygonHierarchy ) { var numComponents = 0; var stack = [polygonHierarchy]; while (stack.length > 0) { var hierarchy = stack.pop(); if (!defined(hierarchy)) { continue; } numComponents += 2; var positions = hierarchy.positions; var holes = hierarchy.holes; if (defined(positions)) { numComponents += positions.length * Cartesian3.packedLength; } if (defined(holes)) { var length = holes.length; for (var i = 0; i < length; ++i) { stack.push(holes[i]); } } } return numComponents; }; PolygonGeometryLibrary.packPolygonHierarchy = function ( polygonHierarchy, array, startingIndex ) { var stack = [polygonHierarchy]; while (stack.length > 0) { var hierarchy = stack.pop(); if (!defined(hierarchy)) { continue; } var positions = hierarchy.positions; var holes = hierarchy.holes; array[startingIndex++] = defined(positions) ? positions.length : 0; array[startingIndex++] = defined(holes) ? holes.length : 0; if (defined(positions)) { var positionsLength = positions.length; for (var i = 0; i < positionsLength; ++i, startingIndex += 3) { Cartesian3.pack(positions[i], array, startingIndex); } } if (defined(holes)) { var holesLength = holes.length; for (var j = 0; j < holesLength; ++j) { stack.push(holes[j]); } } } return startingIndex; }; PolygonGeometryLibrary.unpackPolygonHierarchy = function ( array, startingIndex ) { var positionsLength = array[startingIndex++]; var holesLength = array[startingIndex++]; var positions = new Array(positionsLength); var holes = holesLength > 0 ? new Array(holesLength) : undefined; for ( var i = 0; i < positionsLength; ++i, startingIndex += Cartesian3.packedLength ) { positions[i] = Cartesian3.unpack(array, startingIndex); } for (var j = 0; j < holesLength; ++j) { holes[j] = PolygonGeometryLibrary.unpackPolygonHierarchy( array, startingIndex ); startingIndex = holes[j].startingIndex; delete holes[j].startingIndex; } return { positions: positions, holes: holes, startingIndex: startingIndex, }; }; var distanceScratch = new Cartesian3(); function getPointAtDistance(p0, p1, distance, length) { Cartesian3.subtract(p1, p0, distanceScratch); Cartesian3.multiplyByScalar( distanceScratch, distance / length, distanceScratch ); Cartesian3.add(p0, distanceScratch, distanceScratch); return [distanceScratch.x, distanceScratch.y, distanceScratch.z]; } PolygonGeometryLibrary.subdivideLineCount = function (p0, p1, minDistance) { var distance = Cartesian3.distance(p0, p1); var n = distance / minDistance; var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n))); return Math.pow(2, countDivide); }; var scratchCartographic0$1 = new Cartographic(); var scratchCartographic1$2 = new Cartographic(); var scratchCartographic2$1 = new Cartographic(); var scratchCartesian0 = new Cartesian3(); PolygonGeometryLibrary.subdivideRhumbLineCount = function ( ellipsoid, p0, p1, minDistance ) { var c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0$1); var c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1$2); var rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid); var n = rhumb.surfaceDistance / minDistance; var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n))); return Math.pow(2, countDivide); }; PolygonGeometryLibrary.subdivideLine = function (p0, p1, minDistance, result) { var numVertices = PolygonGeometryLibrary.subdivideLineCount( p0, p1, minDistance ); var length = Cartesian3.distance(p0, p1); var distanceBetweenVertices = length / numVertices; if (!defined(result)) { result = []; } var positions = result; positions.length = numVertices * 3; var index = 0; for (var i = 0; i < numVertices; i++) { var p = getPointAtDistance(p0, p1, i * distanceBetweenVertices, length); positions[index++] = p[0]; positions[index++] = p[1]; positions[index++] = p[2]; } return positions; }; PolygonGeometryLibrary.subdivideRhumbLine = function ( ellipsoid, p0, p1, minDistance, result ) { var c0 = ellipsoid.cartesianToCartographic(p0, scratchCartographic0$1); var c1 = ellipsoid.cartesianToCartographic(p1, scratchCartographic1$2); var rhumb = new EllipsoidRhumbLine(c0, c1, ellipsoid); var n = rhumb.surfaceDistance / minDistance; var countDivide = Math.max(0, Math.ceil(CesiumMath.log2(n))); var numVertices = Math.pow(2, countDivide); var distanceBetweenVertices = rhumb.surfaceDistance / numVertices; if (!defined(result)) { result = []; } var positions = result; positions.length = numVertices * 3; var index = 0; for (var i = 0; i < numVertices; i++) { var c = rhumb.interpolateUsingSurfaceDistance( i * distanceBetweenVertices, scratchCartographic2$1 ); var p = ellipsoid.cartographicToCartesian(c, scratchCartesian0); positions[index++] = p.x; positions[index++] = p.y; positions[index++] = p.z; } return positions; }; var scaleToGeodeticHeightN1 = new Cartesian3(); var scaleToGeodeticHeightN2 = new Cartesian3(); var scaleToGeodeticHeightP1 = new Cartesian3(); var scaleToGeodeticHeightP2 = new Cartesian3(); PolygonGeometryLibrary.scaleToGeodeticHeightExtruded = function ( geometry, maxHeight, minHeight, ellipsoid, perPositionHeight ) { ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var n1 = scaleToGeodeticHeightN1; var n2 = scaleToGeodeticHeightN2; var p = scaleToGeodeticHeightP1; var p2 = scaleToGeodeticHeightP2; if ( defined(geometry) && defined(geometry.attributes) && defined(geometry.attributes.position) ) { var positions = geometry.attributes.position.values; var length = positions.length / 2; for (var i = 0; i < length; i += 3) { Cartesian3.fromArray(positions, i, p); ellipsoid.geodeticSurfaceNormal(p, n1); p2 = ellipsoid.scaleToGeodeticSurface(p, p2); n2 = Cartesian3.multiplyByScalar(n1, minHeight, n2); n2 = Cartesian3.add(p2, n2, n2); positions[i + length] = n2.x; positions[i + 1 + length] = n2.y; positions[i + 2 + length] = n2.z; if (perPositionHeight) { p2 = Cartesian3.clone(p, p2); } n2 = Cartesian3.multiplyByScalar(n1, maxHeight, n2); n2 = Cartesian3.add(p2, n2, n2); positions[i] = n2.x; positions[i + 1] = n2.y; positions[i + 2] = n2.z; } } return geometry; }; PolygonGeometryLibrary.polygonOutlinesFromHierarchy = function ( polygonHierarchy, scaleToEllipsoidSurface, ellipsoid ) { // create from a polygon hierarchy // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf var polygons = []; var queue = new Queue(); queue.enqueue(polygonHierarchy); var i; var j; var length; while (queue.length !== 0) { var outerNode = queue.dequeue(); var outerRing = outerNode.positions; if (scaleToEllipsoidSurface) { length = outerRing.length; for (i = 0; i < length; i++) { ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]); } } outerRing = arrayRemoveDuplicates( outerRing, Cartesian3.equalsEpsilon, true ); if (outerRing.length < 3) { continue; } var numChildren = outerNode.holes ? outerNode.holes.length : 0; // The outer polygon contains inner polygons for (i = 0; i < numChildren; i++) { var hole = outerNode.holes[i]; var holePositions = hole.positions; if (scaleToEllipsoidSurface) { length = holePositions.length; for (j = 0; j < length; ++j) { ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]); } } holePositions = arrayRemoveDuplicates( holePositions, Cartesian3.equalsEpsilon, true ); if (holePositions.length < 3) { continue; } polygons.push(holePositions); var numGrandchildren = 0; if (defined(hole.holes)) { numGrandchildren = hole.holes.length; } for (j = 0; j < numGrandchildren; j++) { queue.enqueue(hole.holes[j]); } } polygons.push(outerRing); } return polygons; }; PolygonGeometryLibrary.polygonsFromHierarchy = function ( polygonHierarchy, projectPointsTo2D, scaleToEllipsoidSurface, ellipsoid ) { // create from a polygon hierarchy // Algorithm adapted from http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf var hierarchy = []; var polygons = []; var queue = new Queue(); queue.enqueue(polygonHierarchy); while (queue.length !== 0) { var outerNode = queue.dequeue(); var outerRing = outerNode.positions; var holes = outerNode.holes; var i; var length; if (scaleToEllipsoidSurface) { length = outerRing.length; for (i = 0; i < length; i++) { ellipsoid.scaleToGeodeticSurface(outerRing[i], outerRing[i]); } } outerRing = arrayRemoveDuplicates( outerRing, Cartesian3.equalsEpsilon, true ); if (outerRing.length < 3) { continue; } var positions2D = projectPointsTo2D(outerRing); if (!defined(positions2D)) { continue; } var holeIndices = []; var originalWindingOrder = PolygonPipeline.computeWindingOrder2D( positions2D ); if (originalWindingOrder === WindingOrder$1.CLOCKWISE) { positions2D.reverse(); outerRing = outerRing.slice().reverse(); } var positions = outerRing.slice(); var numChildren = defined(holes) ? holes.length : 0; var polygonHoles = []; var j; for (i = 0; i < numChildren; i++) { var hole = holes[i]; var holePositions = hole.positions; if (scaleToEllipsoidSurface) { length = holePositions.length; for (j = 0; j < length; ++j) { ellipsoid.scaleToGeodeticSurface(holePositions[j], holePositions[j]); } } holePositions = arrayRemoveDuplicates( holePositions, Cartesian3.equalsEpsilon, true ); if (holePositions.length < 3) { continue; } var holePositions2D = projectPointsTo2D(holePositions); if (!defined(holePositions2D)) { continue; } originalWindingOrder = PolygonPipeline.computeWindingOrder2D( holePositions2D ); if (originalWindingOrder === WindingOrder$1.CLOCKWISE) { holePositions2D.reverse(); holePositions = holePositions.slice().reverse(); } polygonHoles.push(holePositions); holeIndices.push(positions.length); positions = positions.concat(holePositions); positions2D = positions2D.concat(holePositions2D); var numGrandchildren = 0; if (defined(hole.holes)) { numGrandchildren = hole.holes.length; } for (j = 0; j < numGrandchildren; j++) { queue.enqueue(hole.holes[j]); } } hierarchy.push({ outerRing: outerRing, holes: polygonHoles, }); polygons.push({ positions: positions, positions2D: positions2D, holes: holeIndices, }); } return { hierarchy: hierarchy, polygons: polygons, }; }; var computeBoundingRectangleCartesian2 = new Cartesian2(); var computeBoundingRectangleCartesian3 = new Cartesian3(); var computeBoundingRectangleQuaternion = new Quaternion(); var computeBoundingRectangleMatrix3 = new Matrix3(); PolygonGeometryLibrary.computeBoundingRectangle = function ( planeNormal, projectPointTo2D, positions, angle, result ) { var rotation = Quaternion.fromAxisAngle( planeNormal, angle, computeBoundingRectangleQuaternion ); var textureMatrix = Matrix3.fromQuaternion( rotation, computeBoundingRectangleMatrix3 ); var minX = Number.POSITIVE_INFINITY; var maxX = Number.NEGATIVE_INFINITY; var minY = Number.POSITIVE_INFINITY; var maxY = Number.NEGATIVE_INFINITY; var length = positions.length; for (var i = 0; i < length; ++i) { var p = Cartesian3.clone(positions[i], computeBoundingRectangleCartesian3); Matrix3.multiplyByVector(textureMatrix, p, p); var st = projectPointTo2D(p, computeBoundingRectangleCartesian2); if (defined(st)) { minX = Math.min(minX, st.x); maxX = Math.max(maxX, st.x); minY = Math.min(minY, st.y); maxY = Math.max(maxY, st.y); } } result.x = minX; result.y = minY; result.width = maxX - minX; result.height = maxY - minY; return result; }; PolygonGeometryLibrary.createGeometryFromPositions = function ( ellipsoid, polygon, granularity, perPositionHeight, vertexFormat, arcType ) { var indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes); /* If polygon is completely unrenderable, just use the first three vertices */ if (indices.length < 3) { indices = [0, 1, 2]; } var positions = polygon.positions; if (perPositionHeight) { var length = positions.length; var flattenedPositions = new Array(length * 3); var index = 0; for (var i = 0; i < length; i++) { var p = positions[i]; flattenedPositions[index++] = p.x; flattenedPositions[index++] = p.y; flattenedPositions[index++] = p.z; } var geometry = new Geometry({ attributes: { position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: flattenedPositions, }), }, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, }); if (vertexFormat.normal) { return GeometryPipeline.computeNormal(geometry); } return geometry; } if (arcType === ArcType$1.GEODESIC) { return PolygonPipeline.computeSubdivision( ellipsoid, positions, indices, granularity ); } else if (arcType === ArcType$1.RHUMB) { return PolygonPipeline.computeRhumbLineSubdivision( ellipsoid, positions, indices, granularity ); } }; var computeWallIndicesSubdivided = []; var p1Scratch$1 = new Cartesian3(); var p2Scratch$1 = new Cartesian3(); PolygonGeometryLibrary.computeWallGeometry = function ( positions, ellipsoid, granularity, perPositionHeight, arcType ) { var edgePositions; var topEdgeLength; var i; var p1; var p2; var length = positions.length; var index = 0; if (!perPositionHeight) { var minDistance = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); var numVertices = 0; if (arcType === ArcType$1.GEODESIC) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideLineCount( positions[i], positions[(i + 1) % length], minDistance ); } } else if (arcType === ArcType$1.RHUMB) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount( ellipsoid, positions[i], positions[(i + 1) % length], minDistance ); } } topEdgeLength = (numVertices + length) * 3; edgePositions = new Array(topEdgeLength * 2); for (i = 0; i < length; i++) { p1 = positions[i]; p2 = positions[(i + 1) % length]; var tempPositions; if (arcType === ArcType$1.GEODESIC) { tempPositions = PolygonGeometryLibrary.subdivideLine( p1, p2, minDistance, computeWallIndicesSubdivided ); } else if (arcType === ArcType$1.RHUMB) { tempPositions = PolygonGeometryLibrary.subdivideRhumbLine( ellipsoid, p1, p2, minDistance, computeWallIndicesSubdivided ); } var tempPositionsLength = tempPositions.length; for (var j = 0; j < tempPositionsLength; ++j, ++index) { edgePositions[index] = tempPositions[j]; edgePositions[index + topEdgeLength] = tempPositions[j]; } edgePositions[index] = p2.x; edgePositions[index + topEdgeLength] = p2.x; ++index; edgePositions[index] = p2.y; edgePositions[index + topEdgeLength] = p2.y; ++index; edgePositions[index] = p2.z; edgePositions[index + topEdgeLength] = p2.z; ++index; } } else { topEdgeLength = length * 3 * 2; edgePositions = new Array(topEdgeLength * 2); for (i = 0; i < length; i++) { p1 = positions[i]; p2 = positions[(i + 1) % length]; edgePositions[index] = edgePositions[index + topEdgeLength] = p1.x; ++index; edgePositions[index] = edgePositions[index + topEdgeLength] = p1.y; ++index; edgePositions[index] = edgePositions[index + topEdgeLength] = p1.z; ++index; edgePositions[index] = edgePositions[index + topEdgeLength] = p2.x; ++index; edgePositions[index] = edgePositions[index + topEdgeLength] = p2.y; ++index; edgePositions[index] = edgePositions[index + topEdgeLength] = p2.z; ++index; } } length = edgePositions.length; var indices = IndexDatatype$1.createTypedArray( length / 3, length - positions.length * 6 ); var edgeIndex = 0; length /= 6; for (i = 0; i < length; i++) { var UL = i; var UR = UL + 1; var LL = UL + length; var LR = LL + 1; p1 = Cartesian3.fromArray(edgePositions, UL * 3, p1Scratch$1); p2 = Cartesian3.fromArray(edgePositions, UR * 3, p2Scratch$1); if ( Cartesian3.equalsEpsilon( p1, p2, CesiumMath.EPSILON10, CesiumMath.EPSILON10 ) ) { //skip corner continue; } indices[edgeIndex++] = UL; indices[edgeIndex++] = LL; indices[edgeIndex++] = UR; indices[edgeIndex++] = UR; indices[edgeIndex++] = LL; indices[edgeIndex++] = LR; } return new Geometry({ attributes: new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: edgePositions, }), }), indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, }); }; var scratchPosition$b = new Cartesian3(); var scratchBR = new BoundingRectangle(); var stScratch$1 = new Cartesian2(); var textureCoordinatesOrigin = new Cartesian2(); var scratchNormal$4 = new Cartesian3(); var scratchTangent$3 = new Cartesian3(); var scratchBitangent$3 = new Cartesian3(); var centerScratch$6 = new Cartesian3(); var axis1Scratch = new Cartesian3(); var axis2Scratch = new Cartesian3(); var quaternionScratch$2 = new Quaternion(); var textureMatrixScratch = new Matrix3(); var tangentRotationScratch = new Matrix3(); var surfaceNormalScratch = new Cartesian3(); function createGeometryFromPolygon( polygon, vertexFormat, boundingRectangle, stRotation, projectPointTo2D, normal, tangent, bitangent ) { var positions = polygon.positions; var indices = PolygonPipeline.triangulate(polygon.positions2D, polygon.holes); /* If polygon is completely unrenderable, just use the first three vertices */ if (indices.length < 3) { indices = [0, 1, 2]; } var newIndices = IndexDatatype$1.createTypedArray( positions.length, indices.length ); newIndices.set(indices); var textureMatrix = textureMatrixScratch; if (stRotation !== 0.0) { var rotation = Quaternion.fromAxisAngle( normal, stRotation, quaternionScratch$2 ); textureMatrix = Matrix3.fromQuaternion(rotation, textureMatrix); if (vertexFormat.tangent || vertexFormat.bitangent) { rotation = Quaternion.fromAxisAngle( normal, -stRotation, quaternionScratch$2 ); var tangentRotation = Matrix3.fromQuaternion( rotation, tangentRotationScratch ); tangent = Cartesian3.normalize( Matrix3.multiplyByVector(tangentRotation, tangent, tangent), tangent ); if (vertexFormat.bitangent) { bitangent = Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); } } } else { textureMatrix = Matrix3.clone(Matrix3.IDENTITY, textureMatrix); } var stOrigin = textureCoordinatesOrigin; if (vertexFormat.st) { stOrigin.x = boundingRectangle.x; stOrigin.y = boundingRectangle.y; } var length = positions.length; var size = length * 3; var flatPositions = new Float64Array(size); var normals = vertexFormat.normal ? new Float32Array(size) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(size) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(size) : undefined; var textureCoordinates = vertexFormat.st ? new Float32Array(length * 2) : undefined; var positionIndex = 0; var normalIndex = 0; var bitangentIndex = 0; var tangentIndex = 0; var stIndex = 0; for (var i = 0; i < length; i++) { var position = positions[i]; flatPositions[positionIndex++] = position.x; flatPositions[positionIndex++] = position.y; flatPositions[positionIndex++] = position.z; if (vertexFormat.st) { var p = Matrix3.multiplyByVector( textureMatrix, position, scratchPosition$b ); var st = projectPointTo2D(p, stScratch$1); Cartesian2.subtract(st, stOrigin, st); var stx = CesiumMath.clamp(st.x / boundingRectangle.width, 0, 1); var sty = CesiumMath.clamp(st.y / boundingRectangle.height, 0, 1); textureCoordinates[stIndex++] = stx; textureCoordinates[stIndex++] = sty; } if (vertexFormat.normal) { normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; } if (vertexFormat.tangent) { tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; } if (vertexFormat.bitangent) { bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: flatPositions, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } return new Geometry({ attributes: attributes, indices: newIndices, primitiveType: PrimitiveType$1.TRIANGLES, }); } /** * A description of a polygon composed of arbitrary coplanar positions. * * @alias CoplanarPolygonGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * * @example * var polygonGeometry = new Cesium.CoplanarPolygonGeometry({ * polygonHierarchy: new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArrayHeights([ * -90.0, 30.0, 0.0, * -90.0, 30.0, 300000.0, * -80.0, 30.0, 300000.0, * -80.0, 30.0, 0.0 * ])) * }); * */ function CoplanarPolygonGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var polygonHierarchy = options.polygonHierarchy; //>>includeStart('debug', pragmas.debug); Check.defined("options.polygonHierarchy", polygonHierarchy); //>>includeEnd('debug'); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); this._vertexFormat = VertexFormat.clone(vertexFormat); this._polygonHierarchy = polygonHierarchy; this._stRotation = defaultValue(options.stRotation, 0.0); this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._workerName = "createCoplanarPolygonGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + VertexFormat.packedLength + Ellipsoid.packedLength + 2; } /** * A description of a coplanar polygon from an array of positions. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that defined the corner points of the polygon. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @returns {CoplanarPolygonGeometry} * * @example * // create a polygon from points * var polygon = Cesium.CoplanarPolygonGeometry.fromPositions({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * }); * var geometry = Cesium.PolygonGeometry.createGeometry(polygon); * * @see PolygonGeometry#createGeometry */ CoplanarPolygonGeometry.fromPositions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", options.positions); //>>includeEnd('debug'); var newOptions = { polygonHierarchy: { positions: options.positions, }, vertexFormat: options.vertexFormat, stRotation: options.stRotation, ellipsoid: options.ellipsoid, }; return new CoplanarPolygonGeometry(newOptions); }; /** * Stores the provided instance into the provided array. * * @param {CoplanarPolygonGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CoplanarPolygonGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); startingIndex = PolygonGeometryLibrary.packPolygonHierarchy( value._polygonHierarchy, array, startingIndex ); Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._stRotation; array[startingIndex] = value.packedLength; return array; }; var scratchEllipsoid$c = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat$a = new VertexFormat(); var scratchOptions$h = { polygonHierarchy: {}, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CoplanarPolygonGeometry} [result] The object into which to store the result. * @returns {CoplanarPolygonGeometry} The modified result parameter or a new CoplanarPolygonGeometry instance if one was not provided. */ CoplanarPolygonGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var polygonHierarchy = PolygonGeometryLibrary.unpackPolygonHierarchy( array, startingIndex ); startingIndex = polygonHierarchy.startingIndex; delete polygonHierarchy.startingIndex; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$c); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$a ); startingIndex += VertexFormat.packedLength; var stRotation = array[startingIndex++]; var packedLength = array[startingIndex]; if (!defined(result)) { result = new CoplanarPolygonGeometry(scratchOptions$h); } result._polygonHierarchy = polygonHierarchy; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._stRotation = stRotation; result.packedLength = packedLength; return result; }; /** * Computes the geometric representation of an arbitrary coplanar polygon, including its vertices, indices, and a bounding sphere. * * @param {CoplanarPolygonGeometry} polygonGeometry A description of the polygon. * @returns {Geometry|undefined} The computed vertices and indices. */ CoplanarPolygonGeometry.createGeometry = function (polygonGeometry) { var vertexFormat = polygonGeometry._vertexFormat; var polygonHierarchy = polygonGeometry._polygonHierarchy; var stRotation = polygonGeometry._stRotation; var outerPositions = polygonHierarchy.positions; outerPositions = arrayRemoveDuplicates( outerPositions, Cartesian3.equalsEpsilon, true ); if (outerPositions.length < 3) { return; } var normal = scratchNormal$4; var tangent = scratchTangent$3; var bitangent = scratchBitangent$3; var axis1 = axis1Scratch; var axis2 = axis2Scratch; var validGeometry = CoplanarPolygonGeometryLibrary.computeProjectTo2DArguments( outerPositions, centerScratch$6, axis1, axis2 ); if (!validGeometry) { return undefined; } normal = Cartesian3.cross(axis1, axis2, normal); normal = Cartesian3.normalize(normal, normal); if ( !Cartesian3.equalsEpsilon( centerScratch$6, Cartesian3.ZERO, CesiumMath.EPSILON6 ) ) { var surfaceNormal = polygonGeometry._ellipsoid.geodeticSurfaceNormal( centerScratch$6, surfaceNormalScratch ); if (Cartesian3.dot(normal, surfaceNormal) < 0) { normal = Cartesian3.negate(normal, normal); axis1 = Cartesian3.negate(axis1, axis1); } } var projectPoints = CoplanarPolygonGeometryLibrary.createProjectPointsTo2DFunction( centerScratch$6, axis1, axis2 ); var projectPoint = CoplanarPolygonGeometryLibrary.createProjectPointTo2DFunction( centerScratch$6, axis1, axis2 ); if (vertexFormat.tangent) { tangent = Cartesian3.clone(axis1, tangent); } if (vertexFormat.bitangent) { bitangent = Cartesian3.clone(axis2, bitangent); } var results = PolygonGeometryLibrary.polygonsFromHierarchy( polygonHierarchy, projectPoints, false ); var hierarchy = results.hierarchy; var polygons = results.polygons; if (hierarchy.length === 0) { return; } outerPositions = hierarchy[0].outerRing; var boundingSphere = BoundingSphere.fromPoints(outerPositions); var boundingRectangle = PolygonGeometryLibrary.computeBoundingRectangle( normal, projectPoint, outerPositions, stRotation, scratchBR ); var geometries = []; for (var i = 0; i < polygons.length; i++) { var geometryInstance = new GeometryInstance({ geometry: createGeometryFromPolygon( polygons[i], vertexFormat, boundingRectangle, stRotation, projectPoint, normal, tangent, bitangent ), }); geometries.push(geometryInstance); } var geometry = GeometryPipeline.combineInstances(geometries)[0]; geometry.attributes.position.values = new Float64Array( geometry.attributes.position.values ); geometry.indices = IndexDatatype$1.createTypedArray( geometry.attributes.position.values.length / 3, geometry.indices ); var attributes = geometry.attributes; if (!vertexFormat.position) { delete attributes.position; } return new Geometry({ attributes: attributes, indices: geometry.indices, primitiveType: geometry.primitiveType, boundingSphere: boundingSphere, }); }; function createGeometryFromPositions$1(positions) { var length = positions.length; var flatPositions = new Float64Array(length * 3); var indices = IndexDatatype$1.createTypedArray(length, length * 2); var positionIndex = 0; var index = 0; for (var i = 0; i < length; i++) { var position = positions[i]; flatPositions[positionIndex++] = position.x; flatPositions[positionIndex++] = position.y; flatPositions[positionIndex++] = position.z; indices[index++] = i; indices[index++] = (i + 1) % length; } var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: flatPositions, }), }); return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, }); } /** * A description of the outline of a polygon composed of arbitrary coplanar positions. * * @alias CoplanarPolygonOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes. * * @see CoplanarPolygonOutlineGeometry.createGeometry * * @example * var polygonOutline = new Cesium.CoplanarPolygonOutlineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArrayHeights([ * -90.0, 30.0, 0.0, * -90.0, 30.0, 1000.0, * -80.0, 30.0, 1000.0, * -80.0, 30.0, 0.0 * ]) * }); * var geometry = Cesium.CoplanarPolygonOutlineGeometry.createGeometry(polygonOutline); */ function CoplanarPolygonOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var polygonHierarchy = options.polygonHierarchy; //>>includeStart('debug', pragmas.debug); Check.defined("options.polygonHierarchy", polygonHierarchy); //>>includeEnd('debug'); this._polygonHierarchy = polygonHierarchy; this._workerName = "createCoplanarPolygonOutlineGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + 1; } /** * A description of a coplanar polygon outline from an array of positions. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that defined the corner points of the polygon. * @returns {CoplanarPolygonOutlineGeometry} */ CoplanarPolygonOutlineGeometry.fromPositions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", options.positions); //>>includeEnd('debug'); var newOptions = { polygonHierarchy: { positions: options.positions, }, }; return new CoplanarPolygonOutlineGeometry(newOptions); }; /** * Stores the provided instance into the provided array. * * @param {CoplanarPolygonOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CoplanarPolygonOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); startingIndex = PolygonGeometryLibrary.packPolygonHierarchy( value._polygonHierarchy, array, startingIndex ); array[startingIndex] = value.packedLength; return array; }; var scratchOptions$g = { polygonHierarchy: {}, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CoplanarPolygonOutlineGeometry} [result] The object into which to store the result. * @returns {CoplanarPolygonOutlineGeometry} The modified result parameter or a new CoplanarPolygonOutlineGeometry instance if one was not provided. */ CoplanarPolygonOutlineGeometry.unpack = function ( array, startingIndex, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var polygonHierarchy = PolygonGeometryLibrary.unpackPolygonHierarchy( array, startingIndex ); startingIndex = polygonHierarchy.startingIndex; delete polygonHierarchy.startingIndex; var packedLength = array[startingIndex]; if (!defined(result)) { result = new CoplanarPolygonOutlineGeometry(scratchOptions$g); } result._polygonHierarchy = polygonHierarchy; result.packedLength = packedLength; return result; }; /** * Computes the geometric representation of an arbitrary coplanar polygon, including its vertices, indices, and a bounding sphere. * * @param {CoplanarPolygonOutlineGeometry} polygonGeometry A description of the polygon. * @returns {Geometry|undefined} The computed vertices and indices. */ CoplanarPolygonOutlineGeometry.createGeometry = function (polygonGeometry) { var polygonHierarchy = polygonGeometry._polygonHierarchy; var outerPositions = polygonHierarchy.positions; outerPositions = arrayRemoveDuplicates( outerPositions, Cartesian3.equalsEpsilon, true ); if (outerPositions.length < 3) { return; } var isValid = CoplanarPolygonGeometryLibrary.validOutline(outerPositions); if (!isValid) { return undefined; } var polygons = PolygonGeometryLibrary.polygonOutlinesFromHierarchy( polygonHierarchy, false ); if (polygons.length === 0) { return undefined; } var geometries = []; for (var i = 0; i < polygons.length; i++) { var geometryInstance = new GeometryInstance({ geometry: createGeometryFromPositions$1(polygons[i]), }); geometries.push(geometryInstance); } var geometry = GeometryPipeline.combineInstances(geometries)[0]; var boundingSphere = BoundingSphere.fromPoints(polygonHierarchy.positions); return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: geometry.primitiveType, boundingSphere: boundingSphere, }); }; /** * Style options for corners. * * @demo The {@link https://sandcastle.cesium.com/index.html?src=Corridor.html&label=Geometries|Corridor Demo} * demonstrates the three corner types, as used by {@link CorridorGraphics}. * * @enum {Number} */ var CornerType = { /** * * * Corner has a smooth edge. * @type {Number} * @constant */ ROUNDED: 0, /** * * * Corner point is the intersection of adjacent edges. * @type {Number} * @constant */ MITERED: 1, /** * * * Corner is clipped. * @type {Number} * @constant */ BEVELED: 2, }; var CornerType$1 = Object.freeze(CornerType); function setConstants(ellipsoidGeodesic) { var uSquared = ellipsoidGeodesic._uSquared; var a = ellipsoidGeodesic._ellipsoid.maximumRadius; var b = ellipsoidGeodesic._ellipsoid.minimumRadius; var f = (a - b) / a; var cosineHeading = Math.cos(ellipsoidGeodesic._startHeading); var sineHeading = Math.sin(ellipsoidGeodesic._startHeading); var tanU = (1 - f) * Math.tan(ellipsoidGeodesic._start.latitude); var cosineU = 1.0 / Math.sqrt(1.0 + tanU * tanU); var sineU = cosineU * tanU; var sigma = Math.atan2(tanU, cosineHeading); var sineAlpha = cosineU * sineHeading; var sineSquaredAlpha = sineAlpha * sineAlpha; var cosineSquaredAlpha = 1.0 - sineSquaredAlpha; var cosineAlpha = Math.sqrt(cosineSquaredAlpha); var u2Over4 = uSquared / 4.0; var u4Over16 = u2Over4 * u2Over4; var u6Over64 = u4Over16 * u2Over4; var u8Over256 = u4Over16 * u4Over16; var a0 = 1.0 + u2Over4 - (3.0 * u4Over16) / 4.0 + (5.0 * u6Over64) / 4.0 - (175.0 * u8Over256) / 64.0; var a1 = 1.0 - u2Over4 + (15.0 * u4Over16) / 8.0 - (35.0 * u6Over64) / 8.0; var a2 = 1.0 - 3.0 * u2Over4 + (35.0 * u4Over16) / 4.0; var a3 = 1.0 - 5.0 * u2Over4; var distanceRatio = a0 * sigma - (a1 * Math.sin(2.0 * sigma) * u2Over4) / 2.0 - (a2 * Math.sin(4.0 * sigma) * u4Over16) / 16.0 - (a3 * Math.sin(6.0 * sigma) * u6Over64) / 48.0 - (Math.sin(8.0 * sigma) * 5.0 * u8Over256) / 512; var constants = ellipsoidGeodesic._constants; constants.a = a; constants.b = b; constants.f = f; constants.cosineHeading = cosineHeading; constants.sineHeading = sineHeading; constants.tanU = tanU; constants.cosineU = cosineU; constants.sineU = sineU; constants.sigma = sigma; constants.sineAlpha = sineAlpha; constants.sineSquaredAlpha = sineSquaredAlpha; constants.cosineSquaredAlpha = cosineSquaredAlpha; constants.cosineAlpha = cosineAlpha; constants.u2Over4 = u2Over4; constants.u4Over16 = u4Over16; constants.u6Over64 = u6Over64; constants.u8Over256 = u8Over256; constants.a0 = a0; constants.a1 = a1; constants.a2 = a2; constants.a3 = a3; constants.distanceRatio = distanceRatio; } function computeC(f, cosineSquaredAlpha) { return ( (f * cosineSquaredAlpha * (4.0 + f * (4.0 - 3.0 * cosineSquaredAlpha))) / 16.0 ); } function computeDeltaLambda( f, sineAlpha, cosineSquaredAlpha, sigma, sineSigma, cosineSigma, cosineTwiceSigmaMidpoint ) { var C = computeC(f, cosineSquaredAlpha); return ( (1.0 - C) * f * sineAlpha * (sigma + C * sineSigma * (cosineTwiceSigmaMidpoint + C * cosineSigma * (2.0 * cosineTwiceSigmaMidpoint * cosineTwiceSigmaMidpoint - 1.0))) ); } function vincentyInverseFormula( ellipsoidGeodesic, major, minor, firstLongitude, firstLatitude, secondLongitude, secondLatitude ) { var eff = (major - minor) / major; var l = secondLongitude - firstLongitude; var u1 = Math.atan((1 - eff) * Math.tan(firstLatitude)); var u2 = Math.atan((1 - eff) * Math.tan(secondLatitude)); var cosineU1 = Math.cos(u1); var sineU1 = Math.sin(u1); var cosineU2 = Math.cos(u2); var sineU2 = Math.sin(u2); var cc = cosineU1 * cosineU2; var cs = cosineU1 * sineU2; var ss = sineU1 * sineU2; var sc = sineU1 * cosineU2; var lambda = l; var lambdaDot = CesiumMath.TWO_PI; var cosineLambda = Math.cos(lambda); var sineLambda = Math.sin(lambda); var sigma; var cosineSigma; var sineSigma; var cosineSquaredAlpha; var cosineTwiceSigmaMidpoint; do { cosineLambda = Math.cos(lambda); sineLambda = Math.sin(lambda); var temp = cs - sc * cosineLambda; sineSigma = Math.sqrt( cosineU2 * cosineU2 * sineLambda * sineLambda + temp * temp ); cosineSigma = ss + cc * cosineLambda; sigma = Math.atan2(sineSigma, cosineSigma); var sineAlpha; if (sineSigma === 0.0) { sineAlpha = 0.0; cosineSquaredAlpha = 1.0; } else { sineAlpha = (cc * sineLambda) / sineSigma; cosineSquaredAlpha = 1.0 - sineAlpha * sineAlpha; } lambdaDot = lambda; cosineTwiceSigmaMidpoint = cosineSigma - (2.0 * ss) / cosineSquaredAlpha; if (!isFinite(cosineTwiceSigmaMidpoint)) { cosineTwiceSigmaMidpoint = 0.0; } lambda = l + computeDeltaLambda( eff, sineAlpha, cosineSquaredAlpha, sigma, sineSigma, cosineSigma, cosineTwiceSigmaMidpoint ); } while (Math.abs(lambda - lambdaDot) > CesiumMath.EPSILON12); var uSquared = (cosineSquaredAlpha * (major * major - minor * minor)) / (minor * minor); var A = 1.0 + (uSquared * (4096.0 + uSquared * (uSquared * (320.0 - 175.0 * uSquared) - 768.0))) / 16384.0; var B = (uSquared * (256.0 + uSquared * (uSquared * (74.0 - 47.0 * uSquared) - 128.0))) / 1024.0; var cosineSquaredTwiceSigmaMidpoint = cosineTwiceSigmaMidpoint * cosineTwiceSigmaMidpoint; var deltaSigma = B * sineSigma * (cosineTwiceSigmaMidpoint + (B * (cosineSigma * (2.0 * cosineSquaredTwiceSigmaMidpoint - 1.0) - (B * cosineTwiceSigmaMidpoint * (4.0 * sineSigma * sineSigma - 3.0) * (4.0 * cosineSquaredTwiceSigmaMidpoint - 3.0)) / 6.0)) / 4.0); var distance = minor * A * (sigma - deltaSigma); var startHeading = Math.atan2(cosineU2 * sineLambda, cs - sc * cosineLambda); var endHeading = Math.atan2(cosineU1 * sineLambda, cs * cosineLambda - sc); ellipsoidGeodesic._distance = distance; ellipsoidGeodesic._startHeading = startHeading; ellipsoidGeodesic._endHeading = endHeading; ellipsoidGeodesic._uSquared = uSquared; } var scratchCart1 = new Cartesian3(); var scratchCart2$1 = new Cartesian3(); function computeProperties(ellipsoidGeodesic, start, end, ellipsoid) { var firstCartesian = Cartesian3.normalize( ellipsoid.cartographicToCartesian(start, scratchCart2$1), scratchCart1 ); var lastCartesian = Cartesian3.normalize( ellipsoid.cartographicToCartesian(end, scratchCart2$1), scratchCart2$1 ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals( "value", Math.abs( Math.abs(Cartesian3.angleBetween(firstCartesian, lastCartesian)) - Math.PI ), 0.0125 ); //>>includeEnd('debug'); vincentyInverseFormula( ellipsoidGeodesic, ellipsoid.maximumRadius, ellipsoid.minimumRadius, start.longitude, start.latitude, end.longitude, end.latitude ); ellipsoidGeodesic._start = Cartographic.clone( start, ellipsoidGeodesic._start ); ellipsoidGeodesic._end = Cartographic.clone(end, ellipsoidGeodesic._end); ellipsoidGeodesic._start.height = 0; ellipsoidGeodesic._end.height = 0; setConstants(ellipsoidGeodesic); } /** * Initializes a geodesic on the ellipsoid connecting the two provided planetodetic points. * * @alias EllipsoidGeodesic * @constructor * * @param {Cartographic} [start] The initial planetodetic point on the path. * @param {Cartographic} [end] The final planetodetic point on the path. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the geodesic lies. */ function EllipsoidGeodesic(start, end, ellipsoid) { var e = defaultValue(ellipsoid, Ellipsoid.WGS84); this._ellipsoid = e; this._start = new Cartographic(); this._end = new Cartographic(); this._constants = {}; this._startHeading = undefined; this._endHeading = undefined; this._distance = undefined; this._uSquared = undefined; if (defined(start) && defined(end)) { computeProperties(this, start, end, e); } } Object.defineProperties(EllipsoidGeodesic.prototype, { /** * Gets the ellipsoid. * @memberof EllipsoidGeodesic.prototype * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Gets the surface distance between the start and end point * @memberof EllipsoidGeodesic.prototype * @type {Number} * @readonly */ surfaceDistance: { get: function () { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); return this._distance; }, }, /** * Gets the initial planetodetic point on the path. * @memberof EllipsoidGeodesic.prototype * @type {Cartographic} * @readonly */ start: { get: function () { return this._start; }, }, /** * Gets the final planetodetic point on the path. * @memberof EllipsoidGeodesic.prototype * @type {Cartographic} * @readonly */ end: { get: function () { return this._end; }, }, /** * Gets the heading at the initial point. * @memberof EllipsoidGeodesic.prototype * @type {Number} * @readonly */ startHeading: { get: function () { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); return this._startHeading; }, }, /** * Gets the heading at the final point. * @memberof EllipsoidGeodesic.prototype * @type {Number} * @readonly */ endHeading: { get: function () { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); return this._endHeading; }, }, }); /** * Sets the start and end points of the geodesic * * @param {Cartographic} start The initial planetodetic point on the path. * @param {Cartographic} end The final planetodetic point on the path. */ EllipsoidGeodesic.prototype.setEndPoints = function (start, end) { //>>includeStart('debug', pragmas.debug); Check.defined("start", start); Check.defined("end", end); //>>includeEnd('debug'); computeProperties(this, start, end, this._ellipsoid); }; /** * Provides the location of a point at the indicated portion along the geodesic. * * @param {Number} fraction The portion of the distance between the initial and final points. * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the geodesic. */ EllipsoidGeodesic.prototype.interpolateUsingFraction = function ( fraction, result ) { return this.interpolateUsingSurfaceDistance( this._distance * fraction, result ); }; /** * Provides the location of a point at the indicated distance along the geodesic. * * @param {Number} distance The distance from the inital point to the point of interest along the geodesic * @param {Cartographic} [result] The object in which to store the result. * @returns {Cartographic} The location of the point along the geodesic. * * @exception {DeveloperError} start and end must be set before calling function interpolateUsingSurfaceDistance */ EllipsoidGeodesic.prototype.interpolateUsingSurfaceDistance = function ( distance, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("distance", this._distance); //>>includeEnd('debug'); var constants = this._constants; var s = constants.distanceRatio + distance / constants.b; var cosine2S = Math.cos(2.0 * s); var cosine4S = Math.cos(4.0 * s); var cosine6S = Math.cos(6.0 * s); var sine2S = Math.sin(2.0 * s); var sine4S = Math.sin(4.0 * s); var sine6S = Math.sin(6.0 * s); var sine8S = Math.sin(8.0 * s); var s2 = s * s; var s3 = s * s2; var u8Over256 = constants.u8Over256; var u2Over4 = constants.u2Over4; var u6Over64 = constants.u6Over64; var u4Over16 = constants.u4Over16; var sigma = (2.0 * s3 * u8Over256 * cosine2S) / 3.0 + s * (1.0 - u2Over4 + (7.0 * u4Over16) / 4.0 - (15.0 * u6Over64) / 4.0 + (579.0 * u8Over256) / 64.0 - (u4Over16 - (15.0 * u6Over64) / 4.0 + (187.0 * u8Over256) / 16.0) * cosine2S - ((5.0 * u6Over64) / 4.0 - (115.0 * u8Over256) / 16.0) * cosine4S - (29.0 * u8Over256 * cosine6S) / 16.0) + (u2Over4 / 2.0 - u4Over16 + (71.0 * u6Over64) / 32.0 - (85.0 * u8Over256) / 16.0) * sine2S + ((5.0 * u4Over16) / 16.0 - (5.0 * u6Over64) / 4.0 + (383.0 * u8Over256) / 96.0) * sine4S - s2 * ((u6Over64 - (11.0 * u8Over256) / 2.0) * sine2S + (5.0 * u8Over256 * sine4S) / 2.0) + ((29.0 * u6Over64) / 96.0 - (29.0 * u8Over256) / 16.0) * sine6S + (539.0 * u8Over256 * sine8S) / 1536.0; var theta = Math.asin(Math.sin(sigma) * constants.cosineAlpha); var latitude = Math.atan((constants.a / constants.b) * Math.tan(theta)); // Redefine in terms of relative argument of latitude. sigma = sigma - constants.sigma; var cosineTwiceSigmaMidpoint = Math.cos(2.0 * constants.sigma + sigma); var sineSigma = Math.sin(sigma); var cosineSigma = Math.cos(sigma); var cc = constants.cosineU * cosineSigma; var ss = constants.sineU * sineSigma; var lambda = Math.atan2( sineSigma * constants.sineHeading, cc - ss * constants.cosineHeading ); var l = lambda - computeDeltaLambda( constants.f, constants.sineAlpha, constants.cosineSquaredAlpha, sigma, sineSigma, cosineSigma, cosineTwiceSigmaMidpoint ); if (defined(result)) { result.longitude = this._start.longitude + l; result.latitude = latitude; result.height = 0.0; return result; } return new Cartographic(this._start.longitude + l, latitude, 0.0); }; /** * @private */ var PolylinePipeline = {}; PolylinePipeline.numberOfPoints = function (p0, p1, minDistance) { var distance = Cartesian3.distance(p0, p1); return Math.ceil(distance / minDistance); }; PolylinePipeline.numberOfPointsRhumbLine = function (p0, p1, granularity) { var radiansDistanceSquared = Math.pow(p0.longitude - p1.longitude, 2) + Math.pow(p0.latitude - p1.latitude, 2); return Math.max( 1, Math.ceil(Math.sqrt(radiansDistanceSquared / (granularity * granularity))) ); }; var cartoScratch$2 = new Cartographic(); PolylinePipeline.extractHeights = function (positions, ellipsoid) { var length = positions.length; var heights = new Array(length); for (var i = 0; i < length; i++) { var p = positions[i]; heights[i] = ellipsoid.cartesianToCartographic(p, cartoScratch$2).height; } return heights; }; var wrapLongitudeInversMatrix = new Matrix4(); var wrapLongitudeOrigin = new Cartesian3(); var wrapLongitudeXZNormal = new Cartesian3(); var wrapLongitudeXZPlane = new Plane(Cartesian3.UNIT_X, 0.0); var wrapLongitudeYZNormal = new Cartesian3(); var wrapLongitudeYZPlane = new Plane(Cartesian3.UNIT_X, 0.0); var wrapLongitudeIntersection = new Cartesian3(); var wrapLongitudeOffset = new Cartesian3(); var subdivideHeightsScratchArray = []; function subdivideHeights$1(numPoints, h0, h1) { var heights = subdivideHeightsScratchArray; heights.length = numPoints; var i; if (h0 === h1) { for (i = 0; i < numPoints; i++) { heights[i] = h0; } return heights; } var dHeight = h1 - h0; var heightPerVertex = dHeight / numPoints; for (i = 0; i < numPoints; i++) { var h = h0 + i * heightPerVertex; heights[i] = h; } return heights; } var carto1 = new Cartographic(); var carto2 = new Cartographic(); var cartesian = new Cartesian3(); var scaleFirst = new Cartesian3(); var scaleLast = new Cartesian3(); var ellipsoidGeodesic$1 = new EllipsoidGeodesic(); var ellipsoidRhumb = new EllipsoidRhumbLine(); //Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2. //Result includes p1, but not include p2. This function is called for a sequence of line segments, //and this prevents duplication of end point. function generateCartesianArc( p0, p1, minDistance, ellipsoid, h0, h1, array, offset ) { var first = ellipsoid.scaleToGeodeticSurface(p0, scaleFirst); var last = ellipsoid.scaleToGeodeticSurface(p1, scaleLast); var numPoints = PolylinePipeline.numberOfPoints(p0, p1, minDistance); var start = ellipsoid.cartesianToCartographic(first, carto1); var end = ellipsoid.cartesianToCartographic(last, carto2); var heights = subdivideHeights$1(numPoints, h0, h1); ellipsoidGeodesic$1.setEndPoints(start, end); var surfaceDistanceBetweenPoints = ellipsoidGeodesic$1.surfaceDistance / numPoints; var index = offset; start.height = h0; var cart = ellipsoid.cartographicToCartesian(start, cartesian); Cartesian3.pack(cart, array, index); index += 3; for (var i = 1; i < numPoints; i++) { var carto = ellipsoidGeodesic$1.interpolateUsingSurfaceDistance( i * surfaceDistanceBetweenPoints, carto2 ); carto.height = heights[i]; cart = ellipsoid.cartographicToCartesian(carto, cartesian); Cartesian3.pack(cart, array, index); index += 3; } return index; } //Returns subdivided line scaled to ellipsoid surface starting at p1 and ending at p2. //Result includes p1, but not include p2. This function is called for a sequence of line segments, //and this prevents duplication of end point. function generateCartesianRhumbArc( p0, p1, granularity, ellipsoid, h0, h1, array, offset ) { var start = ellipsoid.cartesianToCartographic(p0, carto1); var end = ellipsoid.cartesianToCartographic(p1, carto2); var numPoints = PolylinePipeline.numberOfPointsRhumbLine( start, end, granularity ); start.height = 0.0; end.height = 0.0; var heights = subdivideHeights$1(numPoints, h0, h1); if (!ellipsoidRhumb.ellipsoid.equals(ellipsoid)) { ellipsoidRhumb = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); } ellipsoidRhumb.setEndPoints(start, end); var surfaceDistanceBetweenPoints = ellipsoidRhumb.surfaceDistance / numPoints; var index = offset; start.height = h0; var cart = ellipsoid.cartographicToCartesian(start, cartesian); Cartesian3.pack(cart, array, index); index += 3; for (var i = 1; i < numPoints; i++) { var carto = ellipsoidRhumb.interpolateUsingSurfaceDistance( i * surfaceDistanceBetweenPoints, carto2 ); carto.height = heights[i]; cart = ellipsoid.cartographicToCartesian(carto, cartesian); Cartesian3.pack(cart, array, index); index += 3; } return index; } /** * Breaks a {@link Polyline} into segments such that it does not cross the ±180 degree meridian of an ellipsoid. * * @param {Cartesian3[]} positions The polyline's Cartesian positions. * @param {Matrix4} [modelMatrix=Matrix4.IDENTITY] The polyline's model matrix. Assumed to be 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. * @returns {Object} An object with a positions property that is an array of positions and a * segments property. * * * @example * var polylines = new Cesium.PolylineCollection(); * var polyline = polylines.add(...); * var positions = polyline.positions; * var modelMatrix = polylines.modelMatrix; * var segments = Cesium.PolylinePipeline.wrapLongitude(positions, modelMatrix); * * @see PolygonPipeline.wrapLongitude * @see Polyline * @see PolylineCollection */ PolylinePipeline.wrapLongitude = function (positions, modelMatrix) { var cartesians = []; var segments = []; if (defined(positions) && positions.length > 0) { modelMatrix = defaultValue(modelMatrix, Matrix4.IDENTITY); var inverseModelMatrix = Matrix4.inverseTransformation( modelMatrix, wrapLongitudeInversMatrix ); var origin = Matrix4.multiplyByPoint( inverseModelMatrix, Cartesian3.ZERO, wrapLongitudeOrigin ); var xzNormal = Cartesian3.normalize( Matrix4.multiplyByPointAsVector( inverseModelMatrix, Cartesian3.UNIT_Y, wrapLongitudeXZNormal ), wrapLongitudeXZNormal ); var xzPlane = Plane.fromPointNormal(origin, xzNormal, wrapLongitudeXZPlane); var yzNormal = Cartesian3.normalize( Matrix4.multiplyByPointAsVector( inverseModelMatrix, Cartesian3.UNIT_X, wrapLongitudeYZNormal ), wrapLongitudeYZNormal ); var yzPlane = Plane.fromPointNormal(origin, yzNormal, wrapLongitudeYZPlane); var count = 1; cartesians.push(Cartesian3.clone(positions[0])); var prev = cartesians[0]; var length = positions.length; for (var i = 1; i < length; ++i) { var cur = positions[i]; // intersects the IDL if either endpoint is on the negative side of the yz-plane if ( Plane.getPointDistance(yzPlane, prev) < 0.0 || Plane.getPointDistance(yzPlane, cur) < 0.0 ) { // and intersects the xz-plane var intersection = IntersectionTests.lineSegmentPlane( prev, cur, xzPlane, wrapLongitudeIntersection ); if (defined(intersection)) { // move point on the xz-plane slightly away from the plane var offset = Cartesian3.multiplyByScalar( xzNormal, 5.0e-9, wrapLongitudeOffset ); if (Plane.getPointDistance(xzPlane, prev) < 0.0) { Cartesian3.negate(offset, offset); } cartesians.push( Cartesian3.add(intersection, offset, new Cartesian3()) ); segments.push(count + 1); Cartesian3.negate(offset, offset); cartesians.push( Cartesian3.add(intersection, offset, new Cartesian3()) ); count = 1; } } cartesians.push(Cartesian3.clone(positions[i])); count++; prev = cur; } segments.push(count); } return { positions: cartesians, lengths: segments, }; }; /** * Subdivides polyline and raises all points to the specified height. Returns an array of numbers to represent the positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @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 {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Number[]} A new array of positions of type {Number} that have been subdivided and raised to the surface of the ellipsoid. * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([ * -105.0, 40.0, * -100.0, 38.0, * -105.0, 35.0, * -100.0, 32.0 * ]); * var surfacePositions = Cesium.PolylinePipeline.generateArc({ * positons: positions * }); */ PolylinePipeline.generateArc = function (options) { if (!defined(options)) { options = {}; } var positions = options.positions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.positions is required."); } //>>includeEnd('debug'); var length = positions.length; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var height = defaultValue(options.height, 0); var hasHeightArray = Array.isArray(height); if (length < 1) { return []; } else if (length === 1) { var p = ellipsoid.scaleToGeodeticSurface(positions[0], scaleFirst); height = hasHeightArray ? height[0] : height; if (height !== 0) { var n = ellipsoid.geodeticSurfaceNormal(p, cartesian); Cartesian3.multiplyByScalar(n, height, n); Cartesian3.add(p, n, p); } return [p.x, p.y, p.z]; } var minDistance = options.minDistance; if (!defined(minDistance)) { var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius); } var numPoints = 0; var i; for (i = 0; i < length - 1; i++) { numPoints += PolylinePipeline.numberOfPoints( positions[i], positions[i + 1], minDistance ); } var arrayLength = (numPoints + 1) * 3; var newPositions = new Array(arrayLength); var offset = 0; for (i = 0; i < length - 1; i++) { var p0 = positions[i]; var p1 = positions[i + 1]; var h0 = hasHeightArray ? height[i] : height; var h1 = hasHeightArray ? height[i + 1] : height; offset = generateCartesianArc( p0, p1, minDistance, ellipsoid, h0, h1, newPositions, offset ); } subdivideHeightsScratchArray.length = 0; var lastPoint = positions[length - 1]; var carto = ellipsoid.cartesianToCartographic(lastPoint, carto1); carto.height = hasHeightArray ? height[length - 1] : height; var cart = ellipsoid.cartographicToCartesian(carto, cartesian); Cartesian3.pack(cart, newPositions, arrayLength - 3); return newPositions; }; var scratchCartographic0 = new Cartographic(); var scratchCartographic1$1 = new Cartographic(); /** * Subdivides polyline and raises all points to the specified height using Rhumb lines. Returns an array of numbers to represent the positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @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 {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Number[]} A new array of positions of type {Number} that have been subdivided and raised to the surface of the ellipsoid. * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([ * -105.0, 40.0, * -100.0, 38.0, * -105.0, 35.0, * -100.0, 32.0 * ]); * var surfacePositions = Cesium.PolylinePipeline.generateRhumbArc({ * positons: positions * }); */ PolylinePipeline.generateRhumbArc = function (options) { if (!defined(options)) { options = {}; } var positions = options.positions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.positions is required."); } //>>includeEnd('debug'); var length = positions.length; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var height = defaultValue(options.height, 0); var hasHeightArray = Array.isArray(height); if (length < 1) { return []; } else if (length === 1) { var p = ellipsoid.scaleToGeodeticSurface(positions[0], scaleFirst); height = hasHeightArray ? height[0] : height; if (height !== 0) { var n = ellipsoid.geodeticSurfaceNormal(p, cartesian); Cartesian3.multiplyByScalar(n, height, n); Cartesian3.add(p, n, p); } return [p.x, p.y, p.z]; } var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var numPoints = 0; var i; var c0 = ellipsoid.cartesianToCartographic( positions[0], scratchCartographic0 ); var c1; for (i = 0; i < length - 1; i++) { c1 = ellipsoid.cartesianToCartographic( positions[i + 1], scratchCartographic1$1 ); numPoints += PolylinePipeline.numberOfPointsRhumbLine(c0, c1, granularity); c0 = Cartographic.clone(c1, scratchCartographic0); } var arrayLength = (numPoints + 1) * 3; var newPositions = new Array(arrayLength); var offset = 0; for (i = 0; i < length - 1; i++) { var p0 = positions[i]; var p1 = positions[i + 1]; var h0 = hasHeightArray ? height[i] : height; var h1 = hasHeightArray ? height[i + 1] : height; offset = generateCartesianRhumbArc( p0, p1, granularity, ellipsoid, h0, h1, newPositions, offset ); } subdivideHeightsScratchArray.length = 0; var lastPoint = positions[length - 1]; var carto = ellipsoid.cartesianToCartographic(lastPoint, carto1); carto.height = hasHeightArray ? height[length - 1] : height; var cart = ellipsoid.cartographicToCartesian(carto, cartesian); Cartesian3.pack(cart, newPositions, arrayLength - 3); return newPositions; }; /** * Subdivides polyline and raises all points to the specified height. Returns an array of new {Cartesian3} positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @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 {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Cartesian3[]} A new array of cartesian3 positions that have been subdivided and raised to the surface of the ellipsoid. * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([ * -105.0, 40.0, * -100.0, 38.0, * -105.0, 35.0, * -100.0, 32.0 * ]); * var surfacePositions = Cesium.PolylinePipeline.generateCartesianArc({ * positons: positions * }); */ PolylinePipeline.generateCartesianArc = function (options) { var numberArray = PolylinePipeline.generateArc(options); var size = numberArray.length / 3; var newPositions = new Array(size); for (var i = 0; i < size; i++) { newPositions[i] = Cartesian3.unpack(numberArray, i * 3); } return newPositions; }; /** * Subdivides polyline and raises all points to the specified height using Rhumb Lines. Returns an array of new {Cartesian3} positions. * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions The array of type {Cartesian3} representing positions. * @param {Number|Number[]} [options.height=0.0] A number or array of numbers representing the heights of each position. * @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 {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the positions lie. * @returns {Cartesian3[]} A new array of cartesian3 positions that have been subdivided and raised to the surface of the ellipsoid. * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([ * -105.0, 40.0, * -100.0, 38.0, * -105.0, 35.0, * -100.0, 32.0 * ]); * var surfacePositions = Cesium.PolylinePipeline.generateCartesianRhumbArc({ * positons: positions * }); */ PolylinePipeline.generateCartesianRhumbArc = function (options) { var numberArray = PolylinePipeline.generateRhumbArc(options); var size = numberArray.length / 3; var newPositions = new Array(size); for (var i = 0; i < size; i++) { newPositions[i] = Cartesian3.unpack(numberArray, i * 3); } return newPositions; }; var scratch2Array = [new Cartesian3(), new Cartesian3()]; var scratchCartesian1$3 = new Cartesian3(); var scratchCartesian2$6 = new Cartesian3(); var scratchCartesian3$7 = new Cartesian3(); var scratchCartesian4$4 = new Cartesian3(); var scratchCartesian5$1 = new Cartesian3(); var scratchCartesian6 = new Cartesian3(); var scratchCartesian7 = new Cartesian3(); var scratchCartesian8 = new Cartesian3(); var scratchCartesian9 = new Cartesian3(); var scratch1$2 = new Cartesian3(); var scratch2$2 = new Cartesian3(); /** * @private */ var PolylineVolumeGeometryLibrary = {}; var cartographic = new Cartographic(); function scaleToSurface$2(positions, ellipsoid) { var heights = new Array(positions.length); for (var i = 0; i < positions.length; i++) { var pos = positions[i]; cartographic = ellipsoid.cartesianToCartographic(pos, cartographic); heights[i] = cartographic.height; positions[i] = ellipsoid.scaleToGeodeticSurface(pos, pos); } return heights; } function subdivideHeights(points, h0, h1, granularity) { var p0 = points[0]; var p1 = points[1]; var angleBetween = Cartesian3.angleBetween(p0, p1); var numPoints = Math.ceil(angleBetween / granularity); var heights = new Array(numPoints); var i; if (h0 === h1) { for (i = 0; i < numPoints; i++) { heights[i] = h0; } heights.push(h1); return heights; } var dHeight = h1 - h0; var heightPerVertex = dHeight / numPoints; for (i = 1; i < numPoints; i++) { var h = h0 + i * heightPerVertex; heights[i] = h; } heights[0] = h0; heights.push(h1); return heights; } var nextScratch = new Cartesian3(); var prevScratch = new Cartesian3(); function computeRotationAngle(start, end, position, ellipsoid) { var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid); var next = tangentPlane.projectPointOntoPlane( Cartesian3.add(position, start, nextScratch), nextScratch ); var prev = tangentPlane.projectPointOntoPlane( Cartesian3.add(position, end, prevScratch), prevScratch ); var angle = Cartesian2.angleBetween(next, prev); return prev.x * next.y - prev.y * next.x >= 0.0 ? -angle : angle; } var negativeX = new Cartesian3(-1, 0, 0); var transform$1 = new Matrix4(); var translation$1 = new Matrix4(); var rotationZ = new Matrix3(); var scaleMatrix = Matrix3.IDENTITY.clone(); var westScratch = new Cartesian3(); var finalPosScratch = new Cartesian4(); var heightCartesian = new Cartesian3(); function addPosition( center, left, shape, finalPositions, ellipsoid, height, xScalar, repeat ) { var west = westScratch; var finalPosition = finalPosScratch; transform$1 = Transforms.eastNorthUpToFixedFrame(center, ellipsoid, transform$1); west = Matrix4.multiplyByPointAsVector(transform$1, negativeX, west); west = Cartesian3.normalize(west, west); var angle = computeRotationAngle(west, left, center, ellipsoid); rotationZ = Matrix3.fromRotationZ(angle, rotationZ); heightCartesian.z = height; transform$1 = Matrix4.multiplyTransformation( transform$1, Matrix4.fromRotationTranslation(rotationZ, heightCartesian, translation$1), transform$1 ); var scale = scaleMatrix; scale[0] = xScalar; for (var j = 0; j < repeat; j++) { for (var i = 0; i < shape.length; i += 3) { finalPosition = Cartesian3.fromArray(shape, i, finalPosition); finalPosition = Matrix3.multiplyByVector( scale, finalPosition, finalPosition ); finalPosition = Matrix4.multiplyByPoint( transform$1, finalPosition, finalPosition ); finalPositions.push(finalPosition.x, finalPosition.y, finalPosition.z); } } return finalPositions; } var centerScratch$5 = new Cartesian3(); function addPositions( centers, left, shape, finalPositions, ellipsoid, heights, xScalar ) { for (var i = 0; i < centers.length; i += 3) { var center = Cartesian3.fromArray(centers, i, centerScratch$5); finalPositions = addPosition( center, left, shape, finalPositions, ellipsoid, heights[i / 3], xScalar, 1 ); } return finalPositions; } function convertShapeTo3DDuplicate(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0), duplicate points var length = shape2D.length; var shape = new Array(length * 6); var index = 0; var xOffset = boundingRectangle.x + boundingRectangle.width / 2; var yOffset = boundingRectangle.y + boundingRectangle.height / 2; var point = shape2D[0]; shape[index++] = point.x - xOffset; shape[index++] = 0.0; shape[index++] = point.y - yOffset; for (var i = 1; i < length; i++) { point = shape2D[i]; var x = point.x - xOffset; var z = point.y - yOffset; shape[index++] = x; shape[index++] = 0.0; shape[index++] = z; shape[index++] = x; shape[index++] = 0.0; shape[index++] = z; } point = shape2D[0]; shape[index++] = point.x - xOffset; shape[index++] = 0.0; shape[index++] = point.y - yOffset; return shape; } function convertShapeTo3D(shape2D, boundingRectangle) { //orientate 2D shape to XZ plane center at (0, 0, 0) var length = shape2D.length; var shape = new Array(length * 3); var index = 0; var xOffset = boundingRectangle.x + boundingRectangle.width / 2; var yOffset = boundingRectangle.y + boundingRectangle.height / 2; for (var i = 0; i < length; i++) { shape[index++] = shape2D[i].x - xOffset; shape[index++] = 0; shape[index++] = shape2D[i].y - yOffset; } return shape; } var quaterion$1 = new Quaternion(); var startPointScratch = new Cartesian3(); var rotMatrix$1 = new Matrix3(); function computeRoundCorner$1( pivot, startPoint, endPoint, cornerType, leftIsOutside, ellipsoid, finalPositions, shape, height, duplicatePoints ) { var angle = Cartesian3.angleBetween( Cartesian3.subtract(startPoint, pivot, scratch1$2), Cartesian3.subtract(endPoint, pivot, scratch2$2) ); var granularity = cornerType === CornerType$1.BEVELED ? 0 : Math.ceil(angle / CesiumMath.toRadians(5)); var m; if (leftIsOutside) { m = Matrix3.fromQuaternion( Quaternion.fromAxisAngle( Cartesian3.negate(pivot, scratch1$2), angle / (granularity + 1), quaterion$1 ), rotMatrix$1 ); } else { m = Matrix3.fromQuaternion( Quaternion.fromAxisAngle(pivot, angle / (granularity + 1), quaterion$1), rotMatrix$1 ); } var left; var surfacePoint; startPoint = Cartesian3.clone(startPoint, startPointScratch); if (granularity > 0) { var repeat = duplicatePoints ? 2 : 1; for (var i = 0; i < granularity; i++) { startPoint = Matrix3.multiplyByVector(m, startPoint, startPoint); left = Cartesian3.subtract(startPoint, pivot, scratch1$2); left = Cartesian3.normalize(left, left); if (!leftIsOutside) { left = Cartesian3.negate(left, left); } surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2$2); finalPositions = addPosition( surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, repeat ); } } else { left = Cartesian3.subtract(startPoint, pivot, scratch1$2); left = Cartesian3.normalize(left, left); if (!leftIsOutside) { left = Cartesian3.negate(left, left); } surfacePoint = ellipsoid.scaleToGeodeticSurface(startPoint, scratch2$2); finalPositions = addPosition( surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1 ); endPoint = Cartesian3.clone(endPoint, startPointScratch); left = Cartesian3.subtract(endPoint, pivot, scratch1$2); left = Cartesian3.normalize(left, left); if (!leftIsOutside) { left = Cartesian3.negate(left, left); } surfacePoint = ellipsoid.scaleToGeodeticSurface(endPoint, scratch2$2); finalPositions = addPosition( surfacePoint, left, shape, finalPositions, ellipsoid, height, 1, 1 ); } return finalPositions; } PolylineVolumeGeometryLibrary.removeDuplicatesFromShape = function ( shapePositions ) { var length = shapePositions.length; var cleanedPositions = []; for (var i0 = length - 1, i1 = 0; i1 < length; i0 = i1++) { var v0 = shapePositions[i0]; var v1 = shapePositions[i1]; if (!Cartesian2.equals(v0, v1)) { cleanedPositions.push(v1); // Shallow copy! } } return cleanedPositions; }; PolylineVolumeGeometryLibrary.angleIsGreaterThanPi = function ( forward, backward, position, ellipsoid ) { var tangentPlane = new EllipsoidTangentPlane(position, ellipsoid); var next = tangentPlane.projectPointOntoPlane( Cartesian3.add(position, forward, nextScratch), nextScratch ); var prev = tangentPlane.projectPointOntoPlane( Cartesian3.add(position, backward, prevScratch), prevScratch ); return prev.x * next.y - prev.y * next.x >= 0.0; }; var scratchForwardProjection$1 = new Cartesian3(); var scratchBackwardProjection$1 = new Cartesian3(); PolylineVolumeGeometryLibrary.computePositions = function ( positions, shape2D, boundingRectangle, geometry, duplicatePoints ) { var ellipsoid = geometry._ellipsoid; var heights = scaleToSurface$2(positions, ellipsoid); var granularity = geometry._granularity; var cornerType = geometry._cornerType; var shapeForSides = duplicatePoints ? convertShapeTo3DDuplicate(shape2D, boundingRectangle) : convertShapeTo3D(shape2D, boundingRectangle); var shapeForEnds = duplicatePoints ? convertShapeTo3D(shape2D, boundingRectangle) : undefined; var heightOffset = boundingRectangle.height / 2; var width = boundingRectangle.width / 2; var length = positions.length; var finalPositions = []; var ends = duplicatePoints ? [] : undefined; var forward = scratchCartesian1$3; var backward = scratchCartesian2$6; var cornerDirection = scratchCartesian3$7; var surfaceNormal = scratchCartesian4$4; var pivot = scratchCartesian5$1; var start = scratchCartesian6; var end = scratchCartesian7; var left = scratchCartesian8; var previousPosition = scratchCartesian9; var position = positions[0]; var nextPosition = positions[1]; surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal); forward = Cartesian3.subtract(nextPosition, position, forward); forward = Cartesian3.normalize(forward, forward); left = Cartesian3.cross(surfaceNormal, forward, left); left = Cartesian3.normalize(left, left); var h0 = heights[0]; var h1 = heights[1]; if (duplicatePoints) { ends = addPosition( position, left, shapeForEnds, ends, ellipsoid, h0 + heightOffset, 1, 1 ); } previousPosition = Cartesian3.clone(position, previousPosition); position = nextPosition; backward = Cartesian3.negate(forward, backward); var subdividedHeights; var subdividedPositions; for (var i = 1; i < length - 1; i++) { var repeat = duplicatePoints ? 2 : 1; nextPosition = positions[i + 1]; forward = Cartesian3.subtract(nextPosition, position, forward); forward = Cartesian3.normalize(forward, forward); cornerDirection = Cartesian3.add(forward, backward, cornerDirection); cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection); surfaceNormal = ellipsoid.geodeticSurfaceNormal(position, surfaceNormal); var forwardProjection = Cartesian3.multiplyByScalar( surfaceNormal, Cartesian3.dot(forward, surfaceNormal), scratchForwardProjection$1 ); Cartesian3.subtract(forward, forwardProjection, forwardProjection); Cartesian3.normalize(forwardProjection, forwardProjection); var backwardProjection = Cartesian3.multiplyByScalar( surfaceNormal, Cartesian3.dot(backward, surfaceNormal), scratchBackwardProjection$1 ); Cartesian3.subtract(backward, backwardProjection, backwardProjection); Cartesian3.normalize(backwardProjection, backwardProjection); var doCorner = !CesiumMath.equalsEpsilon( Math.abs(Cartesian3.dot(forwardProjection, backwardProjection)), 1.0, CesiumMath.EPSILON7 ); if (doCorner) { cornerDirection = Cartesian3.cross( cornerDirection, surfaceNormal, cornerDirection ); cornerDirection = Cartesian3.cross( surfaceNormal, cornerDirection, cornerDirection ); cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection); var scalar = 1 / Math.max( 0.25, Cartesian3.magnitude( Cartesian3.cross(cornerDirection, backward, scratch1$2) ) ); var leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi( forward, backward, position, ellipsoid ); if (leftIsOutside) { pivot = Cartesian3.add( position, Cartesian3.multiplyByScalar( cornerDirection, scalar * width, cornerDirection ), pivot ); start = Cartesian3.add( pivot, Cartesian3.multiplyByScalar(left, width, start), start ); scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]); scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]); subdividedHeights = subdivideHeights( scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity ); subdividedPositions = PolylinePipeline.generateArc({ positions: scratch2Array, granularity: granularity, ellipsoid: ellipsoid, }); finalPositions = addPositions( subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1 ); left = Cartesian3.cross(surfaceNormal, forward, left); left = Cartesian3.normalize(left, left); end = Cartesian3.add( pivot, Cartesian3.multiplyByScalar(left, width, end), end ); if ( cornerType === CornerType$1.ROUNDED || cornerType === CornerType$1.BEVELED ) { computeRoundCorner$1( pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints ); } else { cornerDirection = Cartesian3.negate(cornerDirection, cornerDirection); finalPositions = addPosition( position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat ); } previousPosition = Cartesian3.clone(end, previousPosition); } else { pivot = Cartesian3.add( position, Cartesian3.multiplyByScalar( cornerDirection, scalar * width, cornerDirection ), pivot ); start = Cartesian3.add( pivot, Cartesian3.multiplyByScalar(left, -width, start), start ); scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]); scratch2Array[1] = Cartesian3.clone(start, scratch2Array[1]); subdividedHeights = subdivideHeights( scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity ); subdividedPositions = PolylinePipeline.generateArc({ positions: scratch2Array, granularity: granularity, ellipsoid: ellipsoid, }); finalPositions = addPositions( subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1 ); left = Cartesian3.cross(surfaceNormal, forward, left); left = Cartesian3.normalize(left, left); end = Cartesian3.add( pivot, Cartesian3.multiplyByScalar(left, -width, end), end ); if ( cornerType === CornerType$1.ROUNDED || cornerType === CornerType$1.BEVELED ) { computeRoundCorner$1( pivot, start, end, cornerType, leftIsOutside, ellipsoid, finalPositions, shapeForSides, h1 + heightOffset, duplicatePoints ); } else { finalPositions = addPosition( position, cornerDirection, shapeForSides, finalPositions, ellipsoid, h1 + heightOffset, scalar, repeat ); } previousPosition = Cartesian3.clone(end, previousPosition); } backward = Cartesian3.negate(forward, backward); } else { finalPositions = addPosition( previousPosition, left, shapeForSides, finalPositions, ellipsoid, h0 + heightOffset, 1, 1 ); previousPosition = position; } h0 = h1; h1 = heights[i + 1]; position = nextPosition; } scratch2Array[0] = Cartesian3.clone(previousPosition, scratch2Array[0]); scratch2Array[1] = Cartesian3.clone(position, scratch2Array[1]); subdividedHeights = subdivideHeights( scratch2Array, h0 + heightOffset, h1 + heightOffset, granularity ); subdividedPositions = PolylinePipeline.generateArc({ positions: scratch2Array, granularity: granularity, ellipsoid: ellipsoid, }); finalPositions = addPositions( subdividedPositions, left, shapeForSides, finalPositions, ellipsoid, subdividedHeights, 1 ); if (duplicatePoints) { ends = addPosition( position, left, shapeForEnds, ends, ellipsoid, h1 + heightOffset, 1, 1 ); } length = finalPositions.length; var posLength = duplicatePoints ? length + ends.length : length; var combinedPositions = new Float64Array(posLength); combinedPositions.set(finalPositions); if (duplicatePoints) { combinedPositions.set(ends, length); } return combinedPositions; }; /** * @private */ var CorridorGeometryLibrary = {}; var scratch1$1 = new Cartesian3(); var scratch2$1 = new Cartesian3(); var scratch3 = new Cartesian3(); var scratch4 = new Cartesian3(); var scaleArray2 = [new Cartesian3(), new Cartesian3()]; var cartesian1$2 = new Cartesian3(); var cartesian2$2 = new Cartesian3(); var cartesian3$2 = new Cartesian3(); var cartesian4$1 = new Cartesian3(); var cartesian5$1 = new Cartesian3(); var cartesian6$1 = new Cartesian3(); var cartesian7 = new Cartesian3(); var cartesian8 = new Cartesian3(); var cartesian9 = new Cartesian3(); var cartesian10 = new Cartesian3(); var quaterion = new Quaternion(); var rotMatrix = new Matrix3(); function computeRoundCorner( cornerPoint, startPoint, endPoint, cornerType, leftIsOutside ) { var angle = Cartesian3.angleBetween( Cartesian3.subtract(startPoint, cornerPoint, scratch1$1), Cartesian3.subtract(endPoint, cornerPoint, scratch2$1) ); var granularity = cornerType === CornerType$1.BEVELED ? 1 : Math.ceil(angle / CesiumMath.toRadians(5)) + 1; var size = granularity * 3; var array = new Array(size); array[size - 3] = endPoint.x; array[size - 2] = endPoint.y; array[size - 1] = endPoint.z; var m; if (leftIsOutside) { m = Matrix3.fromQuaternion( Quaternion.fromAxisAngle( Cartesian3.negate(cornerPoint, scratch1$1), angle / granularity, quaterion ), rotMatrix ); } else { m = Matrix3.fromQuaternion( Quaternion.fromAxisAngle(cornerPoint, angle / granularity, quaterion), rotMatrix ); } var index = 0; startPoint = Cartesian3.clone(startPoint, scratch1$1); for (var i = 0; i < granularity; i++) { startPoint = Matrix3.multiplyByVector(m, startPoint, startPoint); array[index++] = startPoint.x; array[index++] = startPoint.y; array[index++] = startPoint.z; } return array; } function addEndCaps(calculatedPositions) { var cornerPoint = cartesian1$2; var startPoint = cartesian2$2; var endPoint = cartesian3$2; var leftEdge = calculatedPositions[1]; startPoint = Cartesian3.fromArray( calculatedPositions[1], leftEdge.length - 3, startPoint ); endPoint = Cartesian3.fromArray(calculatedPositions[0], 0, endPoint); cornerPoint = Cartesian3.midpoint(startPoint, endPoint, cornerPoint); var firstEndCap = computeRoundCorner( cornerPoint, startPoint, endPoint, CornerType$1.ROUNDED, false ); var length = calculatedPositions.length - 1; var rightEdge = calculatedPositions[length - 1]; leftEdge = calculatedPositions[length]; startPoint = Cartesian3.fromArray( rightEdge, rightEdge.length - 3, startPoint ); endPoint = Cartesian3.fromArray(leftEdge, 0, endPoint); cornerPoint = Cartesian3.midpoint(startPoint, endPoint, cornerPoint); var lastEndCap = computeRoundCorner( cornerPoint, startPoint, endPoint, CornerType$1.ROUNDED, false ); return [firstEndCap, lastEndCap]; } function computeMiteredCorner( position, leftCornerDirection, lastPoint, leftIsOutside ) { var cornerPoint = scratch1$1; if (leftIsOutside) { cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); } else { leftCornerDirection = Cartesian3.negate( leftCornerDirection, leftCornerDirection ); cornerPoint = Cartesian3.add(position, leftCornerDirection, cornerPoint); } return [ cornerPoint.x, cornerPoint.y, cornerPoint.z, lastPoint.x, lastPoint.y, lastPoint.z, ]; } function addShiftedPositions(positions, left, scalar, calculatedPositions) { var rightPositions = new Array(positions.length); var leftPositions = new Array(positions.length); var scaledLeft = Cartesian3.multiplyByScalar(left, scalar, scratch1$1); var scaledRight = Cartesian3.negate(scaledLeft, scratch2$1); var rightIndex = 0; var leftIndex = positions.length - 1; for (var i = 0; i < positions.length; i += 3) { var pos = Cartesian3.fromArray(positions, i, scratch3); var rightPos = Cartesian3.add(pos, scaledRight, scratch4); rightPositions[rightIndex++] = rightPos.x; rightPositions[rightIndex++] = rightPos.y; rightPositions[rightIndex++] = rightPos.z; var leftPos = Cartesian3.add(pos, scaledLeft, scratch4); leftPositions[leftIndex--] = leftPos.z; leftPositions[leftIndex--] = leftPos.y; leftPositions[leftIndex--] = leftPos.x; } calculatedPositions.push(rightPositions, leftPositions); return calculatedPositions; } /** * @private */ CorridorGeometryLibrary.addAttribute = function ( attribute, value, front, back ) { var x = value.x; var y = value.y; var z = value.z; if (defined(front)) { attribute[front] = x; attribute[front + 1] = y; attribute[front + 2] = z; } if (defined(back)) { attribute[back] = z; attribute[back - 1] = y; attribute[back - 2] = x; } }; var scratchForwardProjection = new Cartesian3(); var scratchBackwardProjection = new Cartesian3(); /** * @private */ CorridorGeometryLibrary.computePositions = function (params) { var granularity = params.granularity; var positions = params.positions; var ellipsoid = params.ellipsoid; var width = params.width / 2; var cornerType = params.cornerType; var saveAttributes = params.saveAttributes; var normal = cartesian1$2; var forward = cartesian2$2; var backward = cartesian3$2; var left = cartesian4$1; var cornerDirection = cartesian5$1; var startPoint = cartesian6$1; var previousPos = cartesian7; var rightPos = cartesian8; var leftPos = cartesian9; var center = cartesian10; var calculatedPositions = []; var calculatedLefts = saveAttributes ? [] : undefined; var calculatedNormals = saveAttributes ? [] : undefined; var position = positions[0]; //add first point var nextPosition = positions[1]; forward = Cartesian3.normalize( Cartesian3.subtract(nextPosition, position, forward), forward ); normal = ellipsoid.geodeticSurfaceNormal(position, normal); left = Cartesian3.normalize(Cartesian3.cross(normal, forward, left), left); if (saveAttributes) { calculatedLefts.push(left.x, left.y, left.z); calculatedNormals.push(normal.x, normal.y, normal.z); } previousPos = Cartesian3.clone(position, previousPos); position = nextPosition; backward = Cartesian3.negate(forward, backward); var subdividedPositions; var corners = []; var i; var length = positions.length; for (i = 1; i < length - 1; i++) { // add middle points and corners normal = ellipsoid.geodeticSurfaceNormal(position, normal); nextPosition = positions[i + 1]; forward = Cartesian3.normalize( Cartesian3.subtract(nextPosition, position, forward), forward ); cornerDirection = Cartesian3.normalize( Cartesian3.add(forward, backward, cornerDirection), cornerDirection ); var forwardProjection = Cartesian3.multiplyByScalar( normal, Cartesian3.dot(forward, normal), scratchForwardProjection ); Cartesian3.subtract(forward, forwardProjection, forwardProjection); Cartesian3.normalize(forwardProjection, forwardProjection); var backwardProjection = Cartesian3.multiplyByScalar( normal, Cartesian3.dot(backward, normal), scratchBackwardProjection ); Cartesian3.subtract(backward, backwardProjection, backwardProjection); Cartesian3.normalize(backwardProjection, backwardProjection); var doCorner = !CesiumMath.equalsEpsilon( Math.abs(Cartesian3.dot(forwardProjection, backwardProjection)), 1.0, CesiumMath.EPSILON7 ); if (doCorner) { cornerDirection = Cartesian3.cross( cornerDirection, normal, cornerDirection ); cornerDirection = Cartesian3.cross( normal, cornerDirection, cornerDirection ); cornerDirection = Cartesian3.normalize(cornerDirection, cornerDirection); var scalar = width / Math.max( 0.25, Cartesian3.magnitude( Cartesian3.cross(cornerDirection, backward, scratch1$1) ) ); var leftIsOutside = PolylineVolumeGeometryLibrary.angleIsGreaterThanPi( forward, backward, position, ellipsoid ); cornerDirection = Cartesian3.multiplyByScalar( cornerDirection, scalar, cornerDirection ); if (leftIsOutside) { rightPos = Cartesian3.add(position, cornerDirection, rightPos); center = Cartesian3.add( rightPos, Cartesian3.multiplyByScalar(left, width, center), center ); leftPos = Cartesian3.add( rightPos, Cartesian3.multiplyByScalar(left, width * 2, leftPos), leftPos ); scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); subdividedPositions = PolylinePipeline.generateArc({ positions: scaleArray2, granularity: granularity, ellipsoid: ellipsoid, }); calculatedPositions = addShiftedPositions( subdividedPositions, left, width, calculatedPositions ); if (saveAttributes) { calculatedLefts.push(left.x, left.y, left.z); calculatedNormals.push(normal.x, normal.y, normal.z); } startPoint = Cartesian3.clone(leftPos, startPoint); left = Cartesian3.normalize( Cartesian3.cross(normal, forward, left), left ); leftPos = Cartesian3.add( rightPos, Cartesian3.multiplyByScalar(left, width * 2, leftPos), leftPos ); previousPos = Cartesian3.add( rightPos, Cartesian3.multiplyByScalar(left, width, previousPos), previousPos ); if ( cornerType === CornerType$1.ROUNDED || cornerType === CornerType$1.BEVELED ) { corners.push({ leftPositions: computeRoundCorner( rightPos, startPoint, leftPos, cornerType, leftIsOutside ), }); } else { corners.push({ leftPositions: computeMiteredCorner( position, Cartesian3.negate(cornerDirection, cornerDirection), leftPos, leftIsOutside ), }); } } else { leftPos = Cartesian3.add(position, cornerDirection, leftPos); center = Cartesian3.add( leftPos, Cartesian3.negate( Cartesian3.multiplyByScalar(left, width, center), center ), center ); rightPos = Cartesian3.add( leftPos, Cartesian3.negate( Cartesian3.multiplyByScalar(left, width * 2, rightPos), rightPos ), rightPos ); scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); scaleArray2[1] = Cartesian3.clone(center, scaleArray2[1]); subdividedPositions = PolylinePipeline.generateArc({ positions: scaleArray2, granularity: granularity, ellipsoid: ellipsoid, }); calculatedPositions = addShiftedPositions( subdividedPositions, left, width, calculatedPositions ); if (saveAttributes) { calculatedLefts.push(left.x, left.y, left.z); calculatedNormals.push(normal.x, normal.y, normal.z); } startPoint = Cartesian3.clone(rightPos, startPoint); left = Cartesian3.normalize( Cartesian3.cross(normal, forward, left), left ); rightPos = Cartesian3.add( leftPos, Cartesian3.negate( Cartesian3.multiplyByScalar(left, width * 2, rightPos), rightPos ), rightPos ); previousPos = Cartesian3.add( leftPos, Cartesian3.negate( Cartesian3.multiplyByScalar(left, width, previousPos), previousPos ), previousPos ); if ( cornerType === CornerType$1.ROUNDED || cornerType === CornerType$1.BEVELED ) { corners.push({ rightPositions: computeRoundCorner( leftPos, startPoint, rightPos, cornerType, leftIsOutside ), }); } else { corners.push({ rightPositions: computeMiteredCorner( position, cornerDirection, rightPos, leftIsOutside ), }); } } backward = Cartesian3.negate(forward, backward); } position = nextPosition; } normal = ellipsoid.geodeticSurfaceNormal(position, normal); scaleArray2[0] = Cartesian3.clone(previousPos, scaleArray2[0]); scaleArray2[1] = Cartesian3.clone(position, scaleArray2[1]); subdividedPositions = PolylinePipeline.generateArc({ positions: scaleArray2, granularity: granularity, ellipsoid: ellipsoid, }); calculatedPositions = addShiftedPositions( subdividedPositions, left, width, calculatedPositions ); if (saveAttributes) { calculatedLefts.push(left.x, left.y, left.z); calculatedNormals.push(normal.x, normal.y, normal.z); } var endPositions; if (cornerType === CornerType$1.ROUNDED) { endPositions = addEndCaps(calculatedPositions); } return { positions: calculatedPositions, corners: corners, lefts: calculatedLefts, normals: calculatedNormals, endPositions: endPositions, }; }; var cartesian1$1 = new Cartesian3(); var cartesian2$1 = new Cartesian3(); var cartesian3$1 = new Cartesian3(); var cartesian4 = new Cartesian3(); var cartesian5 = new Cartesian3(); var cartesian6 = new Cartesian3(); var scratch1 = new Cartesian3(); var scratch2 = new Cartesian3(); function scaleToSurface$1(positions, ellipsoid) { for (var i = 0; i < positions.length; i++) { positions[i] = ellipsoid.scaleToGeodeticSurface(positions[i], positions[i]); } return positions; } function addNormals(attr, normal, left, front, back, vertexFormat) { var normals = attr.normals; var tangents = attr.tangents; var bitangents = attr.bitangents; var forward = Cartesian3.normalize( Cartesian3.cross(left, normal, scratch1), scratch1 ); if (vertexFormat.normal) { CorridorGeometryLibrary.addAttribute(normals, normal, front, back); } if (vertexFormat.tangent) { CorridorGeometryLibrary.addAttribute(tangents, forward, front, back); } if (vertexFormat.bitangent) { CorridorGeometryLibrary.addAttribute(bitangents, left, front, back); } } function combine$1(computedPositions, vertexFormat, ellipsoid) { var positions = computedPositions.positions; var corners = computedPositions.corners; var endPositions = computedPositions.endPositions; var computedLefts = computedPositions.lefts; var computedNormals = computedPositions.normals; var attributes = new GeometryAttributes(); var corner; var leftCount = 0; var rightCount = 0; var i; var indicesLength = 0; var length; for (i = 0; i < positions.length; i += 2) { length = positions[i].length - 3; leftCount += length; //subtracting 3 to account for duplicate points at corners indicesLength += length * 2; rightCount += positions[i + 1].length - 3; } leftCount += 3; //add back count for end positions rightCount += 3; for (i = 0; i < corners.length; i++) { corner = corners[i]; var leftSide = corners[i].leftPositions; if (defined(leftSide)) { length = leftSide.length; leftCount += length; indicesLength += length; } else { length = corners[i].rightPositions.length; rightCount += length; indicesLength += length; } } var addEndPositions = defined(endPositions); var endPositionLength; if (addEndPositions) { endPositionLength = endPositions[0].length - 3; leftCount += endPositionLength; rightCount += endPositionLength; endPositionLength /= 3; indicesLength += endPositionLength * 6; } var size = leftCount + rightCount; var finalPositions = new Float64Array(size); var normals = vertexFormat.normal ? new Float32Array(size) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(size) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(size) : undefined; var attr = { normals: normals, tangents: tangents, bitangents: bitangents, }; var front = 0; var back = size - 1; var UL, LL, UR, LR; var normal = cartesian1$1; var left = cartesian2$1; var rightPos, leftPos; var halfLength = endPositionLength / 2; var indices = IndexDatatype$1.createTypedArray(size / 3, indicesLength); var index = 0; if (addEndPositions) { // add rounded end leftPos = cartesian3$1; rightPos = cartesian4; var firstEndPositions = endPositions[0]; normal = Cartesian3.fromArray(computedNormals, 0, normal); left = Cartesian3.fromArray(computedLefts, 0, left); for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray( firstEndPositions, (halfLength - 1 - i) * 3, leftPos ); rightPos = Cartesian3.fromArray( firstEndPositions, (halfLength + i) * 3, rightPos ); CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); CorridorGeometryLibrary.addAttribute( finalPositions, leftPos, undefined, back ); addNormals(attr, normal, left, front, back, vertexFormat); LL = front / 3; LR = LL + 1; UL = (back - 2) / 3; UR = UL - 1; indices[index++] = UL; indices[index++] = LL; indices[index++] = UR; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } } var posIndex = 0; var compIndex = 0; var rightEdge = positions[posIndex++]; //add first two edges var leftEdge = positions[posIndex++]; finalPositions.set(rightEdge, front); finalPositions.set(leftEdge, back - leftEdge.length + 1); left = Cartesian3.fromArray(computedLefts, compIndex, left); var rightNormal; var leftNormal; length = leftEdge.length - 3; for (i = 0; i < length; i += 3) { rightNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(rightEdge, i, scratch1), scratch1 ); leftNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(leftEdge, length - i, scratch2), scratch2 ); normal = Cartesian3.normalize( Cartesian3.add(rightNormal, leftNormal, normal), normal ); addNormals(attr, normal, left, front, back, vertexFormat); LL = front / 3; LR = LL + 1; UL = (back - 2) / 3; UR = UL - 1; indices[index++] = UL; indices[index++] = LL; indices[index++] = UR; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } rightNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(rightEdge, length, scratch1), scratch1 ); leftNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(leftEdge, length, scratch2), scratch2 ); normal = Cartesian3.normalize( Cartesian3.add(rightNormal, leftNormal, normal), normal ); compIndex += 3; for (i = 0; i < corners.length; i++) { var j; corner = corners[i]; var l = corner.leftPositions; var r = corner.rightPositions; var pivot; var start; var outsidePoint = cartesian6; var previousPoint = cartesian3$1; var nextPoint = cartesian4; normal = Cartesian3.fromArray(computedNormals, compIndex, normal); if (defined(l)) { addNormals(attr, normal, left, undefined, back, vertexFormat); back -= 3; pivot = LR; start = UR; for (j = 0; j < l.length / 3; j++) { outsidePoint = Cartesian3.fromArray(l, j * 3, outsidePoint); indices[index++] = pivot; indices[index++] = start - j - 1; indices[index++] = start - j; CorridorGeometryLibrary.addAttribute( finalPositions, outsidePoint, undefined, back ); previousPoint = Cartesian3.fromArray( finalPositions, (start - j - 1) * 3, previousPoint ); nextPoint = Cartesian3.fromArray(finalPositions, pivot * 3, nextPoint); left = Cartesian3.normalize( Cartesian3.subtract(previousPoint, nextPoint, left), left ); addNormals(attr, normal, left, undefined, back, vertexFormat); back -= 3; } outsidePoint = Cartesian3.fromArray( finalPositions, pivot * 3, outsidePoint ); previousPoint = Cartesian3.subtract( Cartesian3.fromArray(finalPositions, start * 3, previousPoint), outsidePoint, previousPoint ); nextPoint = Cartesian3.subtract( Cartesian3.fromArray(finalPositions, (start - j) * 3, nextPoint), outsidePoint, nextPoint ); left = Cartesian3.normalize( Cartesian3.add(previousPoint, nextPoint, left), left ); addNormals(attr, normal, left, front, undefined, vertexFormat); front += 3; } else { addNormals(attr, normal, left, front, undefined, vertexFormat); front += 3; pivot = UR; start = LR; for (j = 0; j < r.length / 3; j++) { outsidePoint = Cartesian3.fromArray(r, j * 3, outsidePoint); indices[index++] = pivot; indices[index++] = start + j; indices[index++] = start + j + 1; CorridorGeometryLibrary.addAttribute( finalPositions, outsidePoint, front ); previousPoint = Cartesian3.fromArray( finalPositions, pivot * 3, previousPoint ); nextPoint = Cartesian3.fromArray( finalPositions, (start + j) * 3, nextPoint ); left = Cartesian3.normalize( Cartesian3.subtract(previousPoint, nextPoint, left), left ); addNormals(attr, normal, left, front, undefined, vertexFormat); front += 3; } outsidePoint = Cartesian3.fromArray( finalPositions, pivot * 3, outsidePoint ); previousPoint = Cartesian3.subtract( Cartesian3.fromArray(finalPositions, (start + j) * 3, previousPoint), outsidePoint, previousPoint ); nextPoint = Cartesian3.subtract( Cartesian3.fromArray(finalPositions, start * 3, nextPoint), outsidePoint, nextPoint ); left = Cartesian3.normalize( Cartesian3.negate(Cartesian3.add(nextPoint, previousPoint, left), left), left ); addNormals(attr, normal, left, undefined, back, vertexFormat); back -= 3; } rightEdge = positions[posIndex++]; leftEdge = positions[posIndex++]; rightEdge.splice(0, 3); //remove duplicate points added by corner leftEdge.splice(leftEdge.length - 3, 3); finalPositions.set(rightEdge, front); finalPositions.set(leftEdge, back - leftEdge.length + 1); length = leftEdge.length - 3; compIndex += 3; left = Cartesian3.fromArray(computedLefts, compIndex, left); for (j = 0; j < leftEdge.length; j += 3) { rightNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(rightEdge, j, scratch1), scratch1 ); leftNormal = ellipsoid.geodeticSurfaceNormal( Cartesian3.fromArray(leftEdge, length - j, scratch2), scratch2 ); normal = Cartesian3.normalize( Cartesian3.add(rightNormal, leftNormal, normal), normal ); addNormals(attr, normal, left, front, back, vertexFormat); LR = front / 3; LL = LR - 1; UR = (back - 2) / 3; UL = UR + 1; indices[index++] = UL; indices[index++] = LL; indices[index++] = UR; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } front -= 3; back += 3; } normal = Cartesian3.fromArray( computedNormals, computedNormals.length - 3, normal ); addNormals(attr, normal, left, front, back, vertexFormat); if (addEndPositions) { // add rounded end front += 3; back -= 3; leftPos = cartesian3$1; rightPos = cartesian4; var lastEndPositions = endPositions[1]; for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray( lastEndPositions, (endPositionLength - i - 1) * 3, leftPos ); rightPos = Cartesian3.fromArray(lastEndPositions, i * 3, rightPos); CorridorGeometryLibrary.addAttribute( finalPositions, leftPos, undefined, back ); CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); addNormals(attr, normal, left, front, back, vertexFormat); LR = front / 3; LL = LR - 1; UR = (back - 2) / 3; UL = UR + 1; indices[index++] = UL; indices[index++] = LL; indices[index++] = UR; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } } attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: finalPositions, }); if (vertexFormat.st) { var st = new Float32Array((size / 3) * 2); var rightSt; var leftSt; var stIndex = 0; if (addEndPositions) { leftCount /= 3; rightCount /= 3; var theta = Math.PI / (endPositionLength + 1); leftSt = 1 / (leftCount - endPositionLength + 1); rightSt = 1 / (rightCount - endPositionLength + 1); var a; var halfEndPos = endPositionLength / 2; for (i = halfEndPos + 1; i < endPositionLength + 1; i++) { // lower left rounded end a = CesiumMath.PI_OVER_TWO + theta * i; st[stIndex++] = rightSt * (1 + Math.cos(a)); st[stIndex++] = 0.5 * (1 + Math.sin(a)); } for (i = 1; i < rightCount - endPositionLength + 1; i++) { // bottom edge st[stIndex++] = i * rightSt; st[stIndex++] = 0; } for (i = endPositionLength; i > halfEndPos; i--) { // lower right rounded end a = CesiumMath.PI_OVER_TWO - i * theta; st[stIndex++] = 1 - rightSt * (1 + Math.cos(a)); st[stIndex++] = 0.5 * (1 + Math.sin(a)); } for (i = halfEndPos; i > 0; i--) { // upper right rounded end a = CesiumMath.PI_OVER_TWO - theta * i; st[stIndex++] = 1 - leftSt * (1 + Math.cos(a)); st[stIndex++] = 0.5 * (1 + Math.sin(a)); } for (i = leftCount - endPositionLength; i > 0; i--) { // top edge st[stIndex++] = i * leftSt; st[stIndex++] = 1; } for (i = 1; i < halfEndPos + 1; i++) { // upper left rounded end a = CesiumMath.PI_OVER_TWO + theta * i; st[stIndex++] = leftSt * (1 + Math.cos(a)); st[stIndex++] = 0.5 * (1 + Math.sin(a)); } } else { leftCount /= 3; rightCount /= 3; leftSt = 1 / (leftCount - 1); rightSt = 1 / (rightCount - 1); for (i = 0; i < rightCount; i++) { // bottom edge st[stIndex++] = i * rightSt; st[stIndex++] = 0; } for (i = leftCount; i > 0; i--) { // top edge st[stIndex++] = (i - 1) * leftSt; st[stIndex++] = 1; } } attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: st, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attr.normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attr.tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attr.bitangents, }); } return { attributes: attributes, indices: indices, }; } function extrudedAttributes(attributes, vertexFormat) { if ( !vertexFormat.normal && !vertexFormat.tangent && !vertexFormat.bitangent && !vertexFormat.st ) { return attributes; } var positions = attributes.position.values; var topNormals; var topBitangents; if (vertexFormat.normal || vertexFormat.bitangent) { topNormals = attributes.normal.values; topBitangents = attributes.bitangent.values; } var size = attributes.position.values.length / 18; var threeSize = size * 3; var twoSize = size * 2; var sixSize = threeSize * 2; var i; if (vertexFormat.normal || vertexFormat.bitangent || vertexFormat.tangent) { var normals = vertexFormat.normal ? new Float32Array(threeSize * 6) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(threeSize * 6) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(threeSize * 6) : undefined; var topPosition = cartesian1$1; var bottomPosition = cartesian2$1; var previousPosition = cartesian3$1; var normal = cartesian4; var tangent = cartesian5; var bitangent = cartesian6; var attrIndex = sixSize; for (i = 0; i < threeSize; i += 3) { var attrIndexOffset = attrIndex + sixSize; topPosition = Cartesian3.fromArray(positions, i, topPosition); bottomPosition = Cartesian3.fromArray( positions, i + threeSize, bottomPosition ); previousPosition = Cartesian3.fromArray( positions, (i + 3) % threeSize, previousPosition ); bottomPosition = Cartesian3.subtract( bottomPosition, topPosition, bottomPosition ); previousPosition = Cartesian3.subtract( previousPosition, topPosition, previousPosition ); normal = Cartesian3.normalize( Cartesian3.cross(bottomPosition, previousPosition, normal), normal ); if (vertexFormat.normal) { CorridorGeometryLibrary.addAttribute(normals, normal, attrIndexOffset); CorridorGeometryLibrary.addAttribute( normals, normal, attrIndexOffset + 3 ); CorridorGeometryLibrary.addAttribute(normals, normal, attrIndex); CorridorGeometryLibrary.addAttribute(normals, normal, attrIndex + 3); } if (vertexFormat.tangent || vertexFormat.bitangent) { bitangent = Cartesian3.fromArray(topNormals, i, bitangent); if (vertexFormat.bitangent) { CorridorGeometryLibrary.addAttribute( bitangents, bitangent, attrIndexOffset ); CorridorGeometryLibrary.addAttribute( bitangents, bitangent, attrIndexOffset + 3 ); CorridorGeometryLibrary.addAttribute( bitangents, bitangent, attrIndex ); CorridorGeometryLibrary.addAttribute( bitangents, bitangent, attrIndex + 3 ); } if (vertexFormat.tangent) { tangent = Cartesian3.normalize( Cartesian3.cross(bitangent, normal, tangent), tangent ); CorridorGeometryLibrary.addAttribute( tangents, tangent, attrIndexOffset ); CorridorGeometryLibrary.addAttribute( tangents, tangent, attrIndexOffset + 3 ); CorridorGeometryLibrary.addAttribute(tangents, tangent, attrIndex); CorridorGeometryLibrary.addAttribute( tangents, tangent, attrIndex + 3 ); } } attrIndex += 6; } if (vertexFormat.normal) { normals.set(topNormals); //top for (i = 0; i < threeSize; i += 3) { //bottom normals normals[i + threeSize] = -topNormals[i]; normals[i + threeSize + 1] = -topNormals[i + 1]; normals[i + threeSize + 2] = -topNormals[i + 2]; } attributes.normal.values = normals; } else { attributes.normal = undefined; } if (vertexFormat.bitangent) { bitangents.set(topBitangents); //top bitangents.set(topBitangents, threeSize); //bottom attributes.bitangent.values = bitangents; } else { attributes.bitangent = undefined; } if (vertexFormat.tangent) { var topTangents = attributes.tangent.values; tangents.set(topTangents); //top tangents.set(topTangents, threeSize); //bottom attributes.tangent.values = tangents; } } if (vertexFormat.st) { var topSt = attributes.st.values; var st = new Float32Array(twoSize * 6); st.set(topSt); //top st.set(topSt, twoSize); //bottom var index = twoSize * 2; for (var j = 0; j < 2; j++) { st[index++] = topSt[0]; st[index++] = topSt[1]; for (i = 2; i < twoSize; i += 2) { var s = topSt[i]; var t = topSt[i + 1]; st[index++] = s; st[index++] = t; st[index++] = s; st[index++] = t; } st[index++] = topSt[0]; st[index++] = topSt[1]; } attributes.st.values = st; } return attributes; } function addWallPositions$1(positions, index, wallPositions) { wallPositions[index++] = positions[0]; wallPositions[index++] = positions[1]; wallPositions[index++] = positions[2]; for (var i = 3; i < positions.length; i += 3) { var x = positions[i]; var y = positions[i + 1]; var z = positions[i + 2]; wallPositions[index++] = x; wallPositions[index++] = y; wallPositions[index++] = z; wallPositions[index++] = x; wallPositions[index++] = y; wallPositions[index++] = z; } wallPositions[index++] = positions[0]; wallPositions[index++] = positions[1]; wallPositions[index++] = positions[2]; return wallPositions; } function computePositionsExtruded$1(params, vertexFormat) { var topVertexFormat = new VertexFormat({ position: vertexFormat.position, normal: vertexFormat.normal || vertexFormat.bitangent || params.shadowVolume, tangent: vertexFormat.tangent, bitangent: vertexFormat.normal || vertexFormat.bitangent, st: vertexFormat.st, }); var ellipsoid = params.ellipsoid; var computedPositions = CorridorGeometryLibrary.computePositions(params); var attr = combine$1(computedPositions, topVertexFormat, ellipsoid); var height = params.height; var extrudedHeight = params.extrudedHeight; var attributes = attr.attributes; var indices = attr.indices; var positions = attributes.position.values; var length = positions.length; var newPositions = new Float64Array(length * 6); var extrudedPositions = new Float64Array(length); extrudedPositions.set(positions); var wallPositions = new Float64Array(length * 4); positions = PolygonPipeline.scaleToGeodeticHeight( positions, height, ellipsoid ); wallPositions = addWallPositions$1(positions, 0, wallPositions); extrudedPositions = PolygonPipeline.scaleToGeodeticHeight( extrudedPositions, extrudedHeight, ellipsoid ); wallPositions = addWallPositions$1( extrudedPositions, length * 2, wallPositions ); newPositions.set(positions); newPositions.set(extrudedPositions, length); newPositions.set(wallPositions, length * 2); attributes.position.values = newPositions; attributes = extrudedAttributes(attributes, vertexFormat); var i; var size = length / 3; if (params.shadowVolume) { var topNormals = attributes.normal.values; length = topNormals.length; var extrudeNormals = new Float32Array(length * 6); for (i = 0; i < length; i++) { topNormals[i] = -topNormals[i]; } //only get normals for bottom layer that's going to be pushed down extrudeNormals.set(topNormals, length); //bottom face extrudeNormals = addWallPositions$1(topNormals, length * 4, extrudeNormals); //bottom wall attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: extrudeNormals, }); if (!vertexFormat.normal) { attributes.normal = undefined; } } if (defined(params.offsetAttribute)) { var applyOffset = new Uint8Array(size * 6); if (params.offsetAttribute === GeometryOffsetAttribute$1.TOP) { applyOffset = arrayFill(applyOffset, 1, 0, size); // top face applyOffset = arrayFill(applyOffset, 1, size * 2, size * 4); // top wall } else { var applyOffsetValue = params.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; applyOffset = arrayFill(applyOffset, applyOffsetValue); } attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } var iLength = indices.length; var twoSize = size + size; var newIndices = IndexDatatype$1.createTypedArray( newPositions.length / 3, iLength * 2 + twoSize * 3 ); newIndices.set(indices); var index = iLength; for (i = 0; i < iLength; i += 3) { // bottom indices var v0 = indices[i]; var v1 = indices[i + 1]; var v2 = indices[i + 2]; newIndices[index++] = v2 + size; newIndices[index++] = v1 + size; newIndices[index++] = v0 + size; } var UL, LL, UR, LR; for (i = 0; i < twoSize; i += 2) { //wall indices UL = i + twoSize; LL = UL + twoSize; UR = UL + 1; LR = LL + 1; newIndices[index++] = UL; newIndices[index++] = LL; newIndices[index++] = UR; newIndices[index++] = UR; newIndices[index++] = LL; newIndices[index++] = LR; } return { attributes: attributes, indices: newIndices, }; } var scratchCartesian1$2 = new Cartesian3(); var scratchCartesian2$5 = new Cartesian3(); var scratchCartographic$d = new Cartographic(); function computeOffsetPoints( position1, position2, ellipsoid, halfWidth, min, max ) { // Compute direction of offset the point var direction = Cartesian3.subtract(position2, position1, scratchCartesian1$2); Cartesian3.normalize(direction, direction); var normal = ellipsoid.geodeticSurfaceNormal(position1, scratchCartesian2$5); var offsetDirection = Cartesian3.cross(direction, normal, scratchCartesian1$2); Cartesian3.multiplyByScalar(offsetDirection, halfWidth, offsetDirection); var minLat = min.latitude; var minLon = min.longitude; var maxLat = max.latitude; var maxLon = max.longitude; // Compute 2 offset points Cartesian3.add(position1, offsetDirection, scratchCartesian2$5); ellipsoid.cartesianToCartographic(scratchCartesian2$5, scratchCartographic$d); var lat = scratchCartographic$d.latitude; var lon = scratchCartographic$d.longitude; minLat = Math.min(minLat, lat); minLon = Math.min(minLon, lon); maxLat = Math.max(maxLat, lat); maxLon = Math.max(maxLon, lon); Cartesian3.subtract(position1, offsetDirection, scratchCartesian2$5); ellipsoid.cartesianToCartographic(scratchCartesian2$5, scratchCartographic$d); lat = scratchCartographic$d.latitude; lon = scratchCartographic$d.longitude; minLat = Math.min(minLat, lat); minLon = Math.min(minLon, lon); maxLat = Math.max(maxLat, lat); maxLon = Math.max(maxLon, lon); min.latitude = minLat; min.longitude = minLon; max.latitude = maxLat; max.longitude = maxLon; } var scratchCartesianOffset = new Cartesian3(); var scratchCartesianEnds = new Cartesian3(); var scratchCartographicMin = new Cartographic(); var scratchCartographicMax = new Cartographic(); function computeRectangle$2(positions, ellipsoid, width, cornerType, result) { positions = scaleToSurface$1(positions, ellipsoid); var cleanPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); var length = cleanPositions.length; if (length < 2 || width <= 0) { return new Rectangle(); } var halfWidth = width * 0.5; scratchCartographicMin.latitude = Number.POSITIVE_INFINITY; scratchCartographicMin.longitude = Number.POSITIVE_INFINITY; scratchCartographicMax.latitude = Number.NEGATIVE_INFINITY; scratchCartographicMax.longitude = Number.NEGATIVE_INFINITY; var lat, lon; if (cornerType === CornerType$1.ROUNDED) { // Compute start cap var first = cleanPositions[0]; Cartesian3.subtract(first, cleanPositions[1], scratchCartesianOffset); Cartesian3.normalize(scratchCartesianOffset, scratchCartesianOffset); Cartesian3.multiplyByScalar( scratchCartesianOffset, halfWidth, scratchCartesianOffset ); Cartesian3.add(first, scratchCartesianOffset, scratchCartesianEnds); ellipsoid.cartesianToCartographic( scratchCartesianEnds, scratchCartographic$d ); lat = scratchCartographic$d.latitude; lon = scratchCartographic$d.longitude; scratchCartographicMin.latitude = Math.min( scratchCartographicMin.latitude, lat ); scratchCartographicMin.longitude = Math.min( scratchCartographicMin.longitude, lon ); scratchCartographicMax.latitude = Math.max( scratchCartographicMax.latitude, lat ); scratchCartographicMax.longitude = Math.max( scratchCartographicMax.longitude, lon ); } // Compute the rest for (var i = 0; i < length - 1; ++i) { computeOffsetPoints( cleanPositions[i], cleanPositions[i + 1], ellipsoid, halfWidth, scratchCartographicMin, scratchCartographicMax ); } // Compute ending point var last = cleanPositions[length - 1]; Cartesian3.subtract(last, cleanPositions[length - 2], scratchCartesianOffset); Cartesian3.normalize(scratchCartesianOffset, scratchCartesianOffset); Cartesian3.multiplyByScalar( scratchCartesianOffset, halfWidth, scratchCartesianOffset ); Cartesian3.add(last, scratchCartesianOffset, scratchCartesianEnds); computeOffsetPoints( last, scratchCartesianEnds, ellipsoid, halfWidth, scratchCartographicMin, scratchCartographicMax ); if (cornerType === CornerType$1.ROUNDED) { // Compute end cap ellipsoid.cartesianToCartographic( scratchCartesianEnds, scratchCartographic$d ); lat = scratchCartographic$d.latitude; lon = scratchCartographic$d.longitude; scratchCartographicMin.latitude = Math.min( scratchCartographicMin.latitude, lat ); scratchCartographicMin.longitude = Math.min( scratchCartographicMin.longitude, lon ); scratchCartographicMax.latitude = Math.max( scratchCartographicMax.latitude, lat ); scratchCartographicMax.longitude = Math.max( scratchCartographicMax.longitude, lon ); } var rectangle = defined(result) ? result : new Rectangle(); rectangle.north = scratchCartographicMax.latitude; rectangle.south = scratchCartographicMin.latitude; rectangle.east = scratchCartographicMax.longitude; rectangle.west = scratchCartographicMin.longitude; return rectangle; } /** * A description of a corridor. Corridor geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @alias CorridorGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that define the center of the corridor. * @param {Number} options.width The distance between the edges of the corridor in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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] The distance in meters between the ellipsoid surface and the positions. * @param {Number} [options.extrudedHeight] The distance in meters between the ellipsoid surface and the extruded face. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners. * * @see CorridorGeometry.createGeometry * @see Packable * * @demo {@link https://sandcastle.cesium.com/index.html?src=Corridor.html|Cesium Sandcastle Corridor Demo} * * @example * var corridor = new Cesium.CorridorGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY, * positions : Cesium.Cartesian3.fromDegreesArray([-72.0, 40.0, -70.0, 35.0]), * width : 100000 * }); */ function CorridorGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; var width = options.width; //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", positions); Check.defined("options.width", width); //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._positions = positions; this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._vertexFormat = VertexFormat.clone( defaultValue(options.vertexFormat, VertexFormat.DEFAULT) ); this._width = width; this._height = Math.max(height, extrudedHeight); this._extrudedHeight = Math.min(height, extrudedHeight); this._cornerType = defaultValue(options.cornerType, CornerType$1.ROUNDED); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = "createCorridorGeometry"; this._offsetAttribute = options.offsetAttribute; this._rectangle = undefined; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 7; } /** * Stores the provided instance into the provided array. * * @param {CorridorGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CorridorGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.defined("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._width; array[startingIndex++] = value._height; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._cornerType; array[startingIndex++] = value._granularity; array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchEllipsoid$b = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat$9 = new VertexFormat(); var scratchOptions$f = { positions: undefined, ellipsoid: scratchEllipsoid$b, vertexFormat: scratchVertexFormat$9, width: undefined, height: undefined, extrudedHeight: undefined, cornerType: undefined, granularity: undefined, shadowVolume: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CorridorGeometry} [result] The object into which to store the result. * @returns {CorridorGeometry} The modified result parameter or a new CorridorGeometry instance if one was not provided. */ CorridorGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var length = array[startingIndex++]; var positions = new Array(length); for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$b); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$9 ); startingIndex += VertexFormat.packedLength; var width = array[startingIndex++]; var height = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var cornerType = array[startingIndex++]; var granularity = array[startingIndex++]; var shadowVolume = array[startingIndex++] === 1.0; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$f.positions = positions; scratchOptions$f.width = width; scratchOptions$f.height = height; scratchOptions$f.extrudedHeight = extrudedHeight; scratchOptions$f.cornerType = cornerType; scratchOptions$f.granularity = granularity; scratchOptions$f.shadowVolume = shadowVolume; scratchOptions$f.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new CorridorGeometry(scratchOptions$f); } result._positions = positions; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._width = width; result._height = height; result._extrudedHeight = extrudedHeight; result._cornerType = cornerType; result._granularity = granularity; result._shadowVolume = shadowVolume; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the bounding rectangle given the provided options * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that define the center of the corridor. * @param {Number} options.width The distance between the edges of the corridor in meters. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners. * @param {Rectangle} [result] An object in which to store the result. * * @returns {Rectangle} The result rectangle. */ CorridorGeometry.computeRectangle = function (options, result) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; var width = options.width; //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", positions); Check.defined("options.width", width); //>>includeEnd('debug'); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var cornerType = defaultValue(options.cornerType, CornerType$1.ROUNDED); return computeRectangle$2(positions, ellipsoid, width, cornerType, result); }; /** * Computes the geometric representation of a corridor, including its vertices, indices, and a bounding sphere. * * @param {CorridorGeometry} corridorGeometry A description of the corridor. * @returns {Geometry|undefined} The computed vertices and indices. */ CorridorGeometry.createGeometry = function (corridorGeometry) { var positions = corridorGeometry._positions; var width = corridorGeometry._width; var ellipsoid = corridorGeometry._ellipsoid; positions = scaleToSurface$1(positions, ellipsoid); var cleanPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); if (cleanPositions.length < 2 || width <= 0) { return; } var height = corridorGeometry._height; var extrudedHeight = corridorGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( height, extrudedHeight, 0, CesiumMath.EPSILON2 ); var vertexFormat = corridorGeometry._vertexFormat; var params = { ellipsoid: ellipsoid, positions: cleanPositions, width: width, cornerType: corridorGeometry._cornerType, granularity: corridorGeometry._granularity, saveAttributes: true, }; var attr; if (extrude) { params.height = height; params.extrudedHeight = extrudedHeight; params.shadowVolume = corridorGeometry._shadowVolume; params.offsetAttribute = corridorGeometry._offsetAttribute; attr = computePositionsExtruded$1(params, vertexFormat); } else { var computedPositions = CorridorGeometryLibrary.computePositions(params); attr = combine$1(computedPositions, vertexFormat, ellipsoid); attr.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( attr.attributes.position.values, height, ellipsoid ); if (defined(corridorGeometry._offsetAttribute)) { var applyOffsetValue = corridorGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; var length = attr.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); arrayFill(applyOffset, applyOffsetValue); attr.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } } var attributes = attr.attributes; var boundingSphere = BoundingSphere.fromVertices( attributes.position.values, undefined, 3 ); if (!vertexFormat.position) { attr.attributes.position.values = undefined; } return new Geometry({ attributes: attributes, indices: attr.indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: boundingSphere, offsetAttribute: corridorGeometry._offsetAttribute, }); }; /** * @private */ CorridorGeometry.createShadowVolume = function ( corridorGeometry, minHeightFunc, maxHeightFunc ) { var granularity = corridorGeometry._granularity; var ellipsoid = corridorGeometry._ellipsoid; var minHeight = minHeightFunc(granularity, ellipsoid); var maxHeight = maxHeightFunc(granularity, ellipsoid); return new CorridorGeometry({ positions: corridorGeometry._positions, width: corridorGeometry._width, cornerType: corridorGeometry._cornerType, ellipsoid: ellipsoid, granularity: granularity, extrudedHeight: minHeight, height: maxHeight, vertexFormat: VertexFormat.POSITION_ONLY, shadowVolume: true, }); }; Object.defineProperties(CorridorGeometry.prototype, { /** * @private */ rectangle: { get: function () { if (!defined(this._rectangle)) { this._rectangle = computeRectangle$2( this._positions, this._ellipsoid, this._width, this._cornerType ); } return this._rectangle; }, }, /** * For remapping texture coordinates when rendering CorridorGeometries as GroundPrimitives. * * Corridors don't support stRotation, * so just return the corners of the original system. * @private */ textureCoordinateRotationPoints: { get: function () { return [0, 0, 0, 1, 1, 0]; }, }, }); var cartesian1 = new Cartesian3(); var cartesian2 = new Cartesian3(); var cartesian3 = new Cartesian3(); function scaleToSurface(positions, ellipsoid) { for (var i = 0; i < positions.length; i++) { positions[i] = ellipsoid.scaleToGeodeticSurface(positions[i], positions[i]); } return positions; } function combine(computedPositions, cornerType) { var wallIndices = []; var positions = computedPositions.positions; var corners = computedPositions.corners; var endPositions = computedPositions.endPositions; var attributes = new GeometryAttributes(); var corner; var leftCount = 0; var rightCount = 0; var i; var indicesLength = 0; var length; for (i = 0; i < positions.length; i += 2) { length = positions[i].length - 3; leftCount += length; //subtracting 3 to account for duplicate points at corners indicesLength += (length / 3) * 4; rightCount += positions[i + 1].length - 3; } leftCount += 3; //add back count for end positions rightCount += 3; for (i = 0; i < corners.length; i++) { corner = corners[i]; var leftSide = corners[i].leftPositions; if (defined(leftSide)) { length = leftSide.length; leftCount += length; indicesLength += (length / 3) * 2; } else { length = corners[i].rightPositions.length; rightCount += length; indicesLength += (length / 3) * 2; } } var addEndPositions = defined(endPositions); var endPositionLength; if (addEndPositions) { endPositionLength = endPositions[0].length - 3; leftCount += endPositionLength; rightCount += endPositionLength; endPositionLength /= 3; indicesLength += endPositionLength * 4; } var size = leftCount + rightCount; var finalPositions = new Float64Array(size); var front = 0; var back = size - 1; var UL, LL, UR, LR; var rightPos, leftPos; var halfLength = endPositionLength / 2; var indices = IndexDatatype$1.createTypedArray(size / 3, indicesLength + 4); var index = 0; indices[index++] = front / 3; indices[index++] = (back - 2) / 3; if (addEndPositions) { // add rounded end wallIndices.push(front / 3); leftPos = cartesian1; rightPos = cartesian2; var firstEndPositions = endPositions[0]; for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray( firstEndPositions, (halfLength - 1 - i) * 3, leftPos ); rightPos = Cartesian3.fromArray( firstEndPositions, (halfLength + i) * 3, rightPos ); CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); CorridorGeometryLibrary.addAttribute( finalPositions, leftPos, undefined, back ); LL = front / 3; LR = LL + 1; UL = (back - 2) / 3; UR = UL - 1; indices[index++] = UL; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } } var posIndex = 0; var rightEdge = positions[posIndex++]; //add first two edges var leftEdge = positions[posIndex++]; finalPositions.set(rightEdge, front); finalPositions.set(leftEdge, back - leftEdge.length + 1); length = leftEdge.length - 3; wallIndices.push(front / 3, (back - 2) / 3); for (i = 0; i < length; i += 3) { LL = front / 3; LR = LL + 1; UL = (back - 2) / 3; UR = UL - 1; indices[index++] = UL; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } for (i = 0; i < corners.length; i++) { var j; corner = corners[i]; var l = corner.leftPositions; var r = corner.rightPositions; var start; var outsidePoint = cartesian3; if (defined(l)) { back -= 3; start = UR; wallIndices.push(LR); for (j = 0; j < l.length / 3; j++) { outsidePoint = Cartesian3.fromArray(l, j * 3, outsidePoint); indices[index++] = start - j - 1; indices[index++] = start - j; CorridorGeometryLibrary.addAttribute( finalPositions, outsidePoint, undefined, back ); back -= 3; } wallIndices.push(start - Math.floor(l.length / 6)); if (cornerType === CornerType$1.BEVELED) { wallIndices.push((back - 2) / 3 + 1); } front += 3; } else { front += 3; start = LR; wallIndices.push(UR); for (j = 0; j < r.length / 3; j++) { outsidePoint = Cartesian3.fromArray(r, j * 3, outsidePoint); indices[index++] = start + j; indices[index++] = start + j + 1; CorridorGeometryLibrary.addAttribute( finalPositions, outsidePoint, front ); front += 3; } wallIndices.push(start + Math.floor(r.length / 6)); if (cornerType === CornerType$1.BEVELED) { wallIndices.push(front / 3 - 1); } back -= 3; } rightEdge = positions[posIndex++]; leftEdge = positions[posIndex++]; rightEdge.splice(0, 3); //remove duplicate points added by corner leftEdge.splice(leftEdge.length - 3, 3); finalPositions.set(rightEdge, front); finalPositions.set(leftEdge, back - leftEdge.length + 1); length = leftEdge.length - 3; for (j = 0; j < leftEdge.length; j += 3) { LR = front / 3; LL = LR - 1; UR = (back - 2) / 3; UL = UR + 1; indices[index++] = UL; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } front -= 3; back += 3; wallIndices.push(front / 3, (back - 2) / 3); } if (addEndPositions) { // add rounded end front += 3; back -= 3; leftPos = cartesian1; rightPos = cartesian2; var lastEndPositions = endPositions[1]; for (i = 0; i < halfLength; i++) { leftPos = Cartesian3.fromArray( lastEndPositions, (endPositionLength - i - 1) * 3, leftPos ); rightPos = Cartesian3.fromArray(lastEndPositions, i * 3, rightPos); CorridorGeometryLibrary.addAttribute( finalPositions, leftPos, undefined, back ); CorridorGeometryLibrary.addAttribute(finalPositions, rightPos, front); LR = front / 3; LL = LR - 1; UR = (back - 2) / 3; UL = UR + 1; indices[index++] = UL; indices[index++] = UR; indices[index++] = LL; indices[index++] = LR; front += 3; back -= 3; } wallIndices.push(front / 3); } else { wallIndices.push(front / 3, (back - 2) / 3); } indices[index++] = front / 3; indices[index++] = (back - 2) / 3; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: finalPositions, }); return { attributes: attributes, indices: indices, wallIndices: wallIndices, }; } function computePositionsExtruded(params) { var ellipsoid = params.ellipsoid; var computedPositions = CorridorGeometryLibrary.computePositions(params); var attr = combine(computedPositions, params.cornerType); var wallIndices = attr.wallIndices; var height = params.height; var extrudedHeight = params.extrudedHeight; var attributes = attr.attributes; var indices = attr.indices; var positions = attributes.position.values; var length = positions.length; var extrudedPositions = new Float64Array(length); extrudedPositions.set(positions); var newPositions = new Float64Array(length * 2); positions = PolygonPipeline.scaleToGeodeticHeight( positions, height, ellipsoid ); extrudedPositions = PolygonPipeline.scaleToGeodeticHeight( extrudedPositions, extrudedHeight, ellipsoid ); newPositions.set(positions); newPositions.set(extrudedPositions, length); attributes.position.values = newPositions; length /= 3; if (defined(params.offsetAttribute)) { var applyOffset = new Uint8Array(length * 2); if (params.offsetAttribute === GeometryOffsetAttribute$1.TOP) { applyOffset = arrayFill(applyOffset, 1, 0, length); } else { var applyOffsetValue = params.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; applyOffset = arrayFill(applyOffset, applyOffsetValue); } attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } var i; var iLength = indices.length; var newIndices = IndexDatatype$1.createTypedArray( newPositions.length / 3, (iLength + wallIndices.length) * 2 ); newIndices.set(indices); var index = iLength; for (i = 0; i < iLength; i += 2) { // bottom indices var v0 = indices[i]; var v1 = indices[i + 1]; newIndices[index++] = v0 + length; newIndices[index++] = v1 + length; } var UL, LL; for (i = 0; i < wallIndices.length; i++) { //wall indices UL = wallIndices[i]; LL = UL + length; newIndices[index++] = UL; newIndices[index++] = LL; } return { attributes: attributes, indices: newIndices, }; } /** * A description of a corridor outline. * * @alias CorridorOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that define the center of the corridor outline. * @param {Number} options.width The distance between the edges of the corridor outline. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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] The distance in meters between the positions and the ellipsoid surface. * @param {Number} [options.extrudedHeight] The distance in meters between the extruded face and the ellipsoid surface. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners. * * @see CorridorOutlineGeometry.createGeometry * * @example * var corridor = new Cesium.CorridorOutlineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([-72.0, 40.0, -70.0, 35.0]), * width : 100000 * }); */ function CorridorOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; var width = options.width; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.positions", positions); Check.typeOf.number("options.width", width); //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._positions = positions; this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._width = width; this._height = Math.max(height, extrudedHeight); this._extrudedHeight = Math.min(height, extrudedHeight); this._cornerType = defaultValue(options.cornerType, CornerType$1.ROUNDED); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._offsetAttribute = options.offsetAttribute; this._workerName = "createCorridorOutlineGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + 6; } /** * Stores the provided instance into the provided array. * * @param {CorridorOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CorridorOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.typeOf.object("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._width; array[startingIndex++] = value._height; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._cornerType; array[startingIndex++] = value._granularity; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchEllipsoid$a = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchOptions$e = { positions: undefined, ellipsoid: scratchEllipsoid$a, width: undefined, height: undefined, extrudedHeight: undefined, cornerType: undefined, granularity: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CorridorOutlineGeometry} [result] The object into which to store the result. * @returns {CorridorOutlineGeometry} The modified result parameter or a new CorridorOutlineGeometry instance if one was not provided. */ CorridorOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var length = array[startingIndex++]; var positions = new Array(length); for (var i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$a); startingIndex += Ellipsoid.packedLength; var width = array[startingIndex++]; var height = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var cornerType = array[startingIndex++]; var granularity = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$e.positions = positions; scratchOptions$e.width = width; scratchOptions$e.height = height; scratchOptions$e.extrudedHeight = extrudedHeight; scratchOptions$e.cornerType = cornerType; scratchOptions$e.granularity = granularity; scratchOptions$e.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new CorridorOutlineGeometry(scratchOptions$e); } result._positions = positions; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._width = width; result._height = height; result._extrudedHeight = extrudedHeight; result._cornerType = cornerType; result._granularity = granularity; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of a corridor, including its vertices, indices, and a bounding sphere. * * @param {CorridorOutlineGeometry} corridorOutlineGeometry A description of the corridor. * @returns {Geometry|undefined} The computed vertices and indices. */ CorridorOutlineGeometry.createGeometry = function (corridorOutlineGeometry) { var positions = corridorOutlineGeometry._positions; var width = corridorOutlineGeometry._width; var ellipsoid = corridorOutlineGeometry._ellipsoid; positions = scaleToSurface(positions, ellipsoid); var cleanPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); if (cleanPositions.length < 2 || width <= 0) { return; } var height = corridorOutlineGeometry._height; var extrudedHeight = corridorOutlineGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( height, extrudedHeight, 0, CesiumMath.EPSILON2 ); var params = { ellipsoid: ellipsoid, positions: cleanPositions, width: width, cornerType: corridorOutlineGeometry._cornerType, granularity: corridorOutlineGeometry._granularity, saveAttributes: false, }; var attr; if (extrude) { params.height = height; params.extrudedHeight = extrudedHeight; params.offsetAttribute = corridorOutlineGeometry._offsetAttribute; attr = computePositionsExtruded(params); } else { var computedPositions = CorridorGeometryLibrary.computePositions(params); attr = combine(computedPositions, params.cornerType); attr.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( attr.attributes.position.values, height, ellipsoid ); if (defined(corridorOutlineGeometry._offsetAttribute)) { var length = attr.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = corridorOutlineGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attr.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } } var attributes = attr.attributes; var boundingSphere = BoundingSphere.fromVertices( attributes.position.values, undefined, 3 ); return new Geometry({ attributes: attributes, indices: attr.indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: boundingSphere, offsetAttribute: corridorOutlineGeometry._offsetAttribute, }); }; /** * The culling volume defined by planes. * * @alias CullingVolume * @constructor * * @param {Cartesian4[]} [planes] An array of clipping planes. */ function CullingVolume(planes) { /** * Each plane is represented by a Cartesian4 object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin. * @type {Cartesian4[]} * @default [] */ this.planes = defaultValue(planes, []); } var faces = [new Cartesian3(), new Cartesian3(), new Cartesian3()]; Cartesian3.clone(Cartesian3.UNIT_X, faces[0]); Cartesian3.clone(Cartesian3.UNIT_Y, faces[1]); Cartesian3.clone(Cartesian3.UNIT_Z, faces[2]); var scratchPlaneCenter = new Cartesian3(); var scratchPlaneNormal = new Cartesian3(); var scratchPlane$1 = new Plane(new Cartesian3(1.0, 0.0, 0.0), 0.0); /** * Constructs a culling volume from a bounding sphere. Creates six planes that create a box containing the sphere. * The planes are aligned to the x, y, and z axes in world coordinates. * * @param {BoundingSphere} boundingSphere The bounding sphere used to create the culling volume. * @param {CullingVolume} [result] The object onto which to store the result. * @returns {CullingVolume} The culling volume created from the bounding sphere. */ CullingVolume.fromBoundingSphere = function (boundingSphere, result) { //>>includeStart('debug', pragmas.debug); if (!defined(boundingSphere)) { throw new DeveloperError("boundingSphere is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new CullingVolume(); } var length = faces.length; var planes = result.planes; planes.length = 2 * length; var center = boundingSphere.center; var radius = boundingSphere.radius; var planeIndex = 0; for (var i = 0; i < length; ++i) { var faceNormal = faces[i]; var plane0 = planes[planeIndex]; var plane1 = planes[planeIndex + 1]; if (!defined(plane0)) { plane0 = planes[planeIndex] = new Cartesian4(); } if (!defined(plane1)) { plane1 = planes[planeIndex + 1] = new Cartesian4(); } Cartesian3.multiplyByScalar(faceNormal, -radius, scratchPlaneCenter); Cartesian3.add(center, scratchPlaneCenter, scratchPlaneCenter); plane0.x = faceNormal.x; plane0.y = faceNormal.y; plane0.z = faceNormal.z; plane0.w = -Cartesian3.dot(faceNormal, scratchPlaneCenter); Cartesian3.multiplyByScalar(faceNormal, radius, scratchPlaneCenter); Cartesian3.add(center, scratchPlaneCenter, scratchPlaneCenter); plane1.x = -faceNormal.x; plane1.y = -faceNormal.y; plane1.z = -faceNormal.z; plane1.w = -Cartesian3.dot( Cartesian3.negate(faceNormal, scratchPlaneNormal), scratchPlaneCenter ); planeIndex += 2; } return result; }; /** * Determines whether a bounding volume intersects the culling volume. * * @param {Object} boundingVolume The bounding volume whose intersection with the culling volume is to be tested. * @returns {Intersect} Intersect.OUTSIDE, Intersect.INTERSECTING, or Intersect.INSIDE. */ CullingVolume.prototype.computeVisibility = function (boundingVolume) { //>>includeStart('debug', pragmas.debug); if (!defined(boundingVolume)) { throw new DeveloperError("boundingVolume is required."); } //>>includeEnd('debug'); var planes = this.planes; var intersecting = false; for (var k = 0, len = planes.length; k < len; ++k) { var result = boundingVolume.intersectPlane( Plane.fromCartesian4(planes[k], scratchPlane$1) ); if (result === Intersect$1.OUTSIDE) { return Intersect$1.OUTSIDE; } else if (result === Intersect$1.INTERSECTING) { intersecting = true; } } return intersecting ? Intersect$1.INTERSECTING : Intersect$1.INSIDE; }; /** * Determines whether a bounding volume intersects the culling volume. * * @param {Object} boundingVolume The bounding volume whose intersection with the culling volume is to be tested. * @param {Number} parentPlaneMask A bit mask from the boundingVolume's parent's check against the same culling * volume, such that if (planeMask & (1 << planeIndex) === 0), for k < 31, then * the parent (and therefore this) volume is completely inside plane[planeIndex] * and that plane check can be skipped. * @returns {Number} A plane mask as described above (which can be applied to this boundingVolume's children). * * @private */ CullingVolume.prototype.computeVisibilityWithPlaneMask = function ( boundingVolume, parentPlaneMask ) { //>>includeStart('debug', pragmas.debug); if (!defined(boundingVolume)) { throw new DeveloperError("boundingVolume is required."); } if (!defined(parentPlaneMask)) { throw new DeveloperError("parentPlaneMask is required."); } //>>includeEnd('debug'); if ( parentPlaneMask === CullingVolume.MASK_OUTSIDE || parentPlaneMask === CullingVolume.MASK_INSIDE ) { // parent is completely outside or completely inside, so this child is as well. return parentPlaneMask; } // Start with MASK_INSIDE (all zeros) so that after the loop, the return value can be compared with MASK_INSIDE. // (Because if there are fewer than 31 planes, the upper bits wont be changed.) var mask = CullingVolume.MASK_INSIDE; var planes = this.planes; for (var k = 0, len = planes.length; k < len; ++k) { // For k greater than 31 (since 31 is the maximum number of INSIDE/INTERSECTING bits we can store), skip the optimization. var flag = k < 31 ? 1 << k : 0; if (k < 31 && (parentPlaneMask & flag) === 0) { // boundingVolume is known to be INSIDE this plane. continue; } var result = boundingVolume.intersectPlane( Plane.fromCartesian4(planes[k], scratchPlane$1) ); if (result === Intersect$1.OUTSIDE) { return CullingVolume.MASK_OUTSIDE; } else if (result === Intersect$1.INTERSECTING) { mask |= flag; } } return mask; }; /** * For plane masks (as used in {@link CullingVolume#computeVisibilityWithPlaneMask}), this special value * represents the case where the object bounding volume is entirely outside the culling volume. * * @type {Number} * @private */ CullingVolume.MASK_OUTSIDE = 0xffffffff; /** * For plane masks (as used in {@link CullingVolume.prototype.computeVisibilityWithPlaneMask}), this value * represents the case where the object bounding volume is entirely inside the culling volume. * * @type {Number} * @private */ CullingVolume.MASK_INSIDE = 0x00000000; /** * For plane masks (as used in {@link CullingVolume.prototype.computeVisibilityWithPlaneMask}), this value * represents the case where the object bounding volume (may) intersect all planes of the culling volume. * * @type {Number} * @private */ CullingVolume.MASK_INDETERMINATE = 0x7fffffff; /** * @private */ var CylinderGeometryLibrary = {}; /** * @private */ CylinderGeometryLibrary.computePositions = function ( length, topRadius, bottomRadius, slices, fill ) { var topZ = length * 0.5; var bottomZ = -topZ; var twoSlice = slices + slices; var size = fill ? 2 * twoSlice : twoSlice; var positions = new Float64Array(size * 3); var i; var index = 0; var tbIndex = 0; var bottomOffset = fill ? twoSlice * 3 : 0; var topOffset = fill ? (twoSlice + slices) * 3 : slices * 3; for (i = 0; i < slices; i++) { var angle = (i / slices) * CesiumMath.TWO_PI; var x = Math.cos(angle); var y = Math.sin(angle); var bottomX = x * bottomRadius; var bottomY = y * bottomRadius; var topX = x * topRadius; var topY = y * topRadius; positions[tbIndex + bottomOffset] = bottomX; positions[tbIndex + bottomOffset + 1] = bottomY; positions[tbIndex + bottomOffset + 2] = bottomZ; positions[tbIndex + topOffset] = topX; positions[tbIndex + topOffset + 1] = topY; positions[tbIndex + topOffset + 2] = topZ; tbIndex += 3; if (fill) { positions[index++] = bottomX; positions[index++] = bottomY; positions[index++] = bottomZ; positions[index++] = topX; positions[index++] = topY; positions[index++] = topZ; } } return positions; }; var radiusScratch$1 = new Cartesian2(); var normalScratch$3 = new Cartesian3(); var bitangentScratch$1 = new Cartesian3(); var tangentScratch$1 = new Cartesian3(); var positionScratch$a = new Cartesian3(); /** * A description of a cylinder. * * @alias CylinderGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Number} options.length The length of the cylinder. * @param {Number} options.topRadius The radius of the top of the cylinder. * @param {Number} options.bottomRadius The radius of the bottom of the cylinder. * @param {Number} [options.slices=128] The number of edges around the perimeter of the cylinder. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @exception {DeveloperError} options.slices must be greater than or equal to 3. * * @see CylinderGeometry.createGeometry * * @example * // create cylinder geometry * var cylinder = new Cesium.CylinderGeometry({ * length: 200000, * topRadius: 80000, * bottomRadius: 200000, * }); * var geometry = Cesium.CylinderGeometry.createGeometry(cylinder); */ function CylinderGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var length = options.length; var topRadius = options.topRadius; var bottomRadius = options.bottomRadius; var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); var slices = defaultValue(options.slices, 128); //>>includeStart('debug', pragmas.debug); if (!defined(length)) { throw new DeveloperError("options.length must be defined."); } if (!defined(topRadius)) { throw new DeveloperError("options.topRadius must be defined."); } if (!defined(bottomRadius)) { throw new DeveloperError("options.bottomRadius must be defined."); } if (slices < 3) { throw new DeveloperError( "options.slices must be greater than or equal to 3." ); } if ( defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute$1.TOP ) { throw new DeveloperError( "GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry." ); } //>>includeEnd('debug'); this._length = length; this._topRadius = topRadius; this._bottomRadius = bottomRadius; this._vertexFormat = VertexFormat.clone(vertexFormat); this._slices = slices; this._offsetAttribute = options.offsetAttribute; this._workerName = "createCylinderGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ CylinderGeometry.packedLength = VertexFormat.packedLength + 5; /** * Stores the provided instance into the provided array. * * @param {CylinderGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CylinderGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._length; array[startingIndex++] = value._topRadius; array[startingIndex++] = value._bottomRadius; array[startingIndex++] = value._slices; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchVertexFormat$8 = new VertexFormat(); var scratchOptions$d = { vertexFormat: scratchVertexFormat$8, length: undefined, topRadius: undefined, bottomRadius: undefined, slices: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CylinderGeometry} [result] The object into which to store the result. * @returns {CylinderGeometry} The modified result parameter or a new CylinderGeometry instance if one was not provided. */ CylinderGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$8 ); startingIndex += VertexFormat.packedLength; var length = array[startingIndex++]; var topRadius = array[startingIndex++]; var bottomRadius = array[startingIndex++]; var slices = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$d.length = length; scratchOptions$d.topRadius = topRadius; scratchOptions$d.bottomRadius = bottomRadius; scratchOptions$d.slices = slices; scratchOptions$d.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new CylinderGeometry(scratchOptions$d); } result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._length = length; result._topRadius = topRadius; result._bottomRadius = bottomRadius; result._slices = slices; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of a cylinder, including its vertices, indices, and a bounding sphere. * * @param {CylinderGeometry} cylinderGeometry A description of the cylinder. * @returns {Geometry|undefined} The computed vertices and indices. */ CylinderGeometry.createGeometry = function (cylinderGeometry) { var length = cylinderGeometry._length; var topRadius = cylinderGeometry._topRadius; var bottomRadius = cylinderGeometry._bottomRadius; var vertexFormat = cylinderGeometry._vertexFormat; var slices = cylinderGeometry._slices; if ( length <= 0 || topRadius < 0 || bottomRadius < 0 || (topRadius === 0 && bottomRadius === 0) ) { return; } var twoSlices = slices + slices; var threeSlices = slices + twoSlices; var numVertices = twoSlices + twoSlices; var positions = CylinderGeometryLibrary.computePositions( length, topRadius, bottomRadius, slices, true ); var st = vertexFormat.st ? new Float32Array(numVertices * 2) : undefined; var normals = vertexFormat.normal ? new Float32Array(numVertices * 3) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(numVertices * 3) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(numVertices * 3) : undefined; var i; var computeNormal = vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent; if (computeNormal) { var computeTangent = vertexFormat.tangent || vertexFormat.bitangent; var normalIndex = 0; var tangentIndex = 0; var bitangentIndex = 0; var theta = Math.atan2(bottomRadius - topRadius, length); var normal = normalScratch$3; normal.z = Math.sin(theta); var normalScale = Math.cos(theta); var tangent = tangentScratch$1; var bitangent = bitangentScratch$1; for (i = 0; i < slices; i++) { var angle = (i / slices) * CesiumMath.TWO_PI; var x = normalScale * Math.cos(angle); var y = normalScale * Math.sin(angle); if (computeNormal) { normal.x = x; normal.y = y; if (computeTangent) { tangent = Cartesian3.normalize( Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent), tangent ); } if (vertexFormat.normal) { normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; } if (vertexFormat.tangent) { tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; } if (vertexFormat.bitangent) { bitangent = Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; } } } for (i = 0; i < slices; i++) { if (vertexFormat.normal) { normals[normalIndex++] = 0; normals[normalIndex++] = 0; normals[normalIndex++] = -1; } if (vertexFormat.tangent) { tangents[tangentIndex++] = 1; tangents[tangentIndex++] = 0; tangents[tangentIndex++] = 0; } if (vertexFormat.bitangent) { bitangents[bitangentIndex++] = 0; bitangents[bitangentIndex++] = -1; bitangents[bitangentIndex++] = 0; } } for (i = 0; i < slices; i++) { if (vertexFormat.normal) { normals[normalIndex++] = 0; normals[normalIndex++] = 0; normals[normalIndex++] = 1; } if (vertexFormat.tangent) { tangents[tangentIndex++] = 1; tangents[tangentIndex++] = 0; tangents[tangentIndex++] = 0; } if (vertexFormat.bitangent) { bitangents[bitangentIndex++] = 0; bitangents[bitangentIndex++] = 1; bitangents[bitangentIndex++] = 0; } } } var numIndices = 12 * slices - 12; var indices = IndexDatatype$1.createTypedArray(numVertices, numIndices); var index = 0; var j = 0; for (i = 0; i < slices - 1; i++) { indices[index++] = j; indices[index++] = j + 2; indices[index++] = j + 3; indices[index++] = j; indices[index++] = j + 3; indices[index++] = j + 1; j += 2; } indices[index++] = twoSlices - 2; indices[index++] = 0; indices[index++] = 1; indices[index++] = twoSlices - 2; indices[index++] = 1; indices[index++] = twoSlices - 1; for (i = 1; i < slices - 1; i++) { indices[index++] = twoSlices + i + 1; indices[index++] = twoSlices + i; indices[index++] = twoSlices; } for (i = 1; i < slices - 1; i++) { indices[index++] = threeSlices; indices[index++] = threeSlices + i; indices[index++] = threeSlices + i + 1; } var textureCoordIndex = 0; if (vertexFormat.st) { var rad = Math.max(topRadius, bottomRadius); for (i = 0; i < numVertices; i++) { var position = Cartesian3.fromArray(positions, i * 3, positionScratch$a); st[textureCoordIndex++] = (position.x + rad) / (2.0 * rad); st[textureCoordIndex++] = (position.y + rad) / (2.0 * rad); } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: st, }); } radiusScratch$1.x = length * 0.5; radiusScratch$1.y = Math.max(bottomRadius, topRadius); var boundingSphere = new BoundingSphere( Cartesian3.ZERO, Cartesian2.magnitude(radiusScratch$1) ); if (defined(cylinderGeometry._offsetAttribute)) { length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = cylinderGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: boundingSphere, offsetAttribute: cylinderGeometry._offsetAttribute, }); }; var unitCylinderGeometry; /** * Returns the geometric representation of a unit cylinder, including its vertices, indices, and a bounding sphere. * @returns {Geometry} The computed vertices and indices. * * @private */ CylinderGeometry.getUnitCylinder = function () { if (!defined(unitCylinderGeometry)) { unitCylinderGeometry = CylinderGeometry.createGeometry( new CylinderGeometry({ topRadius: 1.0, bottomRadius: 1.0, length: 1.0, vertexFormat: VertexFormat.POSITION_ONLY, }) ); } return unitCylinderGeometry; }; var radiusScratch = new Cartesian2(); /** * A description of the outline of a cylinder. * * @alias CylinderOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Number} options.length The length of the cylinder. * @param {Number} options.topRadius The radius of the top of the cylinder. * @param {Number} options.bottomRadius The radius of the bottom of the cylinder. * @param {Number} [options.slices=128] The number of edges around the perimeter of the cylinder. * @param {Number} [options.numberOfVerticalLines=16] Number of lines to draw between the top and bottom surfaces of the cylinder. * * @exception {DeveloperError} options.length must be greater than 0. * @exception {DeveloperError} options.topRadius must be greater than 0. * @exception {DeveloperError} options.bottomRadius must be greater than 0. * @exception {DeveloperError} bottomRadius and topRadius cannot both equal 0. * @exception {DeveloperError} options.slices must be greater than or equal to 3. * * @see CylinderOutlineGeometry.createGeometry * * @example * // create cylinder geometry * var cylinder = new Cesium.CylinderOutlineGeometry({ * length: 200000, * topRadius: 80000, * bottomRadius: 200000, * }); * var geometry = Cesium.CylinderOutlineGeometry.createGeometry(cylinder); */ function CylinderOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var length = options.length; var topRadius = options.topRadius; var bottomRadius = options.bottomRadius; var slices = defaultValue(options.slices, 128); var numberOfVerticalLines = Math.max( defaultValue(options.numberOfVerticalLines, 16), 0 ); //>>includeStart('debug', pragmas.debug); Check.typeOf.number("options.positions", length); Check.typeOf.number("options.topRadius", topRadius); Check.typeOf.number("options.bottomRadius", bottomRadius); Check.typeOf.number.greaterThanOrEquals("options.slices", slices, 3); if ( defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute$1.TOP ) { throw new DeveloperError( "GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry." ); } //>>includeEnd('debug'); this._length = length; this._topRadius = topRadius; this._bottomRadius = bottomRadius; this._slices = slices; this._numberOfVerticalLines = numberOfVerticalLines; this._offsetAttribute = options.offsetAttribute; this._workerName = "createCylinderOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ CylinderOutlineGeometry.packedLength = 6; /** * Stores the provided instance into the provided array. * * @param {CylinderOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ CylinderOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value._length; array[startingIndex++] = value._topRadius; array[startingIndex++] = value._bottomRadius; array[startingIndex++] = value._slices; array[startingIndex++] = value._numberOfVerticalLines; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchOptions$c = { length: undefined, topRadius: undefined, bottomRadius: undefined, slices: undefined, numberOfVerticalLines: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {CylinderOutlineGeometry} [result] The object into which to store the result. * @returns {CylinderOutlineGeometry} The modified result parameter or a new CylinderOutlineGeometry instance if one was not provided. */ CylinderOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var length = array[startingIndex++]; var topRadius = array[startingIndex++]; var bottomRadius = array[startingIndex++]; var slices = array[startingIndex++]; var numberOfVerticalLines = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$c.length = length; scratchOptions$c.topRadius = topRadius; scratchOptions$c.bottomRadius = bottomRadius; scratchOptions$c.slices = slices; scratchOptions$c.numberOfVerticalLines = numberOfVerticalLines; scratchOptions$c.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new CylinderOutlineGeometry(scratchOptions$c); } result._length = length; result._topRadius = topRadius; result._bottomRadius = bottomRadius; result._slices = slices; result._numberOfVerticalLines = numberOfVerticalLines; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of an outline of a cylinder, including its vertices, indices, and a bounding sphere. * * @param {CylinderOutlineGeometry} cylinderGeometry A description of the cylinder outline. * @returns {Geometry|undefined} The computed vertices and indices. */ CylinderOutlineGeometry.createGeometry = function (cylinderGeometry) { var length = cylinderGeometry._length; var topRadius = cylinderGeometry._topRadius; var bottomRadius = cylinderGeometry._bottomRadius; var slices = cylinderGeometry._slices; var numberOfVerticalLines = cylinderGeometry._numberOfVerticalLines; if ( length <= 0 || topRadius < 0 || bottomRadius < 0 || (topRadius === 0 && bottomRadius === 0) ) { return; } var numVertices = slices * 2; var positions = CylinderGeometryLibrary.computePositions( length, topRadius, bottomRadius, slices, false ); var numIndices = slices * 2; var numSide; if (numberOfVerticalLines > 0) { var numSideLines = Math.min(numberOfVerticalLines, slices); numSide = Math.round(slices / numSideLines); numIndices += numSideLines; } var indices = IndexDatatype$1.createTypedArray(numVertices, numIndices * 2); var index = 0; var i; for (i = 0; i < slices - 1; i++) { indices[index++] = i; indices[index++] = i + 1; indices[index++] = i + slices; indices[index++] = i + 1 + slices; } indices[index++] = slices - 1; indices[index++] = 0; indices[index++] = slices + slices - 1; indices[index++] = slices; if (numberOfVerticalLines > 0) { for (i = 0; i < slices; i += numSide) { indices[index++] = i; indices[index++] = i + slices; } } var attributes = new GeometryAttributes(); attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); radiusScratch.x = length * 0.5; radiusScratch.y = Math.max(bottomRadius, topRadius); var boundingSphere = new BoundingSphere( Cartesian3.ZERO, Cartesian2.magnitude(radiusScratch) ); if (defined(cylinderGeometry._offsetAttribute)) { length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = cylinderGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: boundingSphere, offsetAttribute: cylinderGeometry._offsetAttribute, }); }; /** * A simple proxy that appends the desired resource as the sole query parameter * to the given proxy URL. * * @alias DefaultProxy * @constructor * @extends {Proxy} * * @param {String} proxy The proxy URL that will be used to requests all resources. */ function DefaultProxy(proxy) { this.proxy = proxy; } /** * Get the final URL to use to request a given resource. * * @param {String} resource The resource to request. * @returns {String} proxied resource */ DefaultProxy.prototype.getURL = function (resource) { var prefix = this.proxy.indexOf("?") === -1 ? "?" : ""; return this.proxy + prefix + encodeURIComponent(resource); }; /** * Determines visibility based on the distance to the camera. * * @alias DistanceDisplayCondition * @constructor * * @param {Number} [near=0.0] The smallest distance in the interval where the object is visible. * @param {Number} [far=Number.MAX_VALUE] The largest distance in the interval where the object is visible. * * @example * // Make a billboard that is only visible when the distance to the camera is between 10 and 20 meters. * billboard.distanceDisplayCondition = new Cesium.DistanceDisplayCondition(10.0, 20.0); */ function DistanceDisplayCondition(near, far) { near = defaultValue(near, 0.0); this._near = near; far = defaultValue(far, Number.MAX_VALUE); this._far = far; } Object.defineProperties(DistanceDisplayCondition.prototype, { /** * The smallest distance in the interval where the object is visible. * @memberof DistanceDisplayCondition.prototype * @type {Number} * @default 0.0 */ near: { get: function () { return this._near; }, set: function (value) { this._near = value; }, }, /** * The largest distance in the interval where the object is visible. * @memberof DistanceDisplayCondition.prototype * @type {Number} * @default Number.MAX_VALUE */ far: { get: function () { return this._far; }, set: function (value) { this._far = value; }, }, }); /** * The number of elements used to pack the object into an array. * @type {Number} */ DistanceDisplayCondition.packedLength = 2; /** * Stores the provided instance into the provided array. * * @param {DistanceDisplayCondition} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ DistanceDisplayCondition.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.near; array[startingIndex] = value.far; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {DistanceDisplayCondition} [result] The object into which to store the result. * @returns {DistanceDisplayCondition} The modified result parameter or a new DistanceDisplayCondition instance if one was not provided. */ DistanceDisplayCondition.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new DistanceDisplayCondition(); } result.near = array[startingIndex++]; result.far = array[startingIndex]; return result; }; /** * Determines if two distance display conditions are equal. * * @param {DistanceDisplayCondition} left A distance display condition. * @param {DistanceDisplayCondition} right Another distance display condition. * @return {Boolean} Whether the two distance display conditions are equal. */ DistanceDisplayCondition.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.near === right.near && left.far === right.far) ); }; /** * Duplicates a distance display condition instance. * * @param {DistanceDisplayCondition} [value] The distance display condition to duplicate. * @param {DistanceDisplayCondition} [result] The result onto which to store the result. * @return {DistanceDisplayCondition} The duplicated instance. */ DistanceDisplayCondition.clone = function (value, result) { if (!defined(value)) { return undefined; } if (!defined(result)) { result = new DistanceDisplayCondition(); } result.near = value.near; result.far = value.far; return result; }; /** * Duplicates this instance. * * @param {DistanceDisplayCondition} [result] The result onto which to store the result. * @return {DistanceDisplayCondition} The duplicated instance. */ DistanceDisplayCondition.prototype.clone = function (result) { return DistanceDisplayCondition.clone(this, result); }; /** * Determines if this distance display condition is equal to another. * * @param {DistanceDisplayCondition} other Another distance display condition. * @return {Boolean} Whether this distance display condition is equal to the other. */ DistanceDisplayCondition.prototype.equals = function (other) { return DistanceDisplayCondition.equals(this, other); }; /** * Value and type information for per-instance geometry attribute that determines if the geometry instance has a distance display condition. * * @alias DistanceDisplayConditionGeometryInstanceAttribute * @constructor * * @param {Number} [near=0.0] The near distance. * @param {Number} [far=Number.MAX_VALUE] The far distance. * * @exception {DeveloperError} far must be greater than near. * * @example * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.BoxGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL, * minimum : new Cesium.Cartesian3(-250000.0, -250000.0, -250000.0), * maximum : new Cesium.Cartesian3(250000.0, 250000.0, 250000.0) * }), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 1000000.0), new Cesium.Matrix4()), * id : 'box', * attributes : { * distanceDisplayCondition : new Cesium.DistanceDisplayConditionGeometryInstanceAttribute(100.0, 10000.0) * } * }); * * @see GeometryInstance * @see GeometryInstanceAttribute */ function DistanceDisplayConditionGeometryInstanceAttribute(near, far) { near = defaultValue(near, 0.0); far = defaultValue(far, Number.MAX_VALUE); //>>includeStart('debug', pragmas.debug); if (far <= near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); /** * The values for the attributes stored in a typed array. * * @type Float32Array * * @default [0.0, 0.0, Number.MAX_VALUE] */ this.value = new Float32Array([near, far]); } Object.defineProperties( DistanceDisplayConditionGeometryInstanceAttribute.prototype, { /** * The datatype of each component in the attribute, e.g., individual elements in * {@link DistanceDisplayConditionGeometryInstanceAttribute#value}. * * @memberof DistanceDisplayConditionGeometryInstanceAttribute.prototype * * @type {ComponentDatatype} * @readonly * * @default {@link ComponentDatatype.FLOAT} */ componentDatatype: { get: function () { return ComponentDatatype$1.FLOAT; }, }, /** * The number of components in the attributes, i.e., {@link DistanceDisplayConditionGeometryInstanceAttribute#value}. * * @memberof DistanceDisplayConditionGeometryInstanceAttribute.prototype * * @type {Number} * @readonly * * @default 3 */ componentsPerAttribute: { get: function () { return 2; }, }, /** * When true and componentDatatype 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. * * @memberof DistanceDisplayConditionGeometryInstanceAttribute.prototype * * @type {Boolean} * @readonly * * @default false */ normalize: { get: function () { return false; }, }, } ); /** * Creates a new {@link DistanceDisplayConditionGeometryInstanceAttribute} instance given the provided an enabled flag and {@link DistanceDisplayCondition}. * * @param {DistanceDisplayCondition} distanceDisplayCondition The distance display condition. * @returns {DistanceDisplayConditionGeometryInstanceAttribute} The new {@link DistanceDisplayConditionGeometryInstanceAttribute} instance. * * @exception {DeveloperError} distanceDisplayCondition.far must be greater than distanceDisplayCondition.near * * @example * var distanceDisplayCondition = new Cesium.DistanceDisplayCondition(100.0, 10000.0); * var instance = new Cesium.GeometryInstance({ * geometry : geometry, * attributes : { * distanceDisplayCondition : Cesium.DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition(distanceDisplayCondition) * } * }); */ DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition = function ( distanceDisplayCondition ) { //>>includeStart('debug', pragmas.debug); if (!defined(distanceDisplayCondition)) { throw new DeveloperError("distanceDisplayCondition is required."); } if (distanceDisplayCondition.far <= distanceDisplayCondition.near) { throw new DeveloperError( "distanceDisplayCondition.far distance must be greater than distanceDisplayCondition.near distance." ); } //>>includeEnd('debug'); return new DistanceDisplayConditionGeometryInstanceAttribute( distanceDisplayCondition.near, distanceDisplayCondition.far ); }; /** * Converts a distance display condition to a typed array that can be used to assign a distance display condition attribute. * * @param {DistanceDisplayCondition} distanceDisplayCondition The distance display condition value. * @param {Float32Array} [result] The array to store the result in, if undefined a new instance will be created. * @returns {Float32Array} The modified result parameter or a new instance if result was undefined. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.distanceDisplayCondition = Cesium.DistanceDisplayConditionGeometryInstanceAttribute.toValue(distanceDisplayCondition, attributes.distanceDisplayCondition); */ DistanceDisplayConditionGeometryInstanceAttribute.toValue = function ( distanceDisplayCondition, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(distanceDisplayCondition)) { throw new DeveloperError("distanceDisplayCondition is required."); } //>>includeEnd('debug'); if (!defined(result)) { return new Float32Array([ distanceDisplayCondition.near, distanceDisplayCondition.far, ]); } result[0] = distanceDisplayCondition.near; result[1] = distanceDisplayCondition.far; return result; }; /** * @private */ function DoublyLinkedList() { this.head = undefined; this.tail = undefined; this._length = 0; } Object.defineProperties(DoublyLinkedList.prototype, { length: { get: function () { return this._length; }, }, }); /** * @private */ function DoublyLinkedListNode(item, previous, next) { this.item = item; this.previous = previous; this.next = next; } /** * Adds the item to the end of the list * @param {*} [item] * @return {DoublyLinkedListNode} */ DoublyLinkedList.prototype.add = function (item) { var node = new DoublyLinkedListNode(item, this.tail, undefined); if (defined(this.tail)) { this.tail.next = node; this.tail = node; } else { this.head = node; this.tail = node; } ++this._length; return node; }; function remove$1(list, node) { if (defined(node.previous) && defined(node.next)) { node.previous.next = node.next; node.next.previous = node.previous; } else if (defined(node.previous)) { // Remove last node node.previous.next = undefined; list.tail = node.previous; } else if (defined(node.next)) { // Remove first node node.next.previous = undefined; list.head = node.next; } else { // Remove last node in the linked list list.head = undefined; list.tail = undefined; } node.next = undefined; node.previous = undefined; } /** * Removes the given node from the list * @param {DoublyLinkedListNode} node */ DoublyLinkedList.prototype.remove = function (node) { if (!defined(node)) { return; } remove$1(this, node); --this._length; }; /** * Moves nextNode after node * @param {DoublyLinkedListNode} node * @param {DoublyLinkedListNode} nextNode */ DoublyLinkedList.prototype.splice = function (node, nextNode) { if (node === nextNode) { return; } // Remove nextNode, then insert after node remove$1(this, nextNode); var oldNodeNext = node.next; node.next = nextNode; // nextNode is the new tail if (this.tail === node) { this.tail = nextNode; } else { oldNodeNext.previous = nextNode; } nextNode.next = oldNodeNext; nextNode.previous = node; }; /** @license tween.js - https://github.com/sole/tween.js Copyright (c) 2010-2012 Tween.js authors. Easing equations Copyright (c) 2001 Robert Penner http://robertpenner.com/easing/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @author sole / http://soledadpenades.com * @author mrdoob / http://mrdoob.com * @author Robert Eisele / http://www.xarg.org * @author Philippe / http://philippe.elsass.me * @author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html * @author Paul Lewis / http://www.aerotwist.com/ * @author lechecacharro * @author Josh Faul / http://jocafa.com/ * @author egraether / http://egraether.com/ * @author endel / http://endel.me * @author Ben Delarre / http://delarre.net */ // Date.now shim for (ahem) Internet Explo(d|r)er if ( Date.now === undefined ) { Date.now = function () { return new Date().valueOf(); }; } var TWEEN = TWEEN || ( function () { var _tweens = []; return { REVISION: '13', getAll: function () { return _tweens; }, removeAll: function () { _tweens = []; }, add: function ( tween ) { _tweens.push( tween ); }, remove: function ( tween ) { var i = _tweens.indexOf( tween ); if ( i !== -1 ) { _tweens.splice( i, 1 ); } }, update: function ( time ) { if ( _tweens.length === 0 ) return false; var i = 0; time = time !== undefined ? time : ( typeof window !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now() ); while ( i < _tweens.length ) { if ( _tweens[ i ].update( time ) ) { i++; } else { _tweens.splice( i, 1 ); } } return true; } }; } )(); TWEEN.Tween = function ( object ) { var _object = object; var _valuesStart = {}; var _valuesEnd = {}; var _valuesStartRepeat = {}; var _duration = 1000; var _repeat = 0; var _yoyo = false; var _isPlaying = false; var _delayTime = 0; var _startTime = null; var _easingFunction = TWEEN.Easing.Linear.None; var _interpolationFunction = TWEEN.Interpolation.Linear; var _chainedTweens = []; var _onStartCallback = null; var _onStartCallbackFired = false; var _onUpdateCallback = null; var _onCompleteCallback = null; var _onStopCallback = null; // Set all starting values present on the target object for ( var field in object ) { _valuesStart[ field ] = parseFloat(object[field], 10); } this.to = function ( properties, duration ) { if ( duration !== undefined ) { _duration = duration; } _valuesEnd = properties; return this; }; this.start = function ( time ) { TWEEN.add( this ); _isPlaying = true; _onStartCallbackFired = false; _startTime = time !== undefined ? time : ( typeof window !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now() ); _startTime += _delayTime; for ( var property in _valuesEnd ) { // check if an Array was provided as property value if ( _valuesEnd[ property ] instanceof Array ) { if ( _valuesEnd[ property ].length === 0 ) { continue; } // create a local copy of the Array with the start value at the front _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] ); } _valuesStart[ property ] = _object[ property ]; if( ( _valuesStart[ property ] instanceof Array ) === false ) { _valuesStart[ property ] *= 1.0; // Ensures we're using numbers, not strings } _valuesStartRepeat[ property ] = _valuesStart[ property ] || 0; } return this; }; this.stop = function () { if ( !_isPlaying ) { return this; } TWEEN.remove( this ); _isPlaying = false; if ( _onStopCallback !== null ) { _onStopCallback.call( _object ); } this.stopChainedTweens(); return this; }; this.stopChainedTweens = function () { for ( var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++ ) { _chainedTweens[ i ].stop(); } }; this.delay = function ( amount ) { _delayTime = amount; return this; }; this.repeat = function ( times ) { _repeat = times; return this; }; this.yoyo = function( yoyo ) { _yoyo = yoyo; return this; }; this.easing = function ( easing ) { _easingFunction = easing; return this; }; this.interpolation = function ( interpolation ) { _interpolationFunction = interpolation; return this; }; this.chain = function () { _chainedTweens = arguments; return this; }; this.onStart = function ( callback ) { _onStartCallback = callback; return this; }; this.onUpdate = function ( callback ) { _onUpdateCallback = callback; return this; }; this.onComplete = function ( callback ) { _onCompleteCallback = callback; return this; }; this.onStop = function ( callback ) { _onStopCallback = callback; return this; }; this.update = function ( time ) { var property; if ( time < _startTime ) { return true; } if ( _onStartCallbackFired === false ) { if ( _onStartCallback !== null ) { _onStartCallback.call( _object ); } _onStartCallbackFired = true; } var elapsed = ( time - _startTime ) / _duration; elapsed = elapsed > 1 ? 1 : elapsed; var value = _easingFunction( elapsed ); for ( property in _valuesEnd ) { var start = _valuesStart[ property ] || 0; var end = _valuesEnd[ property ]; if ( end instanceof Array ) { _object[ property ] = _interpolationFunction( end, value ); } else { // Parses relative end values with start as base (e.g.: +10, -3) if ( typeof(end) === "string" ) { end = start + parseFloat(end, 10); } // protect against non numeric properties. if ( typeof(end) === "number" ) { _object[ property ] = start + ( end - start ) * value; } } } if ( _onUpdateCallback !== null ) { _onUpdateCallback.call( _object, value ); } if ( elapsed == 1 ) { if ( _repeat > 0 ) { if( isFinite( _repeat ) ) { _repeat--; } // reassign starting values, restart by making startTime = now for( property in _valuesStartRepeat ) { if ( typeof( _valuesEnd[ property ] ) === "string" ) { _valuesStartRepeat[ property ] = _valuesStartRepeat[ property ] + parseFloat(_valuesEnd[ property ], 10); } if (_yoyo) { var tmp = _valuesStartRepeat[ property ]; _valuesStartRepeat[ property ] = _valuesEnd[ property ]; _valuesEnd[ property ] = tmp; } _valuesStart[ property ] = _valuesStartRepeat[ property ]; } _startTime = time + _delayTime; return true; } else { if ( _onCompleteCallback !== null ) { _onCompleteCallback.call( _object ); } for ( var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++ ) { _chainedTweens[ i ].start( time ); } return false; } } return true; }; }; TWEEN.Easing = { Linear: { None: function ( k ) { return k; } }, Quadratic: { In: function ( k ) { return k * k; }, Out: function ( k ) { return k * ( 2 - k ); }, InOut: function ( k ) { if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; return - 0.5 * ( --k * ( k - 2 ) - 1 ); } }, Cubic: { In: function ( k ) { return k * k * k; }, Out: function ( k ) { return --k * k * k + 1; }, InOut: function ( k ) { if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k; return 0.5 * ( ( k -= 2 ) * k * k + 2 ); } }, Quartic: { In: function ( k ) { return k * k * k * k; }, Out: function ( k ) { return 1 - ( --k * k * k * k ); }, InOut: function ( k ) { if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k; return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 ); } }, Quintic: { In: function ( k ) { return k * k * k * k * k; }, Out: function ( k ) { return --k * k * k * k * k + 1; }, InOut: function ( k ) { if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k; return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); } }, Sinusoidal: { In: function ( k ) { return 1 - Math.cos( k * Math.PI / 2 ); }, Out: function ( k ) { return Math.sin( k * Math.PI / 2 ); }, InOut: function ( k ) { return 0.5 * ( 1 - Math.cos( Math.PI * k ) ); } }, Exponential: { In: function ( k ) { return k === 0 ? 0 : Math.pow( 1024, k - 1 ); }, Out: function ( k ) { return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k ); }, InOut: function ( k ) { if ( k === 0 ) return 0; if ( k === 1 ) return 1; if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 ); return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 ); } }, Circular: { In: function ( k ) { return 1 - Math.sqrt( 1 - k * k ); }, Out: function ( k ) { return Math.sqrt( 1 - ( --k * k ) ); }, InOut: function ( k ) { if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1); return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1); } }, Elastic: { In: function ( k ) { var s, a = 0.1, p = 0.4; if ( k === 0 ) return 0; if ( k === 1 ) return 1; if ( !a || a < 1 ) { a = 1; s = p / 4; } else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); }, Out: function ( k ) { var s, a = 0.1, p = 0.4; if ( k === 0 ) return 0; if ( k === 1 ) return 1; if ( !a || a < 1 ) { a = 1; s = p / 4; } else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); }, InOut: function ( k ) { var s, a = 0.1, p = 0.4; if ( k === 0 ) return 0; if ( k === 1 ) return 1; if ( !a || a < 1 ) { a = 1; s = p / 4; } else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; } }, Back: { In: function ( k ) { var s = 1.70158; return k * k * ( ( s + 1 ) * k - s ); }, Out: function ( k ) { var s = 1.70158; return --k * k * ( ( s + 1 ) * k + s ) + 1; }, InOut: function ( k ) { var s = 1.70158 * 1.525; if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); } }, Bounce: { In: function ( k ) { return 1 - TWEEN.Easing.Bounce.Out( 1 - k ); }, Out: function ( k ) { if ( k < ( 1 / 2.75 ) ) { return 7.5625 * k * k; } else if ( k < ( 2 / 2.75 ) ) { return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; } else if ( k < ( 2.5 / 2.75 ) ) { return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; } else { return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; } }, InOut: function ( k ) { if ( k < 0.5 ) return TWEEN.Easing.Bounce.In( k * 2 ) * 0.5; return TWEEN.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5; } } }; TWEEN.Interpolation = { Linear: function ( v, k ) { var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.Linear; if ( k < 0 ) return fn( v[ 0 ], v[ 1 ], f ); if ( k > 1 ) return fn( v[ m ], v[ m - 1 ], m - f ); return fn( v[ i ], v[ i + 1 > m ? m : i + 1 ], f - i ); }, Bezier: function ( v, k ) { var b = 0, n = v.length - 1, pw = Math.pow, bn = TWEEN.Interpolation.Utils.Bernstein, i; for ( i = 0; i <= n; i++ ) { b += pw( 1 - k, n - i ) * pw( k, i ) * v[ i ] * bn( n, i ); } return b; }, CatmullRom: function ( v, k ) { var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.CatmullRom; if ( v[ 0 ] === v[ m ] ) { if ( k < 0 ) i = Math.floor( f = m * ( 1 + k ) ); return fn( v[ ( i - 1 + m ) % m ], v[ i ], v[ ( i + 1 ) % m ], v[ ( i + 2 ) % m ], f - i ); } else { if ( k < 0 ) return v[ 0 ] - ( fn( v[ 0 ], v[ 0 ], v[ 1 ], v[ 1 ], -f ) - v[ 0 ] ); if ( k > 1 ) return v[ m ] - ( fn( v[ m ], v[ m ], v[ m - 1 ], v[ m - 1 ], f - m ) - v[ m ] ); return fn( v[ i ? i - 1 : 0 ], v[ i ], v[ m < i + 1 ? m : i + 1 ], v[ m < i + 2 ? m : i + 2 ], f - i ); } }, Utils: { Linear: function ( p0, p1, t ) { return ( p1 - p0 ) * t + p0; }, Bernstein: function ( n , i ) { var fc = TWEEN.Interpolation.Utils.Factorial; return fc( n ) / fc( i ) / fc( n - i ); }, Factorial: ( function () { var a = [ 1 ]; return function ( n ) { var s = 1, i; if ( a[ n ] ) return a[ n ]; for ( i = n; i > 1; i-- ) s *= i; return a[ n ] = s; }; } )(), CatmullRom: function ( p0, p1, p2, p3, t ) { var v0 = ( p2 - p0 ) * 0.5, v1 = ( p3 - p1 ) * 0.5, t2 = t * t, t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } } }; /** * Easing functions for use with TweenCollection. These function are from * {@link https://github.com/sole/tween.js/|Tween.js} and Robert Penner. See the * {@link http://sole.github.io/tween.js/examples/03_graphs.html|Tween.js graphs for each function}. * * @namespace */ var EasingFunction = { /** * Linear easing. * * @type {EasingFunction.Callback} * @constant */ LINEAR_NONE: TWEEN.Easing.Linear.None, /** * Quadratic in. * * @type {EasingFunction.Callback} * @constant */ QUADRATIC_IN: TWEEN.Easing.Quadratic.In, /** * Quadratic out. * * @type {EasingFunction.Callback} * @constant */ QUADRATIC_OUT: TWEEN.Easing.Quadratic.Out, /** * Quadratic in then out. * * @type {EasingFunction.Callback} * @constant */ QUADRATIC_IN_OUT: TWEEN.Easing.Quadratic.InOut, /** * Cubic in. * * @type {EasingFunction.Callback} * @constant */ CUBIC_IN: TWEEN.Easing.Cubic.In, /** * Cubic out. * * @type {EasingFunction.Callback} * @constant */ CUBIC_OUT: TWEEN.Easing.Cubic.Out, /** * Cubic in then out. * * @type {EasingFunction.Callback} * @constant */ CUBIC_IN_OUT: TWEEN.Easing.Cubic.InOut, /** * Quartic in. * * @type {EasingFunction.Callback} * @constant */ QUARTIC_IN: TWEEN.Easing.Quartic.In, /** * Quartic out. * * @type {EasingFunction.Callback} * @constant */ QUARTIC_OUT: TWEEN.Easing.Quartic.Out, /** * Quartic in then out. * * @type {EasingFunction.Callback} * @constant */ QUARTIC_IN_OUT: TWEEN.Easing.Quartic.InOut, /** * Quintic in. * * @type {EasingFunction.Callback} * @constant */ QUINTIC_IN: TWEEN.Easing.Quintic.In, /** * Quintic out. * * @type {EasingFunction.Callback} * @constant */ QUINTIC_OUT: TWEEN.Easing.Quintic.Out, /** * Quintic in then out. * * @type {EasingFunction.Callback} * @constant */ QUINTIC_IN_OUT: TWEEN.Easing.Quintic.InOut, /** * Sinusoidal in. * * @type {EasingFunction.Callback} * @constant */ SINUSOIDAL_IN: TWEEN.Easing.Sinusoidal.In, /** * Sinusoidal out. * * @type {EasingFunction.Callback} * @constant */ SINUSOIDAL_OUT: TWEEN.Easing.Sinusoidal.Out, /** * Sinusoidal in then out. * * @type {EasingFunction.Callback} * @constant */ SINUSOIDAL_IN_OUT: TWEEN.Easing.Sinusoidal.InOut, /** * Exponential in. * * @type {EasingFunction.Callback} * @constant */ EXPONENTIAL_IN: TWEEN.Easing.Exponential.In, /** * Exponential out. * * @type {EasingFunction.Callback} * @constant */ EXPONENTIAL_OUT: TWEEN.Easing.Exponential.Out, /** * Exponential in then out. * * @type {EasingFunction.Callback} * @constant */ EXPONENTIAL_IN_OUT: TWEEN.Easing.Exponential.InOut, /** * Circular in. * * @type {EasingFunction.Callback} * @constant */ CIRCULAR_IN: TWEEN.Easing.Circular.In, /** * Circular out. * * @type {EasingFunction.Callback} * @constant */ CIRCULAR_OUT: TWEEN.Easing.Circular.Out, /** * Circular in then out. * * @type {EasingFunction.Callback} * @constant */ CIRCULAR_IN_OUT: TWEEN.Easing.Circular.InOut, /** * Elastic in. * * @type {EasingFunction.Callback} * @constant */ ELASTIC_IN: TWEEN.Easing.Elastic.In, /** * Elastic out. * * @type {EasingFunction.Callback} * @constant */ ELASTIC_OUT: TWEEN.Easing.Elastic.Out, /** * Elastic in then out. * * @type {EasingFunction.Callback} * @constant */ ELASTIC_IN_OUT: TWEEN.Easing.Elastic.InOut, /** * Back in. * * @type {EasingFunction.Callback} * @constant */ BACK_IN: TWEEN.Easing.Back.In, /** * Back out. * * @type {EasingFunction.Callback} * @constant */ BACK_OUT: TWEEN.Easing.Back.Out, /** * Back in then out. * * @type {EasingFunction.Callback} * @constant */ BACK_IN_OUT: TWEEN.Easing.Back.InOut, /** * Bounce in. * * @type {EasingFunction.Callback} * @constant */ BOUNCE_IN: TWEEN.Easing.Bounce.In, /** * Bounce out. * * @type {EasingFunction.Callback} * @constant */ BOUNCE_OUT: TWEEN.Easing.Bounce.Out, /** * Bounce in then out. * * @type {EasingFunction.Callback} * @constant */ BOUNCE_IN_OUT: TWEEN.Easing.Bounce.InOut, }; /** * Function interface for implementing a custom easing function. * @callback EasingFunction.Callback * @param {Number} time The time in the range [0, 1]. * @returns {Number} The value of the function at the given time. * * @example * function quadraticIn(time) { * return time * time; * } * * @example * function quadraticOut(time) { * return time * (2.0 - time); * } */ var EasingFunction$1 = Object.freeze(EasingFunction); var scratchPosition$a = new Cartesian3(); var scratchNormal$3 = new Cartesian3(); var scratchTangent$2 = new Cartesian3(); var scratchBitangent$2 = new Cartesian3(); var scratchNormalST = new Cartesian3(); var defaultRadii$1 = new Cartesian3(1.0, 1.0, 1.0); var cos$2 = Math.cos; var sin$2 = Math.sin; /** * A description of an ellipsoid centered at the origin. * * @alias EllipsoidGeometry * @constructor * * @param {Object} [options] Object with the following properties: * @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions. * @param {Cartesian3} [options.innerRadii=options.radii] The inner radii of the ellipsoid in the x, y, and z directions. * @param {Number} [options.minimumClock=0.0] The minimum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis. * @param {Number} [options.maximumClock=2*PI] The maximum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis. * @param {Number} [options.minimumCone=0.0] The minimum angle measured from the positive z-axis and toward the negative z-axis. * @param {Number} [options.maximumCone=PI] The maximum angle measured from the positive z-axis and toward the negative z-axis. * @param {Number} [options.stackPartitions=64] The number of times to partition the ellipsoid into stacks. * @param {Number} [options.slicePartitions=64] The number of times to partition the ellipsoid into radial slices. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @exception {DeveloperError} options.slicePartitions cannot be less than three. * @exception {DeveloperError} options.stackPartitions cannot be less than three. * * @see EllipsoidGeometry#createGeometry * * @example * var ellipsoid = new Cesium.EllipsoidGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY, * radii : new Cesium.Cartesian3(1000000.0, 500000.0, 500000.0) * }); * var geometry = Cesium.EllipsoidGeometry.createGeometry(ellipsoid); */ function EllipsoidGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radii = defaultValue(options.radii, defaultRadii$1); var innerRadii = defaultValue(options.innerRadii, radii); var minimumClock = defaultValue(options.minimumClock, 0.0); var maximumClock = defaultValue(options.maximumClock, CesiumMath.TWO_PI); var minimumCone = defaultValue(options.minimumCone, 0.0); var maximumCone = defaultValue(options.maximumCone, CesiumMath.PI); var stackPartitions = Math.round(defaultValue(options.stackPartitions, 64)); var slicePartitions = Math.round(defaultValue(options.slicePartitions, 64)); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); //>>includeStart('debug', pragmas.debug); if (slicePartitions < 3) { throw new DeveloperError( "options.slicePartitions cannot be less than three." ); } if (stackPartitions < 3) { throw new DeveloperError( "options.stackPartitions cannot be less than three." ); } //>>includeEnd('debug'); this._radii = Cartesian3.clone(radii); this._innerRadii = Cartesian3.clone(innerRadii); this._minimumClock = minimumClock; this._maximumClock = maximumClock; this._minimumCone = minimumCone; this._maximumCone = maximumCone; this._stackPartitions = stackPartitions; this._slicePartitions = slicePartitions; this._vertexFormat = VertexFormat.clone(vertexFormat); this._offsetAttribute = options.offsetAttribute; this._workerName = "createEllipsoidGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ EllipsoidGeometry.packedLength = 2 * Cartesian3.packedLength + VertexFormat.packedLength + 7; /** * Stores the provided instance into the provided array. * * @param {EllipsoidGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ EllipsoidGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._radii, array, startingIndex); startingIndex += Cartesian3.packedLength; Cartesian3.pack(value._innerRadii, array, startingIndex); startingIndex += Cartesian3.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._minimumClock; array[startingIndex++] = value._maximumClock; array[startingIndex++] = value._minimumCone; array[startingIndex++] = value._maximumCone; array[startingIndex++] = value._stackPartitions; array[startingIndex++] = value._slicePartitions; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchRadii$2 = new Cartesian3(); var scratchInnerRadii$1 = new Cartesian3(); var scratchVertexFormat$7 = new VertexFormat(); var scratchOptions$b = { radii: scratchRadii$2, innerRadii: scratchInnerRadii$1, vertexFormat: scratchVertexFormat$7, minimumClock: undefined, maximumClock: undefined, minimumCone: undefined, maximumCone: undefined, stackPartitions: undefined, slicePartitions: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {EllipsoidGeometry} [result] The object into which to store the result. * @returns {EllipsoidGeometry} The modified result parameter or a new EllipsoidGeometry instance if one was not provided. */ EllipsoidGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var radii = Cartesian3.unpack(array, startingIndex, scratchRadii$2); startingIndex += Cartesian3.packedLength; var innerRadii = Cartesian3.unpack(array, startingIndex, scratchInnerRadii$1); startingIndex += Cartesian3.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$7 ); startingIndex += VertexFormat.packedLength; var minimumClock = array[startingIndex++]; var maximumClock = array[startingIndex++]; var minimumCone = array[startingIndex++]; var maximumCone = array[startingIndex++]; var stackPartitions = array[startingIndex++]; var slicePartitions = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$b.minimumClock = minimumClock; scratchOptions$b.maximumClock = maximumClock; scratchOptions$b.minimumCone = minimumCone; scratchOptions$b.maximumCone = maximumCone; scratchOptions$b.stackPartitions = stackPartitions; scratchOptions$b.slicePartitions = slicePartitions; scratchOptions$b.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new EllipsoidGeometry(scratchOptions$b); } result._radii = Cartesian3.clone(radii, result._radii); result._innerRadii = Cartesian3.clone(innerRadii, result._innerRadii); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._minimumClock = minimumClock; result._maximumClock = maximumClock; result._minimumCone = minimumCone; result._maximumCone = maximumCone; result._stackPartitions = stackPartitions; result._slicePartitions = slicePartitions; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {EllipsoidGeometry} ellipsoidGeometry A description of the ellipsoid. * @returns {Geometry|undefined} The computed vertices and indices. */ EllipsoidGeometry.createGeometry = function (ellipsoidGeometry) { var radii = ellipsoidGeometry._radii; if (radii.x <= 0 || radii.y <= 0 || radii.z <= 0) { return; } var innerRadii = ellipsoidGeometry._innerRadii; if (innerRadii.x <= 0 || innerRadii.y <= 0 || innerRadii.z <= 0) { return; } var minimumClock = ellipsoidGeometry._minimumClock; var maximumClock = ellipsoidGeometry._maximumClock; var minimumCone = ellipsoidGeometry._minimumCone; var maximumCone = ellipsoidGeometry._maximumCone; var vertexFormat = ellipsoidGeometry._vertexFormat; // Add an extra slice and stack so that the number of partitions is the // number of surfaces rather than the number of joints var slicePartitions = ellipsoidGeometry._slicePartitions + 1; var stackPartitions = ellipsoidGeometry._stackPartitions + 1; slicePartitions = Math.round( (slicePartitions * Math.abs(maximumClock - minimumClock)) / CesiumMath.TWO_PI ); stackPartitions = Math.round( (stackPartitions * Math.abs(maximumCone - minimumCone)) / CesiumMath.PI ); if (slicePartitions < 2) { slicePartitions = 2; } if (stackPartitions < 2) { stackPartitions = 2; } var i; var j; var index = 0; // Create arrays for theta and phi. Duplicate first and last angle to // allow different normals at the intersections. var phis = [minimumCone]; var thetas = [minimumClock]; for (i = 0; i < stackPartitions; i++) { phis.push( minimumCone + (i * (maximumCone - minimumCone)) / (stackPartitions - 1) ); } phis.push(maximumCone); for (j = 0; j < slicePartitions; j++) { thetas.push( minimumClock + (j * (maximumClock - minimumClock)) / (slicePartitions - 1) ); } thetas.push(maximumClock); var numPhis = phis.length; var numThetas = thetas.length; // Allow for extra indices if there is an inner surface and if we need // to close the sides if the clock range is not a full circle var extraIndices = 0; var vertexMultiplier = 1.0; var hasInnerSurface = innerRadii.x !== radii.x || innerRadii.y !== radii.y || innerRadii.z !== radii.z; var isTopOpen = false; var isBotOpen = false; var isClockOpen = false; if (hasInnerSurface) { vertexMultiplier = 2.0; if (minimumCone > 0.0) { isTopOpen = true; extraIndices += slicePartitions - 1; } if (maximumCone < Math.PI) { isBotOpen = true; extraIndices += slicePartitions - 1; } if ((maximumClock - minimumClock) % CesiumMath.TWO_PI) { isClockOpen = true; extraIndices += (stackPartitions - 1) * 2 + 1; } else { extraIndices += 1; } } var vertexCount = numThetas * numPhis * vertexMultiplier; var positions = new Float64Array(vertexCount * 3); var isInner = arrayFill(new Array(vertexCount), false); var negateNormal = arrayFill(new Array(vertexCount), false); // Multiply by 6 because there are two triangles per sector var indexCount = slicePartitions * stackPartitions * vertexMultiplier; var numIndices = 6 * (indexCount + extraIndices + 1 - (slicePartitions + stackPartitions) * vertexMultiplier); var indices = IndexDatatype$1.createTypedArray(indexCount, numIndices); var normals = vertexFormat.normal ? new Float32Array(vertexCount * 3) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(vertexCount * 3) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(vertexCount * 3) : undefined; var st = vertexFormat.st ? new Float32Array(vertexCount * 2) : undefined; // Calculate sin/cos phi var sinPhi = new Array(numPhis); var cosPhi = new Array(numPhis); for (i = 0; i < numPhis; i++) { sinPhi[i] = sin$2(phis[i]); cosPhi[i] = cos$2(phis[i]); } // Calculate sin/cos theta var sinTheta = new Array(numThetas); var cosTheta = new Array(numThetas); for (j = 0; j < numThetas; j++) { cosTheta[j] = cos$2(thetas[j]); sinTheta[j] = sin$2(thetas[j]); } // Create outer surface for (i = 0; i < numPhis; i++) { for (j = 0; j < numThetas; j++) { positions[index++] = radii.x * sinPhi[i] * cosTheta[j]; positions[index++] = radii.y * sinPhi[i] * sinTheta[j]; positions[index++] = radii.z * cosPhi[i]; } } // Create inner surface var vertexIndex = vertexCount / 2.0; if (hasInnerSurface) { for (i = 0; i < numPhis; i++) { for (j = 0; j < numThetas; j++) { positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j]; positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j]; positions[index++] = innerRadii.z * cosPhi[i]; // Keep track of which vertices are the inner and which ones // need the normal to be negated isInner[vertexIndex] = true; if (i > 0 && i !== numPhis - 1 && j !== 0 && j !== numThetas - 1) { negateNormal[vertexIndex] = true; } vertexIndex++; } } } // Create indices for outer surface index = 0; var topOffset; var bottomOffset; for (i = 1; i < numPhis - 2; i++) { topOffset = i * numThetas; bottomOffset = (i + 1) * numThetas; for (j = 1; j < numThetas - 2; j++) { indices[index++] = bottomOffset + j; indices[index++] = bottomOffset + j + 1; indices[index++] = topOffset + j + 1; indices[index++] = bottomOffset + j; indices[index++] = topOffset + j + 1; indices[index++] = topOffset + j; } } // Create indices for inner surface if (hasInnerSurface) { var offset = numPhis * numThetas; for (i = 1; i < numPhis - 2; i++) { topOffset = offset + i * numThetas; bottomOffset = offset + (i + 1) * numThetas; for (j = 1; j < numThetas - 2; j++) { indices[index++] = bottomOffset + j; indices[index++] = topOffset + j; indices[index++] = topOffset + j + 1; indices[index++] = bottomOffset + j; indices[index++] = topOffset + j + 1; indices[index++] = bottomOffset + j + 1; } } } var outerOffset; var innerOffset; if (hasInnerSurface) { if (isTopOpen) { // Connect the top of the inner surface to the top of the outer surface innerOffset = numPhis * numThetas; for (i = 1; i < numThetas - 2; i++) { indices[index++] = i; indices[index++] = i + 1; indices[index++] = innerOffset + i + 1; indices[index++] = i; indices[index++] = innerOffset + i + 1; indices[index++] = innerOffset + i; } } if (isBotOpen) { // Connect the bottom of the inner surface to the bottom of the outer surface outerOffset = numPhis * numThetas - numThetas; innerOffset = numPhis * numThetas * vertexMultiplier - numThetas; for (i = 1; i < numThetas - 2; i++) { indices[index++] = outerOffset + i + 1; indices[index++] = outerOffset + i; indices[index++] = innerOffset + i; indices[index++] = outerOffset + i + 1; indices[index++] = innerOffset + i; indices[index++] = innerOffset + i + 1; } } } // Connect the edges if clock is not closed if (isClockOpen) { for (i = 1; i < numPhis - 2; i++) { innerOffset = numThetas * numPhis + numThetas * i; outerOffset = numThetas * i; indices[index++] = innerOffset; indices[index++] = outerOffset + numThetas; indices[index++] = outerOffset; indices[index++] = innerOffset; indices[index++] = innerOffset + numThetas; indices[index++] = outerOffset + numThetas; } for (i = 1; i < numPhis - 2; i++) { innerOffset = numThetas * numPhis + numThetas * (i + 1) - 1; outerOffset = numThetas * (i + 1) - 1; indices[index++] = outerOffset + numThetas; indices[index++] = innerOffset; indices[index++] = outerOffset; indices[index++] = outerOffset + numThetas; indices[index++] = innerOffset + numThetas; indices[index++] = innerOffset; } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); } var stIndex = 0; var normalIndex = 0; var tangentIndex = 0; var bitangentIndex = 0; var vertexCountHalf = vertexCount / 2.0; var ellipsoid; var ellipsoidOuter = Ellipsoid.fromCartesian3(radii); var ellipsoidInner = Ellipsoid.fromCartesian3(innerRadii); if ( vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent ) { for (i = 0; i < vertexCount; i++) { ellipsoid = isInner[i] ? ellipsoidInner : ellipsoidOuter; var position = Cartesian3.fromArray(positions, i * 3, scratchPosition$a); var normal = ellipsoid.geodeticSurfaceNormal(position, scratchNormal$3); if (negateNormal[i]) { Cartesian3.negate(normal, normal); } if (vertexFormat.st) { var normalST = Cartesian2.negate(normal, scratchNormalST); st[stIndex++] = Math.atan2(normalST.y, normalST.x) / CesiumMath.TWO_PI + 0.5; st[stIndex++] = Math.asin(normal.z) / Math.PI + 0.5; } if (vertexFormat.normal) { normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; } if (vertexFormat.tangent || vertexFormat.bitangent) { var tangent = scratchTangent$2; // Use UNIT_X for the poles var tangetOffset = 0; var unit; if (isInner[i]) { tangetOffset = vertexCountHalf; } if ( !isTopOpen && i >= tangetOffset && i < tangetOffset + numThetas * 2 ) { unit = Cartesian3.UNIT_X; } else { unit = Cartesian3.UNIT_Z; } Cartesian3.cross(unit, normal, tangent); Cartesian3.normalize(tangent, tangent); if (vertexFormat.tangent) { tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; } if (vertexFormat.bitangent) { var bitangent = Cartesian3.cross(normal, tangent, scratchBitangent$2); Cartesian3.normalize(bitangent, bitangent); bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; } } } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: st, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } } if (defined(ellipsoidGeometry._offsetAttribute)) { var length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = ellipsoidGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: BoundingSphere.fromEllipsoid(ellipsoidOuter), offsetAttribute: ellipsoidGeometry._offsetAttribute, }); }; var unitEllipsoidGeometry; /** * Returns the geometric representation of a unit ellipsoid, including its vertices, indices, and a bounding sphere. * @returns {Geometry} The computed vertices and indices. * * @private */ EllipsoidGeometry.getUnitEllipsoid = function () { if (!defined(unitEllipsoidGeometry)) { unitEllipsoidGeometry = EllipsoidGeometry.createGeometry( new EllipsoidGeometry({ radii: new Cartesian3(1.0, 1.0, 1.0), vertexFormat: VertexFormat.POSITION_ONLY, }) ); } return unitEllipsoidGeometry; }; var defaultRadii = new Cartesian3(1.0, 1.0, 1.0); var cos$1 = Math.cos; var sin$1 = Math.sin; /** * A description of the outline of an ellipsoid centered at the origin. * * @alias EllipsoidOutlineGeometry * @constructor * * @param {Object} [options] Object with the following properties: * @param {Cartesian3} [options.radii=Cartesian3(1.0, 1.0, 1.0)] The radii of the ellipsoid in the x, y, and z directions. * @param {Cartesian3} [options.innerRadii=options.radii] The inner radii of the ellipsoid in the x, y, and z directions. * @param {Number} [options.minimumClock=0.0] The minimum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis. * @param {Number} [options.maximumClock=2*PI] The maximum angle lying in the xy-plane measured from the positive x-axis and toward the positive y-axis. * @param {Number} [options.minimumCone=0.0] The minimum angle measured from the positive z-axis and toward the negative z-axis. * @param {Number} [options.maximumCone=PI] The maximum angle measured from the positive z-axis and toward the negative z-axis. * @param {Number} [options.stackPartitions=10] The count of stacks for the ellipsoid (1 greater than the number of parallel lines). * @param {Number} [options.slicePartitions=8] The count of slices for the ellipsoid (Equal to the number of radial lines). * @param {Number} [options.subdivisions=128] The number of points per line, determining the granularity of the curvature. * * @exception {DeveloperError} options.stackPartitions must be greater than or equal to one. * @exception {DeveloperError} options.slicePartitions must be greater than or equal to zero. * @exception {DeveloperError} options.subdivisions must be greater than or equal to zero. * * @example * var ellipsoid = new Cesium.EllipsoidOutlineGeometry({ * radii : new Cesium.Cartesian3(1000000.0, 500000.0, 500000.0), * stackPartitions: 6, * slicePartitions: 5 * }); * var geometry = Cesium.EllipsoidOutlineGeometry.createGeometry(ellipsoid); */ function EllipsoidOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var radii = defaultValue(options.radii, defaultRadii); var innerRadii = defaultValue(options.innerRadii, radii); var minimumClock = defaultValue(options.minimumClock, 0.0); var maximumClock = defaultValue(options.maximumClock, CesiumMath.TWO_PI); var minimumCone = defaultValue(options.minimumCone, 0.0); var maximumCone = defaultValue(options.maximumCone, CesiumMath.PI); var stackPartitions = Math.round(defaultValue(options.stackPartitions, 10)); var slicePartitions = Math.round(defaultValue(options.slicePartitions, 8)); var subdivisions = Math.round(defaultValue(options.subdivisions, 128)); //>>includeStart('debug', pragmas.debug); if (stackPartitions < 1) { throw new DeveloperError("options.stackPartitions cannot be less than 1"); } if (slicePartitions < 0) { throw new DeveloperError("options.slicePartitions cannot be less than 0"); } if (subdivisions < 0) { throw new DeveloperError( "options.subdivisions must be greater than or equal to zero." ); } if ( defined(options.offsetAttribute) && options.offsetAttribute === GeometryOffsetAttribute$1.TOP ) { throw new DeveloperError( "GeometryOffsetAttribute.TOP is not a supported options.offsetAttribute for this geometry." ); } //>>includeEnd('debug'); this._radii = Cartesian3.clone(radii); this._innerRadii = Cartesian3.clone(innerRadii); this._minimumClock = minimumClock; this._maximumClock = maximumClock; this._minimumCone = minimumCone; this._maximumCone = maximumCone; this._stackPartitions = stackPartitions; this._slicePartitions = slicePartitions; this._subdivisions = subdivisions; this._offsetAttribute = options.offsetAttribute; this._workerName = "createEllipsoidOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ EllipsoidOutlineGeometry.packedLength = 2 * Cartesian3.packedLength + 8; /** * Stores the provided instance into the provided array. * * @param {EllipsoidOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ EllipsoidOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Cartesian3.pack(value._radii, array, startingIndex); startingIndex += Cartesian3.packedLength; Cartesian3.pack(value._innerRadii, array, startingIndex); startingIndex += Cartesian3.packedLength; array[startingIndex++] = value._minimumClock; array[startingIndex++] = value._maximumClock; array[startingIndex++] = value._minimumCone; array[startingIndex++] = value._maximumCone; array[startingIndex++] = value._stackPartitions; array[startingIndex++] = value._slicePartitions; array[startingIndex++] = value._subdivisions; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchRadii$1 = new Cartesian3(); var scratchInnerRadii = new Cartesian3(); var scratchOptions$a = { radii: scratchRadii$1, innerRadii: scratchInnerRadii, minimumClock: undefined, maximumClock: undefined, minimumCone: undefined, maximumCone: undefined, stackPartitions: undefined, slicePartitions: undefined, subdivisions: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {EllipsoidOutlineGeometry} [result] The object into which to store the result. * @returns {EllipsoidOutlineGeometry} The modified result parameter or a new EllipsoidOutlineGeometry instance if one was not provided. */ EllipsoidOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var radii = Cartesian3.unpack(array, startingIndex, scratchRadii$1); startingIndex += Cartesian3.packedLength; var innerRadii = Cartesian3.unpack(array, startingIndex, scratchInnerRadii); startingIndex += Cartesian3.packedLength; var minimumClock = array[startingIndex++]; var maximumClock = array[startingIndex++]; var minimumCone = array[startingIndex++]; var maximumCone = array[startingIndex++]; var stackPartitions = array[startingIndex++]; var slicePartitions = array[startingIndex++]; var subdivisions = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$a.minimumClock = minimumClock; scratchOptions$a.maximumClock = maximumClock; scratchOptions$a.minimumCone = minimumCone; scratchOptions$a.maximumCone = maximumCone; scratchOptions$a.stackPartitions = stackPartitions; scratchOptions$a.slicePartitions = slicePartitions; scratchOptions$a.subdivisions = subdivisions; scratchOptions$a.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new EllipsoidOutlineGeometry(scratchOptions$a); } result._radii = Cartesian3.clone(radii, result._radii); result._innerRadii = Cartesian3.clone(innerRadii, result._innerRadii); result._minimumClock = minimumClock; result._maximumClock = maximumClock; result._minimumCone = minimumCone; result._maximumCone = maximumCone; result._stackPartitions = stackPartitions; result._slicePartitions = slicePartitions; result._subdivisions = subdivisions; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the geometric representation of an outline of an ellipsoid, including its vertices, indices, and a bounding sphere. * * @param {EllipsoidOutlineGeometry} ellipsoidGeometry A description of the ellipsoid outline. * @returns {Geometry|undefined} The computed vertices and indices. */ EllipsoidOutlineGeometry.createGeometry = function (ellipsoidGeometry) { var radii = ellipsoidGeometry._radii; if (radii.x <= 0 || radii.y <= 0 || radii.z <= 0) { return; } var innerRadii = ellipsoidGeometry._innerRadii; if (innerRadii.x <= 0 || innerRadii.y <= 0 || innerRadii.z <= 0) { return; } var minimumClock = ellipsoidGeometry._minimumClock; var maximumClock = ellipsoidGeometry._maximumClock; var minimumCone = ellipsoidGeometry._minimumCone; var maximumCone = ellipsoidGeometry._maximumCone; var subdivisions = ellipsoidGeometry._subdivisions; var ellipsoid = Ellipsoid.fromCartesian3(radii); // Add an extra slice and stack to remain consistent with EllipsoidGeometry var slicePartitions = ellipsoidGeometry._slicePartitions + 1; var stackPartitions = ellipsoidGeometry._stackPartitions + 1; slicePartitions = Math.round( (slicePartitions * Math.abs(maximumClock - minimumClock)) / CesiumMath.TWO_PI ); stackPartitions = Math.round( (stackPartitions * Math.abs(maximumCone - minimumCone)) / CesiumMath.PI ); if (slicePartitions < 2) { slicePartitions = 2; } if (stackPartitions < 2) { stackPartitions = 2; } var extraIndices = 0; var vertexMultiplier = 1.0; var hasInnerSurface = innerRadii.x !== radii.x || innerRadii.y !== radii.y || innerRadii.z !== radii.z; var isTopOpen = false; var isBotOpen = false; if (hasInnerSurface) { vertexMultiplier = 2.0; // Add 2x slicePartitions to connect the top/bottom of the outer to // the top/bottom of the inner if (minimumCone > 0.0) { isTopOpen = true; extraIndices += slicePartitions; } if (maximumCone < Math.PI) { isBotOpen = true; extraIndices += slicePartitions; } } var vertexCount = subdivisions * vertexMultiplier * (stackPartitions + slicePartitions); var positions = new Float64Array(vertexCount * 3); // Multiply by two because two points define each line segment var numIndices = 2 * (vertexCount + extraIndices - (slicePartitions + stackPartitions) * vertexMultiplier); var indices = IndexDatatype$1.createTypedArray(vertexCount, numIndices); var i; var j; var theta; var phi; var index = 0; // Calculate sin/cos phi var sinPhi = new Array(stackPartitions); var cosPhi = new Array(stackPartitions); for (i = 0; i < stackPartitions; i++) { phi = minimumCone + (i * (maximumCone - minimumCone)) / (stackPartitions - 1); sinPhi[i] = sin$1(phi); cosPhi[i] = cos$1(phi); } // Calculate sin/cos theta var sinTheta = new Array(subdivisions); var cosTheta = new Array(subdivisions); for (i = 0; i < subdivisions; i++) { theta = minimumClock + (i * (maximumClock - minimumClock)) / (subdivisions - 1); sinTheta[i] = sin$1(theta); cosTheta[i] = cos$1(theta); } // Calculate the latitude lines on the outer surface for (i = 0; i < stackPartitions; i++) { for (j = 0; j < subdivisions; j++) { positions[index++] = radii.x * sinPhi[i] * cosTheta[j]; positions[index++] = radii.y * sinPhi[i] * sinTheta[j]; positions[index++] = radii.z * cosPhi[i]; } } // Calculate the latitude lines on the inner surface if (hasInnerSurface) { for (i = 0; i < stackPartitions; i++) { for (j = 0; j < subdivisions; j++) { positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j]; positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j]; positions[index++] = innerRadii.z * cosPhi[i]; } } } // Calculate sin/cos phi sinPhi.length = subdivisions; cosPhi.length = subdivisions; for (i = 0; i < subdivisions; i++) { phi = minimumCone + (i * (maximumCone - minimumCone)) / (subdivisions - 1); sinPhi[i] = sin$1(phi); cosPhi[i] = cos$1(phi); } // Calculate sin/cos theta for each slice partition sinTheta.length = slicePartitions; cosTheta.length = slicePartitions; for (i = 0; i < slicePartitions; i++) { theta = minimumClock + (i * (maximumClock - minimumClock)) / (slicePartitions - 1); sinTheta[i] = sin$1(theta); cosTheta[i] = cos$1(theta); } // Calculate the longitude lines on the outer surface for (i = 0; i < subdivisions; i++) { for (j = 0; j < slicePartitions; j++) { positions[index++] = radii.x * sinPhi[i] * cosTheta[j]; positions[index++] = radii.y * sinPhi[i] * sinTheta[j]; positions[index++] = radii.z * cosPhi[i]; } } // Calculate the longitude lines on the inner surface if (hasInnerSurface) { for (i = 0; i < subdivisions; i++) { for (j = 0; j < slicePartitions; j++) { positions[index++] = innerRadii.x * sinPhi[i] * cosTheta[j]; positions[index++] = innerRadii.y * sinPhi[i] * sinTheta[j]; positions[index++] = innerRadii.z * cosPhi[i]; } } } // Create indices for the latitude lines index = 0; for (i = 0; i < stackPartitions * vertexMultiplier; i++) { var topOffset = i * subdivisions; for (j = 0; j < subdivisions - 1; j++) { indices[index++] = topOffset + j; indices[index++] = topOffset + j + 1; } } // Create indices for the outer longitude lines var offset = stackPartitions * subdivisions * vertexMultiplier; for (i = 0; i < slicePartitions; i++) { for (j = 0; j < subdivisions - 1; j++) { indices[index++] = offset + i + j * slicePartitions; indices[index++] = offset + i + (j + 1) * slicePartitions; } } // Create indices for the inner longitude lines if (hasInnerSurface) { offset = stackPartitions * subdivisions * vertexMultiplier + slicePartitions * subdivisions; for (i = 0; i < slicePartitions; i++) { for (j = 0; j < subdivisions - 1; j++) { indices[index++] = offset + i + j * slicePartitions; indices[index++] = offset + i + (j + 1) * slicePartitions; } } } if (hasInnerSurface) { var outerOffset = stackPartitions * subdivisions * vertexMultiplier; var innerOffset = outerOffset + subdivisions * slicePartitions; if (isTopOpen) { // Draw lines from the top of the inner surface to the top of the outer surface for (i = 0; i < slicePartitions; i++) { indices[index++] = outerOffset + i; indices[index++] = innerOffset + i; } } if (isBotOpen) { // Draw lines from the top of the inner surface to the top of the outer surface outerOffset += subdivisions * slicePartitions - slicePartitions; innerOffset += subdivisions * slicePartitions - slicePartitions; for (i = 0; i < slicePartitions; i++) { indices[index++] = outerOffset + i; indices[index++] = innerOffset + i; } } } var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }), }); if (defined(ellipsoidGeometry._offsetAttribute)) { var length = positions.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = ellipsoidGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: BoundingSphere.fromEllipsoid(ellipsoid), offsetAttribute: ellipsoidGeometry._offsetAttribute, }); }; /** * A very simple {@link TerrainProvider} that produces geometry by tessellating an ellipsoidal * surface. * * @alias EllipsoidTerrainProvider * @constructor * * @param {Object} [options] Object with the following properties: * @param {TilingScheme} [options.tilingScheme] The tiling scheme specifying how the ellipsoidal * surface is broken into tiles. If this parameter is not provided, a {@link GeographicTilingScheme} * is used. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If the tilingScheme is specified, * this parameter is ignored and the tiling scheme's ellipsoid is used instead. If neither * parameter is specified, the WGS84 ellipsoid is used. * * @see TerrainProvider */ function EllipsoidTerrainProvider(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._tilingScheme = options.tilingScheme; if (!defined(this._tilingScheme)) { this._tilingScheme = new GeographicTilingScheme({ ellipsoid: defaultValue(options.ellipsoid, Ellipsoid.WGS84), }); } // Note: the 64 below does NOT need to match the actual vertex dimensions, because // the ellipsoid is significantly smoother than actual terrain. this._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( this._tilingScheme.ellipsoid, 64, this._tilingScheme.getNumberOfXTilesAtLevel(0) ); this._errorEvent = new Event(); this._readyPromise = when.resolve(true); } Object.defineProperties(EllipsoidTerrainProvider.prototype, { /** * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof EllipsoidTerrainProvider.prototype * @type {Event} */ errorEvent: { get: function () { return this._errorEvent; }, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should not be called before {@link EllipsoidTerrainProvider#ready} returns true. * @memberof EllipsoidTerrainProvider.prototype * @type {Credit} */ credit: { get: function () { return undefined; }, }, /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link EllipsoidTerrainProvider#ready} returns true. * @memberof EllipsoidTerrainProvider.prototype * @type {GeographicTilingScheme} */ tilingScheme: { get: function () { return this._tilingScheme; }, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof EllipsoidTerrainProvider.prototype * @type {Boolean} */ ready: { get: function () { return true; }, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof EllipsoidTerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise; }, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link EllipsoidTerrainProvider#ready} returns true. * @memberof EllipsoidTerrainProvider.prototype * @type {Boolean} */ hasWaterMask: { get: function () { return false; }, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link EllipsoidTerrainProvider#ready} returns true. * @memberof EllipsoidTerrainProvider.prototype * @type {Boolean} */ hasVertexNormals: { get: function () { return false; }, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link TerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. * @memberof EllipsoidTerrainProvider.prototype * @type {TileAvailability} */ availability: { get: function () { return undefined; }, }, }); /** * Requests the geometry for a given tile. This function should not be called before * {@link TerrainProvider#ready} returns true. The result includes terrain * data and indicates that all child tiles are available. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ EllipsoidTerrainProvider.prototype.requestTileGeometry = function ( x, y, level, request ) { var width = 16; var height = 16; return when.resolve( new HeightmapTerrainData({ buffer: new Uint8Array(width * height), width: width, height: height, }) ); }; /** * Gets the maximum geometric error allowed in a tile at a given level. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ EllipsoidTerrainProvider.prototype.getLevelMaximumGeometricError = function ( level ) { return this._levelZeroMaximumGeometricError / (1 << level); }; /** * Determines whether data for a tile is available to be loaded. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported, otherwise true or false. */ EllipsoidTerrainProvider.prototype.getTileDataAvailable = function ( x, y, level ) { return undefined; }; /** * Makes sure we load availability data for a tile * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ EllipsoidTerrainProvider.prototype.loadTileDataAvailability = function ( x, y, level ) { return undefined; }; /** * A convenience object that simplifies the common pattern of attaching event listeners * to several events, then removing all those listeners at once later, for example, in * a destroy method. * * @alias EventHelper * @constructor * * * @example * var helper = new Cesium.EventHelper(); * * helper.add(someObject.event, listener1, this); * helper.add(otherObject.event, listener2, this); * * // later... * helper.removeAll(); * * @see Event */ function EventHelper() { this._removalFunctions = []; } /** * Adds a listener to an event, and records the registration to be cleaned up later. * * @param {Event} event The event to attach to. * @param {Function} listener The function to be executed when the event is raised. * @param {Object} [scope] An optional object scope to serve as the this * pointer in which the listener function will execute. * @returns {EventHelper.RemoveCallback} A function that will remove this event listener when invoked. * * @see Event#addEventListener */ EventHelper.prototype.add = function (event, listener, scope) { //>>includeStart('debug', pragmas.debug); if (!defined(event)) { throw new DeveloperError("event is required"); } //>>includeEnd('debug'); var removalFunction = event.addEventListener(listener, scope); this._removalFunctions.push(removalFunction); var that = this; return function () { removalFunction(); var removalFunctions = that._removalFunctions; removalFunctions.splice(removalFunctions.indexOf(removalFunction), 1); }; }; /** * Unregisters all previously added listeners. * * @see Event#removeEventListener */ EventHelper.prototype.removeAll = function () { var removalFunctions = this._removalFunctions; for (var i = 0, len = removalFunctions.length; i < len; ++i) { removalFunctions[i](); } removalFunctions.length = 0; }; /** * Constants to determine how an interpolated value is extrapolated * when querying outside the bounds of available data. * * @enum {Number} * * @see SampledProperty */ var ExtrapolationType = { /** * No extrapolation occurs. * * @type {Number} * @constant */ NONE: 0, /** * The first or last value is used when outside the range of sample data. * * @type {Number} * @constant */ HOLD: 1, /** * The value is extrapolated. * * @type {Number} * @constant */ EXTRAPOLATE: 2, }; var ExtrapolationType$1 = Object.freeze(ExtrapolationType); /** * The viewing frustum is defined by 6 planes. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin/camera position. * * @alias OrthographicOffCenterFrustum * @constructor * * @param {Object} [options] An object with the following properties: * @param {Number} [options.left] The left clipping plane distance. * @param {Number} [options.right] The right clipping plane distance. * @param {Number} [options.top] The top clipping plane distance. * @param {Number} [options.bottom] The bottom clipping plane distance. * @param {Number} [options.near=1.0] The near clipping plane distance. * @param {Number} [options.far=500000000.0] The far clipping plane distance. * * @example * var maxRadii = ellipsoid.maximumRadius; * * var frustum = new Cesium.OrthographicOffCenterFrustum(); * frustum.right = maxRadii * Cesium.Math.PI; * frustum.left = -c.frustum.right; * frustum.top = c.frustum.right * (canvas.clientHeight / canvas.clientWidth); * frustum.bottom = -c.frustum.top; * frustum.near = 0.01 * maxRadii; * frustum.far = 50.0 * maxRadii; */ function OrthographicOffCenterFrustum(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The left clipping plane. * @type {Number} * @default undefined */ this.left = options.left; this._left = undefined; /** * The right clipping plane. * @type {Number} * @default undefined */ this.right = options.right; this._right = undefined; /** * The top clipping plane. * @type {Number} * @default undefined */ this.top = options.top; this._top = undefined; /** * The bottom clipping plane. * @type {Number} * @default undefined */ this.bottom = options.bottom; this._bottom = undefined; /** * The distance of the near plane. * @type {Number} * @default 1.0 */ this.near = defaultValue(options.near, 1.0); this._near = this.near; /** * The distance of the far plane. * @type {Number} * @default 500000000.0; */ this.far = defaultValue(options.far, 500000000.0); this._far = this.far; this._cullingVolume = new CullingVolume(); this._orthographicMatrix = new Matrix4(); } function update$5(frustum) { //>>includeStart('debug', pragmas.debug); if ( !defined(frustum.right) || !defined(frustum.left) || !defined(frustum.top) || !defined(frustum.bottom) || !defined(frustum.near) || !defined(frustum.far) ) { throw new DeveloperError( "right, left, top, bottom, near, or far parameters are not set." ); } //>>includeEnd('debug'); if ( frustum.top !== frustum._top || frustum.bottom !== frustum._bottom || frustum.left !== frustum._left || frustum.right !== frustum._right || frustum.near !== frustum._near || frustum.far !== frustum._far ) { //>>includeStart('debug', pragmas.debug); if (frustum.left > frustum.right) { throw new DeveloperError("right must be greater than left."); } if (frustum.bottom > frustum.top) { throw new DeveloperError("top must be greater than bottom."); } if (frustum.near <= 0 || frustum.near > frustum.far) { throw new DeveloperError( "near must be greater than zero and less than far." ); } //>>includeEnd('debug'); frustum._left = frustum.left; frustum._right = frustum.right; frustum._top = frustum.top; frustum._bottom = frustum.bottom; frustum._near = frustum.near; frustum._far = frustum.far; frustum._orthographicMatrix = Matrix4.computeOrthographicOffCenter( frustum.left, frustum.right, frustum.bottom, frustum.top, frustum.near, frustum.far, frustum._orthographicMatrix ); } } Object.defineProperties(OrthographicOffCenterFrustum.prototype, { /** * Gets the orthographic projection matrix computed from the view frustum. * @memberof OrthographicOffCenterFrustum.prototype * @type {Matrix4} * @readonly */ projectionMatrix: { get: function () { update$5(this); return this._orthographicMatrix; }, }, }); var getPlanesRight$1 = new Cartesian3(); var getPlanesNearCenter$1 = new Cartesian3(); var getPlanesPoint = new Cartesian3(); var negateScratch = new Cartesian3(); /** * Creates a culling volume for this frustum. * * @param {Cartesian3} position The eye position. * @param {Cartesian3} direction The view direction. * @param {Cartesian3} up The up direction. * @returns {CullingVolume} A culling volume at the given position and orientation. * * @example * // Check if a bounding volume intersects the frustum. * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp); * var intersect = cullingVolume.computeVisibility(boundingVolume); */ OrthographicOffCenterFrustum.prototype.computeCullingVolume = function ( position, direction, up ) { //>>includeStart('debug', pragmas.debug); if (!defined(position)) { throw new DeveloperError("position is required."); } if (!defined(direction)) { throw new DeveloperError("direction is required."); } if (!defined(up)) { throw new DeveloperError("up is required."); } //>>includeEnd('debug'); var planes = this._cullingVolume.planes; var t = this.top; var b = this.bottom; var r = this.right; var l = this.left; var n = this.near; var f = this.far; var right = Cartesian3.cross(direction, up, getPlanesRight$1); Cartesian3.normalize(right, right); var nearCenter = getPlanesNearCenter$1; Cartesian3.multiplyByScalar(direction, n, nearCenter); Cartesian3.add(position, nearCenter, nearCenter); var point = getPlanesPoint; // Left plane Cartesian3.multiplyByScalar(right, l, point); Cartesian3.add(nearCenter, point, point); var plane = planes[0]; if (!defined(plane)) { plane = planes[0] = new Cartesian4(); } plane.x = right.x; plane.y = right.y; plane.z = right.z; plane.w = -Cartesian3.dot(right, point); // Right plane Cartesian3.multiplyByScalar(right, r, point); Cartesian3.add(nearCenter, point, point); plane = planes[1]; if (!defined(plane)) { plane = planes[1] = new Cartesian4(); } plane.x = -right.x; plane.y = -right.y; plane.z = -right.z; plane.w = -Cartesian3.dot(Cartesian3.negate(right, negateScratch), point); // Bottom plane Cartesian3.multiplyByScalar(up, b, point); Cartesian3.add(nearCenter, point, point); plane = planes[2]; if (!defined(plane)) { plane = planes[2] = new Cartesian4(); } plane.x = up.x; plane.y = up.y; plane.z = up.z; plane.w = -Cartesian3.dot(up, point); // Top plane Cartesian3.multiplyByScalar(up, t, point); Cartesian3.add(nearCenter, point, point); plane = planes[3]; if (!defined(plane)) { plane = planes[3] = new Cartesian4(); } plane.x = -up.x; plane.y = -up.y; plane.z = -up.z; plane.w = -Cartesian3.dot(Cartesian3.negate(up, negateScratch), point); // Near plane plane = planes[4]; if (!defined(plane)) { plane = planes[4] = new Cartesian4(); } plane.x = direction.x; plane.y = direction.y; plane.z = direction.z; plane.w = -Cartesian3.dot(direction, nearCenter); // Far plane Cartesian3.multiplyByScalar(direction, f, point); Cartesian3.add(position, point, point); plane = planes[5]; if (!defined(plane)) { plane = planes[5] = new Cartesian4(); } plane.x = -direction.x; plane.y = -direction.y; plane.z = -direction.z; plane.w = -Cartesian3.dot(Cartesian3.negate(direction, negateScratch), point); return this._cullingVolume; }; /** * Returns the pixel's width and height in meters. * * @param {Number} drawingBufferWidth The width of the drawing buffer. * @param {Number} drawingBufferHeight The height of the drawing buffer. * @param {Number} distance The distance to the near plane in meters. * @param {Number} pixelRatio The scaling factor from pixel space to coordinate space. * @param {Cartesian2} result The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively. * * @exception {DeveloperError} drawingBufferWidth must be greater than zero. * @exception {DeveloperError} drawingBufferHeight must be greater than zero. * @exception {DeveloperError} pixelRatio must be greater than zero. * * @example * // Example 1 * // Get the width and height of a pixel. * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 0.0, scene.pixelRatio, new Cesium.Cartesian2()); */ OrthographicOffCenterFrustum.prototype.getPixelDimensions = function ( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ) { update$5(this); //>>includeStart('debug', pragmas.debug); if (!defined(drawingBufferWidth) || !defined(drawingBufferHeight)) { throw new DeveloperError( "Both drawingBufferWidth and drawingBufferHeight are required." ); } if (drawingBufferWidth <= 0) { throw new DeveloperError("drawingBufferWidth must be greater than zero."); } if (drawingBufferHeight <= 0) { throw new DeveloperError("drawingBufferHeight must be greater than zero."); } if (!defined(distance)) { throw new DeveloperError("distance is required."); } if (!defined(pixelRatio)) { throw new DeveloperError("pixelRatio is required."); } if (pixelRatio <= 0) { throw new DeveloperError("pixelRatio must be greater than zero."); } if (!defined(result)) { throw new DeveloperError("A result object is required."); } //>>includeEnd('debug'); var frustumWidth = this.right - this.left; var frustumHeight = this.top - this.bottom; var pixelWidth = (pixelRatio * frustumWidth) / drawingBufferWidth; var pixelHeight = (pixelRatio * frustumHeight) / drawingBufferHeight; result.x = pixelWidth; result.y = pixelHeight; return result; }; /** * Returns a duplicate of a OrthographicOffCenterFrustum instance. * * @param {OrthographicOffCenterFrustum} [result] The object onto which to store the result. * @returns {OrthographicOffCenterFrustum} The modified result parameter or a new OrthographicOffCenterFrustum instance if one was not provided. */ OrthographicOffCenterFrustum.prototype.clone = function (result) { if (!defined(result)) { result = new OrthographicOffCenterFrustum(); } result.left = this.left; result.right = this.right; result.top = this.top; result.bottom = this.bottom; result.near = this.near; result.far = this.far; // force update of clone to compute matrices result._left = undefined; result._right = undefined; result._top = undefined; result._bottom = undefined; result._near = undefined; result._far = undefined; return result; }; /** * Compares the provided OrthographicOffCenterFrustum componentwise and returns * true if they are equal, false otherwise. * * @param {OrthographicOffCenterFrustum} [other] The right hand side OrthographicOffCenterFrustum. * @returns {Boolean} true if they are equal, false otherwise. */ OrthographicOffCenterFrustum.prototype.equals = function (other) { return ( defined(other) && other instanceof OrthographicOffCenterFrustum && this.right === other.right && this.left === other.left && this.top === other.top && this.bottom === other.bottom && this.near === other.near && this.far === other.far ); }; /** * Compares the provided OrthographicOffCenterFrustum componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {OrthographicOffCenterFrustum} other The right hand side OrthographicOffCenterFrustum. * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if this and other are within the provided epsilon, false otherwise. */ OrthographicOffCenterFrustum.prototype.equalsEpsilon = function ( other, relativeEpsilon, absoluteEpsilon ) { return ( other === this || (defined(other) && other instanceof OrthographicOffCenterFrustum && CesiumMath.equalsEpsilon( this.right, other.right, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.left, other.left, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.top, other.top, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.bottom, other.bottom, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.near, other.near, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.far, other.far, relativeEpsilon, absoluteEpsilon )) ); }; /** * The viewing frustum is defined by 6 planes. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin/camera position. * * @alias OrthographicFrustum * @constructor * * @param {Object} [options] An object with the following properties: * @param {Number} [options.width] The width of the frustum in meters. * @param {Number} [options.aspectRatio] The aspect ratio of the frustum's width to it's height. * @param {Number} [options.near=1.0] The distance of the near plane. * @param {Number} [options.far=500000000.0] The distance of the far plane. * * @example * var maxRadii = ellipsoid.maximumRadius; * * var frustum = new Cesium.OrthographicFrustum(); * frustum.near = 0.01 * maxRadii; * frustum.far = 50.0 * maxRadii; */ function OrthographicFrustum(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._offCenterFrustum = new OrthographicOffCenterFrustum(); /** * The horizontal width of the frustum in meters. * @type {Number} * @default undefined */ this.width = options.width; this._width = undefined; /** * The aspect ratio of the frustum's width to it's height. * @type {Number} * @default undefined */ this.aspectRatio = options.aspectRatio; this._aspectRatio = undefined; /** * The distance of the near plane. * @type {Number} * @default 1.0 */ this.near = defaultValue(options.near, 1.0); this._near = this.near; /** * The distance of the far plane. * @type {Number} * @default 500000000.0; */ this.far = defaultValue(options.far, 500000000.0); this._far = this.far; } /** * The number of elements used to pack the object into an array. * @type {Number} */ OrthographicFrustum.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {OrthographicFrustum} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ OrthographicFrustum.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.width; array[startingIndex++] = value.aspectRatio; array[startingIndex++] = value.near; array[startingIndex] = value.far; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {OrthographicFrustum} [result] The object into which to store the result. * @returns {OrthographicFrustum} The modified result parameter or a new OrthographicFrustum instance if one was not provided. */ OrthographicFrustum.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new OrthographicFrustum(); } result.width = array[startingIndex++]; result.aspectRatio = array[startingIndex++]; result.near = array[startingIndex++]; result.far = array[startingIndex]; return result; }; function update$4(frustum) { //>>includeStart('debug', pragmas.debug); if ( !defined(frustum.width) || !defined(frustum.aspectRatio) || !defined(frustum.near) || !defined(frustum.far) ) { throw new DeveloperError( "width, aspectRatio, near, or far parameters are not set." ); } //>>includeEnd('debug'); var f = frustum._offCenterFrustum; if ( frustum.width !== frustum._width || frustum.aspectRatio !== frustum._aspectRatio || frustum.near !== frustum._near || frustum.far !== frustum._far ) { //>>includeStart('debug', pragmas.debug); if (frustum.aspectRatio < 0) { throw new DeveloperError("aspectRatio must be positive."); } if (frustum.near < 0 || frustum.near > frustum.far) { throw new DeveloperError( "near must be greater than zero and less than far." ); } //>>includeEnd('debug'); frustum._aspectRatio = frustum.aspectRatio; frustum._width = frustum.width; frustum._near = frustum.near; frustum._far = frustum.far; var ratio = 1.0 / frustum.aspectRatio; f.right = frustum.width * 0.5; f.left = -f.right; f.top = ratio * f.right; f.bottom = -f.top; f.near = frustum.near; f.far = frustum.far; } } Object.defineProperties(OrthographicFrustum.prototype, { /** * Gets the orthographic projection matrix computed from the view frustum. * @memberof OrthographicFrustum.prototype * @type {Matrix4} * @readonly */ projectionMatrix: { get: function () { update$4(this); return this._offCenterFrustum.projectionMatrix; }, }, }); /** * Creates a culling volume for this frustum. * * @param {Cartesian3} position The eye position. * @param {Cartesian3} direction The view direction. * @param {Cartesian3} up The up direction. * @returns {CullingVolume} A culling volume at the given position and orientation. * * @example * // Check if a bounding volume intersects the frustum. * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp); * var intersect = cullingVolume.computeVisibility(boundingVolume); */ OrthographicFrustum.prototype.computeCullingVolume = function ( position, direction, up ) { update$4(this); return this._offCenterFrustum.computeCullingVolume(position, direction, up); }; /** * Returns the pixel's width and height in meters. * * @param {Number} drawingBufferWidth The width of the drawing buffer. * @param {Number} drawingBufferHeight The height of the drawing buffer. * @param {Number} distance The distance to the near plane in meters. * @param {Number} pixelRatio The scaling factor from pixel space to coordinate space. * @param {Cartesian2} result The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively. * * @exception {DeveloperError} drawingBufferWidth must be greater than zero. * @exception {DeveloperError} drawingBufferHeight must be greater than zero. * @exception {DeveloperError} pixelRatio must be greater than zero. * * @example * // Example 1 * // Get the width and height of a pixel. * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 0.0, scene.pixelRatio, new Cesium.Cartesian2()); */ OrthographicFrustum.prototype.getPixelDimensions = function ( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ) { update$4(this); return this._offCenterFrustum.getPixelDimensions( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ); }; /** * Returns a duplicate of a OrthographicFrustum instance. * * @param {OrthographicFrustum} [result] The object onto which to store the result. * @returns {OrthographicFrustum} The modified result parameter or a new OrthographicFrustum instance if one was not provided. */ OrthographicFrustum.prototype.clone = function (result) { if (!defined(result)) { result = new OrthographicFrustum(); } result.aspectRatio = this.aspectRatio; result.width = this.width; result.near = this.near; result.far = this.far; // force update of clone to compute matrices result._aspectRatio = undefined; result._width = undefined; result._near = undefined; result._far = undefined; this._offCenterFrustum.clone(result._offCenterFrustum); return result; }; /** * Compares the provided OrthographicFrustum componentwise and returns * true if they are equal, false otherwise. * * @param {OrthographicFrustum} [other] The right hand side OrthographicFrustum. * @returns {Boolean} true if they are equal, false otherwise. */ OrthographicFrustum.prototype.equals = function (other) { if (!defined(other) || !(other instanceof OrthographicFrustum)) { return false; } update$4(this); update$4(other); return ( this.width === other.width && this.aspectRatio === other.aspectRatio && this._offCenterFrustum.equals(other._offCenterFrustum) ); }; /** * Compares the provided OrthographicFrustum componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {OrthographicFrustum} other The right hand side OrthographicFrustum. * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if this and other are within the provided epsilon, false otherwise. */ OrthographicFrustum.prototype.equalsEpsilon = function ( other, relativeEpsilon, absoluteEpsilon ) { if (!defined(other) || !(other instanceof OrthographicFrustum)) { return false; } update$4(this); update$4(other); return ( CesiumMath.equalsEpsilon( this.width, other.width, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.aspectRatio, other.aspectRatio, relativeEpsilon, absoluteEpsilon ) && this._offCenterFrustum.equalsEpsilon( other._offCenterFrustum, relativeEpsilon, absoluteEpsilon ) ); }; /** * The viewing frustum is defined by 6 planes. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin/camera position. * * @alias PerspectiveOffCenterFrustum * @constructor * * @param {Object} [options] An object with the following properties: * @param {Number} [options.left] The left clipping plane distance. * @param {Number} [options.right] The right clipping plane distance. * @param {Number} [options.top] The top clipping plane distance. * @param {Number} [options.bottom] The bottom clipping plane distance. * @param {Number} [options.near=1.0] The near clipping plane distance. * @param {Number} [options.far=500000000.0] The far clipping plane distance. * * @example * var frustum = new Cesium.PerspectiveOffCenterFrustum({ * left : -1.0, * right : 1.0, * top : 1.0, * bottom : -1.0, * near : 1.0, * far : 100.0 * }); * * @see PerspectiveFrustum */ function PerspectiveOffCenterFrustum(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * Defines the left clipping plane. * @type {Number} * @default undefined */ this.left = options.left; this._left = undefined; /** * Defines the right clipping plane. * @type {Number} * @default undefined */ this.right = options.right; this._right = undefined; /** * Defines the top clipping plane. * @type {Number} * @default undefined */ this.top = options.top; this._top = undefined; /** * Defines the bottom clipping plane. * @type {Number} * @default undefined */ this.bottom = options.bottom; this._bottom = undefined; /** * The distance of the near plane. * @type {Number} * @default 1.0 */ this.near = defaultValue(options.near, 1.0); this._near = this.near; /** * The distance of the far plane. * @type {Number} * @default 500000000.0 */ this.far = defaultValue(options.far, 500000000.0); this._far = this.far; this._cullingVolume = new CullingVolume(); this._perspectiveMatrix = new Matrix4(); this._infinitePerspective = new Matrix4(); } function update$3(frustum) { //>>includeStart('debug', pragmas.debug); if ( !defined(frustum.right) || !defined(frustum.left) || !defined(frustum.top) || !defined(frustum.bottom) || !defined(frustum.near) || !defined(frustum.far) ) { throw new DeveloperError( "right, left, top, bottom, near, or far parameters are not set." ); } //>>includeEnd('debug'); var t = frustum.top; var b = frustum.bottom; var r = frustum.right; var l = frustum.left; var n = frustum.near; var f = frustum.far; if ( t !== frustum._top || b !== frustum._bottom || l !== frustum._left || r !== frustum._right || n !== frustum._near || f !== frustum._far ) { //>>includeStart('debug', pragmas.debug); if (frustum.near <= 0 || frustum.near > frustum.far) { throw new DeveloperError( "near must be greater than zero and less than far." ); } //>>includeEnd('debug'); frustum._left = l; frustum._right = r; frustum._top = t; frustum._bottom = b; frustum._near = n; frustum._far = f; frustum._perspectiveMatrix = Matrix4.computePerspectiveOffCenter( l, r, b, t, n, f, frustum._perspectiveMatrix ); frustum._infinitePerspective = Matrix4.computeInfinitePerspectiveOffCenter( l, r, b, t, n, frustum._infinitePerspective ); } } Object.defineProperties(PerspectiveOffCenterFrustum.prototype, { /** * Gets the perspective projection matrix computed from the view frustum. * @memberof PerspectiveOffCenterFrustum.prototype * @type {Matrix4} * @readonly * * @see PerspectiveOffCenterFrustum#infiniteProjectionMatrix */ projectionMatrix: { get: function () { update$3(this); return this._perspectiveMatrix; }, }, /** * Gets the perspective projection matrix computed from the view frustum with an infinite far plane. * @memberof PerspectiveOffCenterFrustum.prototype * @type {Matrix4} * @readonly * * @see PerspectiveOffCenterFrustum#projectionMatrix */ infiniteProjectionMatrix: { get: function () { update$3(this); return this._infinitePerspective; }, }, }); var getPlanesRight = new Cartesian3(); var getPlanesNearCenter = new Cartesian3(); var getPlanesFarCenter = new Cartesian3(); var getPlanesNormal = new Cartesian3(); /** * Creates a culling volume for this frustum. * * @param {Cartesian3} position The eye position. * @param {Cartesian3} direction The view direction. * @param {Cartesian3} up The up direction. * @returns {CullingVolume} A culling volume at the given position and orientation. * * @example * // Check if a bounding volume intersects the frustum. * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp); * var intersect = cullingVolume.computeVisibility(boundingVolume); */ PerspectiveOffCenterFrustum.prototype.computeCullingVolume = function ( position, direction, up ) { //>>includeStart('debug', pragmas.debug); if (!defined(position)) { throw new DeveloperError("position is required."); } if (!defined(direction)) { throw new DeveloperError("direction is required."); } if (!defined(up)) { throw new DeveloperError("up is required."); } //>>includeEnd('debug'); var planes = this._cullingVolume.planes; var t = this.top; var b = this.bottom; var r = this.right; var l = this.left; var n = this.near; var f = this.far; var right = Cartesian3.cross(direction, up, getPlanesRight); var nearCenter = getPlanesNearCenter; Cartesian3.multiplyByScalar(direction, n, nearCenter); Cartesian3.add(position, nearCenter, nearCenter); var farCenter = getPlanesFarCenter; Cartesian3.multiplyByScalar(direction, f, farCenter); Cartesian3.add(position, farCenter, farCenter); var normal = getPlanesNormal; //Left plane computation Cartesian3.multiplyByScalar(right, l, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); Cartesian3.normalize(normal, normal); Cartesian3.cross(normal, up, normal); Cartesian3.normalize(normal, normal); var plane = planes[0]; if (!defined(plane)) { plane = planes[0] = new Cartesian4(); } plane.x = normal.x; plane.y = normal.y; plane.z = normal.z; plane.w = -Cartesian3.dot(normal, position); //Right plane computation Cartesian3.multiplyByScalar(right, r, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); Cartesian3.cross(up, normal, normal); Cartesian3.normalize(normal, normal); plane = planes[1]; if (!defined(plane)) { plane = planes[1] = new Cartesian4(); } plane.x = normal.x; plane.y = normal.y; plane.z = normal.z; plane.w = -Cartesian3.dot(normal, position); //Bottom plane computation Cartesian3.multiplyByScalar(up, b, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); Cartesian3.cross(right, normal, normal); Cartesian3.normalize(normal, normal); plane = planes[2]; if (!defined(plane)) { plane = planes[2] = new Cartesian4(); } plane.x = normal.x; plane.y = normal.y; plane.z = normal.z; plane.w = -Cartesian3.dot(normal, position); //Top plane computation Cartesian3.multiplyByScalar(up, t, normal); Cartesian3.add(nearCenter, normal, normal); Cartesian3.subtract(normal, position, normal); Cartesian3.cross(normal, right, normal); Cartesian3.normalize(normal, normal); plane = planes[3]; if (!defined(plane)) { plane = planes[3] = new Cartesian4(); } plane.x = normal.x; plane.y = normal.y; plane.z = normal.z; plane.w = -Cartesian3.dot(normal, position); //Near plane computation plane = planes[4]; if (!defined(plane)) { plane = planes[4] = new Cartesian4(); } plane.x = direction.x; plane.y = direction.y; plane.z = direction.z; plane.w = -Cartesian3.dot(direction, nearCenter); //Far plane computation Cartesian3.negate(direction, normal); plane = planes[5]; if (!defined(plane)) { plane = planes[5] = new Cartesian4(); } plane.x = normal.x; plane.y = normal.y; plane.z = normal.z; plane.w = -Cartesian3.dot(normal, farCenter); return this._cullingVolume; }; /** * Returns the pixel's width and height in meters. * * @param {Number} drawingBufferWidth The width of the drawing buffer. * @param {Number} drawingBufferHeight The height of the drawing buffer. * @param {Number} distance The distance to the near plane in meters. * @param {Number} pixelRatio The scaling factor from pixel space to coordinate space. * @param {Cartesian2} result The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively. * * @exception {DeveloperError} drawingBufferWidth must be greater than zero. * @exception {DeveloperError} drawingBufferHeight must be greater than zero. * @exception {DeveloperError} pixelRatio must be greater than zero. * * @example * // Example 1 * // Get the width and height of a pixel. * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 1.0, scene.pixelRatio, new Cesium.Cartesian2()); * * @example * // Example 2 * // Get the width and height of a pixel if the near plane was set to 'distance'. * // For example, get the size of a pixel of an image on a billboard. * var position = camera.position; * var direction = camera.direction; * var toCenter = Cesium.Cartesian3.subtract(primitive.boundingVolume.center, position, new Cesium.Cartesian3()); // vector from camera to a primitive * var toCenterProj = Cesium.Cartesian3.multiplyByScalar(direction, Cesium.Cartesian3.dot(direction, toCenter), new Cesium.Cartesian3()); // project vector onto camera direction vector * var distance = Cesium.Cartesian3.magnitude(toCenterProj); * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, distance, scene.pixelRatio, new Cesium.Cartesian2()); */ PerspectiveOffCenterFrustum.prototype.getPixelDimensions = function ( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ) { update$3(this); //>>includeStart('debug', pragmas.debug); if (!defined(drawingBufferWidth) || !defined(drawingBufferHeight)) { throw new DeveloperError( "Both drawingBufferWidth and drawingBufferHeight are required." ); } if (drawingBufferWidth <= 0) { throw new DeveloperError("drawingBufferWidth must be greater than zero."); } if (drawingBufferHeight <= 0) { throw new DeveloperError("drawingBufferHeight must be greater than zero."); } if (!defined(distance)) { throw new DeveloperError("distance is required."); } if (!defined(pixelRatio)) { throw new DeveloperError("pixelRatio is required"); } if (pixelRatio <= 0) { throw new DeveloperError("pixelRatio must be greater than zero."); } if (!defined(result)) { throw new DeveloperError("A result object is required."); } //>>includeEnd('debug'); var inverseNear = 1.0 / this.near; var tanTheta = this.top * inverseNear; var pixelHeight = (2.0 * pixelRatio * distance * tanTheta) / drawingBufferHeight; tanTheta = this.right * inverseNear; var pixelWidth = (2.0 * pixelRatio * distance * tanTheta) / drawingBufferWidth; result.x = pixelWidth; result.y = pixelHeight; return result; }; /** * Returns a duplicate of a PerspectiveOffCenterFrustum instance. * * @param {PerspectiveOffCenterFrustum} [result] The object onto which to store the result. * @returns {PerspectiveOffCenterFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided. */ PerspectiveOffCenterFrustum.prototype.clone = function (result) { if (!defined(result)) { result = new PerspectiveOffCenterFrustum(); } result.right = this.right; result.left = this.left; result.top = this.top; result.bottom = this.bottom; result.near = this.near; result.far = this.far; // force update of clone to compute matrices result._left = undefined; result._right = undefined; result._top = undefined; result._bottom = undefined; result._near = undefined; result._far = undefined; return result; }; /** * Compares the provided PerspectiveOffCenterFrustum componentwise and returns * true if they are equal, false otherwise. * * @param {PerspectiveOffCenterFrustum} [other] The right hand side PerspectiveOffCenterFrustum. * @returns {Boolean} true if they are equal, false otherwise. */ PerspectiveOffCenterFrustum.prototype.equals = function (other) { return ( defined(other) && other instanceof PerspectiveOffCenterFrustum && this.right === other.right && this.left === other.left && this.top === other.top && this.bottom === other.bottom && this.near === other.near && this.far === other.far ); }; /** * Compares the provided PerspectiveOffCenterFrustum componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {PerspectiveOffCenterFrustum} other The right hand side PerspectiveOffCenterFrustum. * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if this and other are within the provided epsilon, false otherwise. */ PerspectiveOffCenterFrustum.prototype.equalsEpsilon = function ( other, relativeEpsilon, absoluteEpsilon ) { return ( other === this || (defined(other) && other instanceof PerspectiveOffCenterFrustum && CesiumMath.equalsEpsilon( this.right, other.right, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.left, other.left, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.top, other.top, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.bottom, other.bottom, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.near, other.near, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.far, other.far, relativeEpsilon, absoluteEpsilon )) ); }; /** * The viewing frustum is defined by 6 planes. * Each plane is represented by a {@link Cartesian4} object, where the x, y, and z components * define the unit vector normal to the plane, and the w component is the distance of the * plane from the origin/camera position. * * @alias PerspectiveFrustum * @constructor * * @param {Object} [options] An object with the following properties: * @param {Number} [options.fov] The angle of the field of view (FOV), in radians. * @param {Number} [options.aspectRatio] The aspect ratio of the frustum's width to it's height. * @param {Number} [options.near=1.0] The distance of the near plane. * @param {Number} [options.far=500000000.0] The distance of the far plane. * @param {Number} [options.xOffset=0.0] The offset in the x direction. * @param {Number} [options.yOffset=0.0] The offset in the y direction. * * @example * var frustum = new Cesium.PerspectiveFrustum({ * fov : Cesium.Math.PI_OVER_THREE, * aspectRatio : canvas.clientWidth / canvas.clientHeight * near : 1.0, * far : 1000.0 * }); * * @see PerspectiveOffCenterFrustum */ function PerspectiveFrustum(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._offCenterFrustum = new PerspectiveOffCenterFrustum(); /** * The angle of the field of view (FOV), in radians. This angle will be used * as the horizontal FOV if the width is greater than the height, otherwise * it will be the vertical FOV. * @type {Number} * @default undefined */ this.fov = options.fov; this._fov = undefined; this._fovy = undefined; this._sseDenominator = undefined; /** * The aspect ratio of the frustum's width to it's height. * @type {Number} * @default undefined */ this.aspectRatio = options.aspectRatio; this._aspectRatio = undefined; /** * The distance of the near plane. * @type {Number} * @default 1.0 */ this.near = defaultValue(options.near, 1.0); this._near = this.near; /** * The distance of the far plane. * @type {Number} * @default 500000000.0 */ this.far = defaultValue(options.far, 500000000.0); this._far = this.far; /** * Offsets the frustum in the x direction. * @type {Number} * @default 0.0 */ this.xOffset = defaultValue(options.xOffset, 0.0); this._xOffset = this.xOffset; /** * Offsets the frustum in the y direction. * @type {Number} * @default 0.0 */ this.yOffset = defaultValue(options.yOffset, 0.0); this._yOffset = this.yOffset; } /** * The number of elements used to pack the object into an array. * @type {Number} */ PerspectiveFrustum.packedLength = 6; /** * Stores the provided instance into the provided array. * * @param {PerspectiveFrustum} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PerspectiveFrustum.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.fov; array[startingIndex++] = value.aspectRatio; array[startingIndex++] = value.near; array[startingIndex++] = value.far; array[startingIndex++] = value.xOffset; array[startingIndex] = value.yOffset; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PerspectiveFrustum} [result] The object into which to store the result. * @returns {PerspectiveFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided. */ PerspectiveFrustum.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new PerspectiveFrustum(); } result.fov = array[startingIndex++]; result.aspectRatio = array[startingIndex++]; result.near = array[startingIndex++]; result.far = array[startingIndex++]; result.xOffset = array[startingIndex++]; result.yOffset = array[startingIndex]; return result; }; function update$2(frustum) { //>>includeStart('debug', pragmas.debug); if ( !defined(frustum.fov) || !defined(frustum.aspectRatio) || !defined(frustum.near) || !defined(frustum.far) ) { throw new DeveloperError( "fov, aspectRatio, near, or far parameters are not set." ); } //>>includeEnd('debug'); var f = frustum._offCenterFrustum; if ( frustum.fov !== frustum._fov || frustum.aspectRatio !== frustum._aspectRatio || frustum.near !== frustum._near || frustum.far !== frustum._far || frustum.xOffset !== frustum._xOffset || frustum.yOffset !== frustum._yOffset ) { //>>includeStart('debug', pragmas.debug); if (frustum.fov < 0 || frustum.fov >= Math.PI) { throw new DeveloperError("fov must be in the range [0, PI)."); } if (frustum.aspectRatio < 0) { throw new DeveloperError("aspectRatio must be positive."); } if (frustum.near < 0 || frustum.near > frustum.far) { throw new DeveloperError( "near must be greater than zero and less than far." ); } //>>includeEnd('debug'); frustum._aspectRatio = frustum.aspectRatio; frustum._fov = frustum.fov; frustum._fovy = frustum.aspectRatio <= 1 ? frustum.fov : Math.atan(Math.tan(frustum.fov * 0.5) / frustum.aspectRatio) * 2.0; frustum._near = frustum.near; frustum._far = frustum.far; frustum._sseDenominator = 2.0 * Math.tan(0.5 * frustum._fovy); frustum._xOffset = frustum.xOffset; frustum._yOffset = frustum.yOffset; f.top = frustum.near * Math.tan(0.5 * frustum._fovy); f.bottom = -f.top; f.right = frustum.aspectRatio * f.top; f.left = -f.right; f.near = frustum.near; f.far = frustum.far; f.right += frustum.xOffset; f.left += frustum.xOffset; f.top += frustum.yOffset; f.bottom += frustum.yOffset; } } Object.defineProperties(PerspectiveFrustum.prototype, { /** * Gets the perspective projection matrix computed from the view frustum. * @memberof PerspectiveFrustum.prototype * @type {Matrix4} * @readonly * * @see PerspectiveFrustum#infiniteProjectionMatrix */ projectionMatrix: { get: function () { update$2(this); return this._offCenterFrustum.projectionMatrix; }, }, /** * The perspective projection matrix computed from the view frustum with an infinite far plane. * @memberof PerspectiveFrustum.prototype * @type {Matrix4} * @readonly * * @see PerspectiveFrustum#projectionMatrix */ infiniteProjectionMatrix: { get: function () { update$2(this); return this._offCenterFrustum.infiniteProjectionMatrix; }, }, /** * Gets the angle of the vertical field of view, in radians. * @memberof PerspectiveFrustum.prototype * @type {Number} * @readonly * @default undefined */ fovy: { get: function () { update$2(this); return this._fovy; }, }, /** * @readonly * @private */ sseDenominator: { get: function () { update$2(this); return this._sseDenominator; }, }, }); /** * Creates a culling volume for this frustum. * * @param {Cartesian3} position The eye position. * @param {Cartesian3} direction The view direction. * @param {Cartesian3} up The up direction. * @returns {CullingVolume} A culling volume at the given position and orientation. * * @example * // Check if a bounding volume intersects the frustum. * var cullingVolume = frustum.computeCullingVolume(cameraPosition, cameraDirection, cameraUp); * var intersect = cullingVolume.computeVisibility(boundingVolume); */ PerspectiveFrustum.prototype.computeCullingVolume = function ( position, direction, up ) { update$2(this); return this._offCenterFrustum.computeCullingVolume(position, direction, up); }; /** * Returns the pixel's width and height in meters. * * @param {Number} drawingBufferWidth The width of the drawing buffer. * @param {Number} drawingBufferHeight The height of the drawing buffer. * @param {Number} distance The distance to the near plane in meters. * @param {Number} pixelRatio The scaling factor from pixel space to coordinate space. * @param {Cartesian2} result The object onto which to store the result. * @returns {Cartesian2} The modified result parameter or a new instance of {@link Cartesian2} with the pixel's width and height in the x and y properties, respectively. * * @exception {DeveloperError} drawingBufferWidth must be greater than zero. * @exception {DeveloperError} drawingBufferHeight must be greater than zero. * @exception {DeveloperError} pixelRatio must be greater than zero. * * @example * // Example 1 * // Get the width and height of a pixel. * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, 1.0, scene.pixelRatio, new Cesium.Cartesian2()); * * @example * // Example 2 * // Get the width and height of a pixel if the near plane was set to 'distance'. * // For example, get the size of a pixel of an image on a billboard. * var position = camera.position; * var direction = camera.direction; * var toCenter = Cesium.Cartesian3.subtract(primitive.boundingVolume.center, position, new Cesium.Cartesian3()); // vector from camera to a primitive * var toCenterProj = Cesium.Cartesian3.multiplyByScalar(direction, Cesium.Cartesian3.dot(direction, toCenter), new Cesium.Cartesian3()); // project vector onto camera direction vector * var distance = Cesium.Cartesian3.magnitude(toCenterProj); * var pixelSize = camera.frustum.getPixelDimensions(scene.drawingBufferWidth, scene.drawingBufferHeight, distance, scene.pixelRatio, new Cesium.Cartesian2()); */ PerspectiveFrustum.prototype.getPixelDimensions = function ( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ) { update$2(this); return this._offCenterFrustum.getPixelDimensions( drawingBufferWidth, drawingBufferHeight, distance, pixelRatio, result ); }; /** * Returns a duplicate of a PerspectiveFrustum instance. * * @param {PerspectiveFrustum} [result] The object onto which to store the result. * @returns {PerspectiveFrustum} The modified result parameter or a new PerspectiveFrustum instance if one was not provided. */ PerspectiveFrustum.prototype.clone = function (result) { if (!defined(result)) { result = new PerspectiveFrustum(); } result.aspectRatio = this.aspectRatio; result.fov = this.fov; result.near = this.near; result.far = this.far; // force update of clone to compute matrices result._aspectRatio = undefined; result._fov = undefined; result._near = undefined; result._far = undefined; this._offCenterFrustum.clone(result._offCenterFrustum); return result; }; /** * Compares the provided PerspectiveFrustum componentwise and returns * true if they are equal, false otherwise. * * @param {PerspectiveFrustum} [other] The right hand side PerspectiveFrustum. * @returns {Boolean} true if they are equal, false otherwise. */ PerspectiveFrustum.prototype.equals = function (other) { if (!defined(other) || !(other instanceof PerspectiveFrustum)) { return false; } update$2(this); update$2(other); return ( this.fov === other.fov && this.aspectRatio === other.aspectRatio && this._offCenterFrustum.equals(other._offCenterFrustum) ); }; /** * Compares the provided PerspectiveFrustum componentwise and returns * true if they pass an absolute or relative tolerance test, * false otherwise. * * @param {PerspectiveFrustum} other The right hand side PerspectiveFrustum. * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. * @returns {Boolean} true if this and other are within the provided epsilon, false otherwise. */ PerspectiveFrustum.prototype.equalsEpsilon = function ( other, relativeEpsilon, absoluteEpsilon ) { if (!defined(other) || !(other instanceof PerspectiveFrustum)) { return false; } update$2(this); update$2(other); return ( CesiumMath.equalsEpsilon( this.fov, other.fov, relativeEpsilon, absoluteEpsilon ) && CesiumMath.equalsEpsilon( this.aspectRatio, other.aspectRatio, relativeEpsilon, absoluteEpsilon ) && this._offCenterFrustum.equalsEpsilon( other._offCenterFrustum, relativeEpsilon, absoluteEpsilon ) ); }; var PERSPECTIVE$1 = 0; var ORTHOGRAPHIC$1 = 1; /** * Describes a frustum at the given the origin and orientation. * * @alias FrustumGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PerspectiveFrustum|OrthographicFrustum} options.frustum The frustum. * @param {Cartesian3} options.origin The origin of the frustum. * @param {Quaternion} options.orientation The orientation of the frustum. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. */ function FrustumGeometry(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.frustum", options.frustum); Check.typeOf.object("options.origin", options.origin); Check.typeOf.object("options.orientation", options.orientation); //>>includeEnd('debug'); var frustum = options.frustum; var orientation = options.orientation; var origin = options.origin; var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); // This is private because it is used by DebugCameraPrimitive to draw a multi-frustum by // creating multiple FrustumGeometrys. This way the near plane of one frustum doesn't overlap // the far plane of another. var drawNearPlane = defaultValue(options._drawNearPlane, true); var frustumType; var frustumPackedLength; if (frustum instanceof PerspectiveFrustum) { frustumType = PERSPECTIVE$1; frustumPackedLength = PerspectiveFrustum.packedLength; } else if (frustum instanceof OrthographicFrustum) { frustumType = ORTHOGRAPHIC$1; frustumPackedLength = OrthographicFrustum.packedLength; } this._frustumType = frustumType; this._frustum = frustum.clone(); this._origin = Cartesian3.clone(origin); this._orientation = Quaternion.clone(orientation); this._drawNearPlane = drawNearPlane; this._vertexFormat = vertexFormat; this._workerName = "createFrustumGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = 2 + frustumPackedLength + Cartesian3.packedLength + Quaternion.packedLength + VertexFormat.packedLength; } /** * Stores the provided instance into the provided array. * * @param {FrustumGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ FrustumGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var frustumType = value._frustumType; var frustum = value._frustum; array[startingIndex++] = frustumType; if (frustumType === PERSPECTIVE$1) { PerspectiveFrustum.pack(frustum, array, startingIndex); startingIndex += PerspectiveFrustum.packedLength; } else { OrthographicFrustum.pack(frustum, array, startingIndex); startingIndex += OrthographicFrustum.packedLength; } Cartesian3.pack(value._origin, array, startingIndex); startingIndex += Cartesian3.packedLength; Quaternion.pack(value._orientation, array, startingIndex); startingIndex += Quaternion.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex] = value._drawNearPlane ? 1.0 : 0.0; return array; }; var scratchPackPerspective$1 = new PerspectiveFrustum(); var scratchPackOrthographic$1 = new OrthographicFrustum(); var scratchPackQuaternion$1 = new Quaternion(); var scratchPackorigin$1 = new Cartesian3(); var scratchVertexFormat$6 = new VertexFormat(); /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {FrustumGeometry} [result] The object into which to store the result. */ FrustumGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var frustumType = array[startingIndex++]; var frustum; if (frustumType === PERSPECTIVE$1) { frustum = PerspectiveFrustum.unpack( array, startingIndex, scratchPackPerspective$1 ); startingIndex += PerspectiveFrustum.packedLength; } else { frustum = OrthographicFrustum.unpack( array, startingIndex, scratchPackOrthographic$1 ); startingIndex += OrthographicFrustum.packedLength; } var origin = Cartesian3.unpack(array, startingIndex, scratchPackorigin$1); startingIndex += Cartesian3.packedLength; var orientation = Quaternion.unpack( array, startingIndex, scratchPackQuaternion$1 ); startingIndex += Quaternion.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$6 ); startingIndex += VertexFormat.packedLength; var drawNearPlane = array[startingIndex] === 1.0; if (!defined(result)) { return new FrustumGeometry({ frustum: frustum, origin: origin, orientation: orientation, vertexFormat: vertexFormat, _drawNearPlane: drawNearPlane, }); } var frustumResult = frustumType === result._frustumType ? result._frustum : undefined; result._frustum = frustum.clone(frustumResult); result._frustumType = frustumType; result._origin = Cartesian3.clone(origin, result._origin); result._orientation = Quaternion.clone(orientation, result._orientation); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._drawNearPlane = drawNearPlane; return result; }; function getAttributes( offset, normals, tangents, bitangents, st, normal, tangent, bitangent ) { var stOffset = (offset / 3) * 2; for (var i = 0; i < 4; ++i) { if (defined(normals)) { normals[offset] = normal.x; normals[offset + 1] = normal.y; normals[offset + 2] = normal.z; } if (defined(tangents)) { tangents[offset] = tangent.x; tangents[offset + 1] = tangent.y; tangents[offset + 2] = tangent.z; } if (defined(bitangents)) { bitangents[offset] = bitangent.x; bitangents[offset + 1] = bitangent.y; bitangents[offset + 2] = bitangent.z; } offset += 3; } st[stOffset] = 0.0; st[stOffset + 1] = 0.0; st[stOffset + 2] = 1.0; st[stOffset + 3] = 0.0; st[stOffset + 4] = 1.0; st[stOffset + 5] = 1.0; st[stOffset + 6] = 0.0; st[stOffset + 7] = 1.0; } var scratchRotationMatrix = new Matrix3(); var scratchViewMatrix = new Matrix4(); var scratchInverseMatrix = new Matrix4(); var scratchXDirection = new Cartesian3(); var scratchYDirection = new Cartesian3(); var scratchZDirection = new Cartesian3(); var scratchNegativeX = new Cartesian3(); var scratchNegativeY = new Cartesian3(); var scratchNegativeZ = new Cartesian3(); var frustumSplits = new Array(3); var frustumCornersNDC$1 = new Array(4); frustumCornersNDC$1[0] = new Cartesian4(-1.0, -1.0, 1.0, 1.0); frustumCornersNDC$1[1] = new Cartesian4(1.0, -1.0, 1.0, 1.0); frustumCornersNDC$1[2] = new Cartesian4(1.0, 1.0, 1.0, 1.0); frustumCornersNDC$1[3] = new Cartesian4(-1.0, 1.0, 1.0, 1.0); var scratchFrustumCorners$1 = new Array(4); for (var i$4 = 0; i$4 < 4; ++i$4) { scratchFrustumCorners$1[i$4] = new Cartesian4(); } FrustumGeometry._computeNearFarPlanes = function ( origin, orientation, frustumType, frustum, positions, xDirection, yDirection, zDirection ) { var rotationMatrix = Matrix3.fromQuaternion( orientation, scratchRotationMatrix ); var x = defaultValue(xDirection, scratchXDirection); var y = defaultValue(yDirection, scratchYDirection); var z = defaultValue(zDirection, scratchZDirection); x = Matrix3.getColumn(rotationMatrix, 0, x); y = Matrix3.getColumn(rotationMatrix, 1, y); z = Matrix3.getColumn(rotationMatrix, 2, z); Cartesian3.normalize(x, x); Cartesian3.normalize(y, y); Cartesian3.normalize(z, z); Cartesian3.negate(x, x); var view = Matrix4.computeView(origin, z, y, x, scratchViewMatrix); var inverseView; var inverseViewProjection; if (frustumType === PERSPECTIVE$1) { var projection = frustum.projectionMatrix; var viewProjection = Matrix4.multiply( projection, view, scratchInverseMatrix ); inverseViewProjection = Matrix4.inverse( viewProjection, scratchInverseMatrix ); } else { inverseView = Matrix4.inverseTransformation(view, scratchInverseMatrix); } if (defined(inverseViewProjection)) { frustumSplits[0] = frustum.near; frustumSplits[1] = frustum.far; } else { frustumSplits[0] = 0.0; frustumSplits[1] = frustum.near; frustumSplits[2] = frustum.far; } for (var i = 0; i < 2; ++i) { for (var j = 0; j < 4; ++j) { var corner = Cartesian4.clone( frustumCornersNDC$1[j], scratchFrustumCorners$1[j] ); if (!defined(inverseViewProjection)) { if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } var near = frustumSplits[i]; var far = frustumSplits[i + 1]; corner.x = (corner.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5; corner.y = (corner.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5; corner.z = (corner.z * (near - far) - near - far) * 0.5; corner.w = 1.0; Matrix4.multiplyByVector(inverseView, corner, corner); } else { corner = Matrix4.multiplyByVector( inverseViewProjection, corner, corner ); // Reverse perspective divide var w = 1.0 / corner.w; Cartesian3.multiplyByScalar(corner, w, corner); Cartesian3.subtract(corner, origin, corner); Cartesian3.normalize(corner, corner); var fac = Cartesian3.dot(z, corner); Cartesian3.multiplyByScalar(corner, frustumSplits[i] / fac, corner); Cartesian3.add(corner, origin, corner); } positions[12 * i + j * 3] = corner.x; positions[12 * i + j * 3 + 1] = corner.y; positions[12 * i + j * 3 + 2] = corner.z; } } }; /** * Computes the geometric representation of a frustum, including its vertices, indices, and a bounding sphere. * * @param {FrustumGeometry} frustumGeometry A description of the frustum. * @returns {Geometry|undefined} The computed vertices and indices. */ FrustumGeometry.createGeometry = function (frustumGeometry) { var frustumType = frustumGeometry._frustumType; var frustum = frustumGeometry._frustum; var origin = frustumGeometry._origin; var orientation = frustumGeometry._orientation; var drawNearPlane = frustumGeometry._drawNearPlane; var vertexFormat = frustumGeometry._vertexFormat; var numberOfPlanes = drawNearPlane ? 6 : 5; var positions = new Float64Array(3 * 4 * 6); FrustumGeometry._computeNearFarPlanes( origin, orientation, frustumType, frustum, positions ); // -x plane var offset = 3 * 4 * 2; positions[offset] = positions[3 * 4]; positions[offset + 1] = positions[3 * 4 + 1]; positions[offset + 2] = positions[3 * 4 + 2]; positions[offset + 3] = positions[0]; positions[offset + 4] = positions[1]; positions[offset + 5] = positions[2]; positions[offset + 6] = positions[3 * 3]; positions[offset + 7] = positions[3 * 3 + 1]; positions[offset + 8] = positions[3 * 3 + 2]; positions[offset + 9] = positions[3 * 7]; positions[offset + 10] = positions[3 * 7 + 1]; positions[offset + 11] = positions[3 * 7 + 2]; // -y plane offset += 3 * 4; positions[offset] = positions[3 * 5]; positions[offset + 1] = positions[3 * 5 + 1]; positions[offset + 2] = positions[3 * 5 + 2]; positions[offset + 3] = positions[3]; positions[offset + 4] = positions[3 + 1]; positions[offset + 5] = positions[3 + 2]; positions[offset + 6] = positions[0]; positions[offset + 7] = positions[1]; positions[offset + 8] = positions[2]; positions[offset + 9] = positions[3 * 4]; positions[offset + 10] = positions[3 * 4 + 1]; positions[offset + 11] = positions[3 * 4 + 2]; // +x plane offset += 3 * 4; positions[offset] = positions[3]; positions[offset + 1] = positions[3 + 1]; positions[offset + 2] = positions[3 + 2]; positions[offset + 3] = positions[3 * 5]; positions[offset + 4] = positions[3 * 5 + 1]; positions[offset + 5] = positions[3 * 5 + 2]; positions[offset + 6] = positions[3 * 6]; positions[offset + 7] = positions[3 * 6 + 1]; positions[offset + 8] = positions[3 * 6 + 2]; positions[offset + 9] = positions[3 * 2]; positions[offset + 10] = positions[3 * 2 + 1]; positions[offset + 11] = positions[3 * 2 + 2]; // +y plane offset += 3 * 4; positions[offset] = positions[3 * 2]; positions[offset + 1] = positions[3 * 2 + 1]; positions[offset + 2] = positions[3 * 2 + 2]; positions[offset + 3] = positions[3 * 6]; positions[offset + 4] = positions[3 * 6 + 1]; positions[offset + 5] = positions[3 * 6 + 2]; positions[offset + 6] = positions[3 * 7]; positions[offset + 7] = positions[3 * 7 + 1]; positions[offset + 8] = positions[3 * 7 + 2]; positions[offset + 9] = positions[3 * 3]; positions[offset + 10] = positions[3 * 3 + 1]; positions[offset + 11] = positions[3 * 3 + 2]; if (!drawNearPlane) { positions = positions.subarray(3 * 4); } var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }), }); if ( defined(vertexFormat.normal) || defined(vertexFormat.tangent) || defined(vertexFormat.bitangent) || defined(vertexFormat.st) ) { var normals = defined(vertexFormat.normal) ? new Float32Array(3 * 4 * numberOfPlanes) : undefined; var tangents = defined(vertexFormat.tangent) ? new Float32Array(3 * 4 * numberOfPlanes) : undefined; var bitangents = defined(vertexFormat.bitangent) ? new Float32Array(3 * 4 * numberOfPlanes) : undefined; var st = defined(vertexFormat.st) ? new Float32Array(2 * 4 * numberOfPlanes) : undefined; var x = scratchXDirection; var y = scratchYDirection; var z = scratchZDirection; var negativeX = Cartesian3.negate(x, scratchNegativeX); var negativeY = Cartesian3.negate(y, scratchNegativeY); var negativeZ = Cartesian3.negate(z, scratchNegativeZ); offset = 0; if (drawNearPlane) { getAttributes(offset, normals, tangents, bitangents, st, negativeZ, x, y); // near offset += 3 * 4; } getAttributes(offset, normals, tangents, bitangents, st, z, negativeX, y); // far offset += 3 * 4; getAttributes( offset, normals, tangents, bitangents, st, negativeX, negativeZ, y ); // -x offset += 3 * 4; getAttributes( offset, normals, tangents, bitangents, st, negativeY, negativeZ, negativeX ); // -y offset += 3 * 4; getAttributes(offset, normals, tangents, bitangents, st, x, z, y); // +x offset += 3 * 4; getAttributes(offset, normals, tangents, bitangents, st, y, z, negativeX); // +y if (defined(normals)) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (defined(tangents)) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (defined(bitangents)) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (defined(st)) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: st, }); } } var indices = new Uint16Array(6 * numberOfPlanes); for (var i = 0; i < numberOfPlanes; ++i) { var indexOffset = i * 6; var index = i * 4; indices[indexOffset] = index; indices[indexOffset + 1] = index + 1; indices[indexOffset + 2] = index + 2; indices[indexOffset + 3] = index; indices[indexOffset + 4] = index + 2; indices[indexOffset + 5] = index + 3; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: BoundingSphere.fromVertices(positions), }); }; var PERSPECTIVE = 0; var ORTHOGRAPHIC = 1; /** * A description of the outline of a frustum with the given the origin and orientation. * * @alias FrustumOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PerspectiveFrustum|OrthographicFrustum} options.frustum The frustum. * @param {Cartesian3} options.origin The origin of the frustum. * @param {Quaternion} options.orientation The orientation of the frustum. */ function FrustumOutlineGeometry(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.frustum", options.frustum); Check.typeOf.object("options.origin", options.origin); Check.typeOf.object("options.orientation", options.orientation); //>>includeEnd('debug'); var frustum = options.frustum; var orientation = options.orientation; var origin = options.origin; // This is private because it is used by DebugCameraPrimitive to draw a multi-frustum by // creating multiple FrustumOutlineGeometrys. This way the near plane of one frustum doesn't overlap // the far plane of another. var drawNearPlane = defaultValue(options._drawNearPlane, true); var frustumType; var frustumPackedLength; if (frustum instanceof PerspectiveFrustum) { frustumType = PERSPECTIVE; frustumPackedLength = PerspectiveFrustum.packedLength; } else if (frustum instanceof OrthographicFrustum) { frustumType = ORTHOGRAPHIC; frustumPackedLength = OrthographicFrustum.packedLength; } this._frustumType = frustumType; this._frustum = frustum.clone(); this._origin = Cartesian3.clone(origin); this._orientation = Quaternion.clone(orientation); this._drawNearPlane = drawNearPlane; this._workerName = "createFrustumOutlineGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = 2 + frustumPackedLength + Cartesian3.packedLength + Quaternion.packedLength; } /** * Stores the provided instance into the provided array. * * @param {FrustumOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ FrustumOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var frustumType = value._frustumType; var frustum = value._frustum; array[startingIndex++] = frustumType; if (frustumType === PERSPECTIVE) { PerspectiveFrustum.pack(frustum, array, startingIndex); startingIndex += PerspectiveFrustum.packedLength; } else { OrthographicFrustum.pack(frustum, array, startingIndex); startingIndex += OrthographicFrustum.packedLength; } Cartesian3.pack(value._origin, array, startingIndex); startingIndex += Cartesian3.packedLength; Quaternion.pack(value._orientation, array, startingIndex); startingIndex += Quaternion.packedLength; array[startingIndex] = value._drawNearPlane ? 1.0 : 0.0; return array; }; var scratchPackPerspective = new PerspectiveFrustum(); var scratchPackOrthographic = new OrthographicFrustum(); var scratchPackQuaternion = new Quaternion(); var scratchPackorigin = new Cartesian3(); /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {FrustumOutlineGeometry} [result] The object into which to store the result. */ FrustumOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var frustumType = array[startingIndex++]; var frustum; if (frustumType === PERSPECTIVE) { frustum = PerspectiveFrustum.unpack( array, startingIndex, scratchPackPerspective ); startingIndex += PerspectiveFrustum.packedLength; } else { frustum = OrthographicFrustum.unpack( array, startingIndex, scratchPackOrthographic ); startingIndex += OrthographicFrustum.packedLength; } var origin = Cartesian3.unpack(array, startingIndex, scratchPackorigin); startingIndex += Cartesian3.packedLength; var orientation = Quaternion.unpack( array, startingIndex, scratchPackQuaternion ); startingIndex += Quaternion.packedLength; var drawNearPlane = array[startingIndex] === 1.0; if (!defined(result)) { return new FrustumOutlineGeometry({ frustum: frustum, origin: origin, orientation: orientation, _drawNearPlane: drawNearPlane, }); } var frustumResult = frustumType === result._frustumType ? result._frustum : undefined; result._frustum = frustum.clone(frustumResult); result._frustumType = frustumType; result._origin = Cartesian3.clone(origin, result._origin); result._orientation = Quaternion.clone(orientation, result._orientation); result._drawNearPlane = drawNearPlane; return result; }; /** * Computes the geometric representation of a frustum outline, including its vertices, indices, and a bounding sphere. * * @param {FrustumOutlineGeometry} frustumGeometry A description of the frustum. * @returns {Geometry|undefined} The computed vertices and indices. */ FrustumOutlineGeometry.createGeometry = function (frustumGeometry) { var frustumType = frustumGeometry._frustumType; var frustum = frustumGeometry._frustum; var origin = frustumGeometry._origin; var orientation = frustumGeometry._orientation; var drawNearPlane = frustumGeometry._drawNearPlane; var positions = new Float64Array(3 * 4 * 2); FrustumGeometry._computeNearFarPlanes( origin, orientation, frustumType, frustum, positions ); var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }), }); var offset; var index; var numberOfPlanes = drawNearPlane ? 2 : 1; var indices = new Uint16Array(8 * (numberOfPlanes + 1)); // Build the near/far planes var i = drawNearPlane ? 0 : 1; for (; i < 2; ++i) { offset = drawNearPlane ? i * 8 : 0; index = i * 4; indices[offset] = index; indices[offset + 1] = index + 1; indices[offset + 2] = index + 1; indices[offset + 3] = index + 2; indices[offset + 4] = index + 2; indices[offset + 5] = index + 3; indices[offset + 6] = index + 3; indices[offset + 7] = index; } // Build the sides of the frustums for (i = 0; i < 2; ++i) { offset = (numberOfPlanes + i) * 8; index = i * 4; indices[offset] = index; indices[offset + 1] = index + 4; indices[offset + 2] = index + 1; indices[offset + 3] = index + 5; indices[offset + 4] = index + 2; indices[offset + 5] = index + 6; indices[offset + 6] = index + 3; indices[offset + 7] = index + 7; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: BoundingSphere.fromVertices(positions), }); }; /** * The type of geocoding to be performed by a {@link GeocoderService}. * @enum {Number} * @see Geocoder */ var GeocodeType = { /** * Perform a search where the input is considered complete. * * @type {Number} * @constant */ SEARCH: 0, /** * Perform an auto-complete using partial input, typically * reserved for providing possible results as a user is typing. * * @type {Number} * @constant */ AUTOCOMPLETE: 1, }; var GeocodeType$1 = Object.freeze(GeocodeType); /** * @typedef {Object} GeocoderService.Result * @property {String} displayName The display name for a location * @property {Rectangle|Cartesian3} destination The bounding box for a location */ /** * Provides geocoding through an external service. This type describes an interface and * is not intended to be used. * @alias GeocoderService * @constructor * * @see BingMapsGeocoderService * @see PeliasGeocoderService * @see OpenCageGeocoderService */ function GeocoderService() {} /** * @function * * @param {String} query The query to be sent to the geocoder service * @param {GeocodeType} [type=GeocodeType.SEARCH] The type of geocode to perform. * @returns {Promise} */ GeocoderService.prototype.geocode = DeveloperError.throwInstantiationError; /** * Base class for all geometry creation utility classes that can be passed to {@link GeometryInstance} * for asynchronous geometry creation. * * @constructor * @class * @abstract */ function GeometryFactory() { DeveloperError.throwInstantiationError(); } /** * Returns a geometry. * * @param {GeometryFactory} geometryFactory A description of the circle. * @returns {Geometry|undefined} The computed vertices and indices. */ GeometryFactory.createGeometry = function (geometryFactory) { DeveloperError.throwInstantiationError(); }; /** * Values and type information for per-instance geometry attributes. * * @alias GeometryInstanceAttribute * @constructor * * @param {Object} options Object with the following properties: * @param {ComponentDatatype} options.componentDatatype The datatype of each component in the attribute, e.g., individual elements in values. * @param {Number} options.componentsPerAttribute A number between 1 and 4 that defines the number of components in an attributes. * @param {Boolean} [options.normalize=false] When true and componentDatatype 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 {Number[]} options.value The value for the attribute. * * @exception {DeveloperError} options.componentsPerAttribute must be between 1 and 4. * * * @example * var instance = new Cesium.GeometryInstance({ * geometry : Cesium.BoxGeometry.fromDimensions({ * dimensions : new Cesium.Cartesian3(1000000.0, 1000000.0, 500000.0) * }), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(0.0, 0.0)), new Cesium.Cartesian3(0.0, 0.0, 1000000.0), new Cesium.Matrix4()), * id : 'box', * attributes : { * color : new Cesium.GeometryInstanceAttribute({ * componentDatatype : Cesium.ComponentDatatype.UNSIGNED_BYTE, * componentsPerAttribute : 4, * normalize : true, * value : [255, 255, 0, 255] * }) * } * }); * * @see ColorGeometryInstanceAttribute * @see ShowGeometryInstanceAttribute * @see DistanceDisplayConditionGeometryInstanceAttribute */ function GeometryInstanceAttribute(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); 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.value)) { throw new DeveloperError("options.value is required."); } //>>includeEnd('debug'); /** * The datatype of each component in the attribute, e.g., individual elements in * {@link GeometryInstanceAttribute#value}. * * @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 * show : new Cesium.GeometryInstanceAttribute({ * componentDatatype : Cesium.ComponentDatatype.UNSIGNED_BYTE, * componentsPerAttribute : 1, * normalize : true, * value : [1.0] * }) */ this.componentsPerAttribute = options.componentsPerAttribute; /** * When true and componentDatatype 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. *

* This is commonly used when storing colors using {@link ComponentDatatype.UNSIGNED_BYTE}. *

* * @type Boolean * * @default false * * @example * attribute.componentDatatype = Cesium.ComponentDatatype.UNSIGNED_BYTE; * attribute.componentsPerAttribute = 4; * attribute.normalize = true; * attribute.value = [ * Cesium.Color.floatToByte(color.red), * Cesium.Color.floatToByte(color.green), * Cesium.Color.floatToByte(color.blue), * Cesium.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 values defines one attributes since * componentsPerAttribute is 3. * * @type {Number[]} * * @default undefined * * @example * show : new Cesium.GeometryInstanceAttribute({ * componentDatatype : Cesium.ComponentDatatype.UNSIGNED_BYTE, * componentsPerAttribute : 1, * normalize : true, * value : [1.0] * }) */ this.value = options.value; } var tmp$6 = {}; /*! * protobuf.js v6.7.0 (c) 2016, Daniel Wirtz * Compiled Wed, 22 Mar 2017 17:30:26 UTC * Licensed under the BSD-3-Clause License * see: https://github.com/dcodeIO/protobuf.js for details */ (function(global,undefined$1){(function prelude(modules, cache, entries) { // This is the prelude used to bundle protobuf.js for the browser. Wraps up the CommonJS // sources through a conflict-free require shim and is again wrapped within an iife that // provides a unified `global` and a minification-friendly `undefined` var plus a global // "use strict" directive so that minification can remove the directives of each module. function $require(name) { var $module = cache[name]; if (!$module) modules[name][0].call($module = cache[name] = { exports: {} }, $require, $module, $module.exports); return $module.exports; } // Expose globally global.protobuf = $require(entries[0]); // Commented out to avoid polluing the global scope in Node.js // // Be nice to AMD // if (typeof define === "function" && define.amd) // define([], function() { // protobuf.configure(); // return protobuf; // }); // // Be nice to CommonJS // if (typeof module === "object" && module && module.exports) // module.exports = protobuf; })/* end of prelude */({1:[function(require,module,exports){ module.exports = asPromise; /** * Returns a promise from a node-style callback function. * @memberof util * @param {function(?Error, ...*)} fn Function to call * @param {*} ctx Function context * @param {...*} params Function arguments * @returns {Promise<*>} Promisified function */ function asPromise(fn, ctx/*, varargs */) { var params = []; for (var i = 2; i < arguments.length;) params.push(arguments[i++]); var pending = true; return new Promise(function asPromiseExecutor(resolve, reject) { params.push(function asPromiseCallback(err/*, varargs */) { if (pending) { pending = false; if (err) reject(err); else { var args = []; for (var i = 1; i < arguments.length;) args.push(arguments[i++]); resolve.apply(null, args); } } }); try { fn.apply(ctx || this, params); // eslint-disable-line no-invalid-this } catch (err) { if (pending) { pending = false; reject(err); } } }); } },{}],2:[function(require,module,exports){ /** * A minimal base64 implementation for number arrays. * @memberof util * @namespace */ var base64 = exports; /** * Calculates the byte length of a base64 encoded string. * @param {string} string Base64 encoded string * @returns {number} Byte length */ base64.length = function length(string) { var p = string.length; if (!p) return 0; var n = 0; while (--p % 4 > 1 && string.charAt(p) === "=") ++n; return Math.ceil(string.length * 3) / 4 - n; }; // Base64 encoding table var b64 = new Array(64); // Base64 decoding table var s64 = new Array(123); // 65..90, 97..122, 48..57, 43, 47 for (var i = 0; i < 64;) s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++; /** * Encodes a buffer to a base64 encoded string. * @param {Uint8Array} buffer Source buffer * @param {number} start Source start * @param {number} end Source end * @returns {string} Base64 encoded string */ base64.encode = function encode(buffer, start, end) { var string = []; // alt: new Array(Math.ceil((end - start) / 3) * 4); var i = 0, // output index j = 0, // goto index t; // temporary while (start < end) { var b = buffer[start++]; switch (j) { case 0: string[i++] = b64[b >> 2]; t = (b & 3) << 4; j = 1; break; case 1: string[i++] = b64[t | b >> 4]; t = (b & 15) << 2; j = 2; break; case 2: string[i++] = b64[t | b >> 6]; string[i++] = b64[b & 63]; j = 0; break; } } if (j) { string[i++] = b64[t]; string[i ] = 61; if (j === 1) string[i + 1] = 61; } return String.fromCharCode.apply(String, string); }; var invalidEncoding = "invalid encoding"; /** * Decodes a base64 encoded string to a buffer. * @param {string} string Source string * @param {Uint8Array} buffer Destination buffer * @param {number} offset Destination offset * @returns {number} Number of bytes written * @throws {Error} If encoding is invalid */ base64.decode = function decode(string, buffer, offset) { var start = offset; var j = 0, // goto index t; // temporary for (var i = 0; i < string.length;) { var c = string.charCodeAt(i++); if (c === 61 && j > 1) break; if ((c = s64[c]) === undefined$1) throw Error(invalidEncoding); switch (j) { case 0: t = c; j = 1; break; case 1: buffer[offset++] = t << 2 | (c & 48) >> 4; t = c; j = 2; break; case 2: buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2; t = c; j = 3; break; case 3: buffer[offset++] = (t & 3) << 6 | c; j = 0; break; } } if (j === 1) throw Error(invalidEncoding); return offset - start; }; /** * Tests if the specified string appears to be base64 encoded. * @param {string} string String to test * @returns {boolean} `true` if probably base64 encoded, otherwise false */ base64.test = function test(string) { return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string); }; },{}],3:[function(require,module,exports){ module.exports = EventEmitter; /** * Constructs a new event emitter instance. * @classdesc A minimal event emitter. * @memberof util * @constructor */ function EventEmitter() { /** * Registered listeners. * @type {Object.} * @private */ this._listeners = {}; } /** * Registers an event listener. * @param {string} evt Event name * @param {function} fn Listener * @param {*} [ctx] Listener context * @returns {util.EventEmitter} `this` */ EventEmitter.prototype.on = function on(evt, fn, ctx) { (this._listeners[evt] || (this._listeners[evt] = [])).push({ fn : fn, ctx : ctx || this }); return this; }; /** * Removes an event listener or any matching listeners if arguments are omitted. * @param {string} [evt] Event name. Removes all listeners if omitted. * @param {function} [fn] Listener to remove. Removes all listeners of `evt` if omitted. * @returns {util.EventEmitter} `this` */ EventEmitter.prototype.off = function off(evt, fn) { if (evt === undefined$1) this._listeners = {}; else { if (fn === undefined$1) this._listeners[evt] = []; else { var listeners = this._listeners[evt]; for (var i = 0; i < listeners.length;) if (listeners[i].fn === fn) listeners.splice(i, 1); else ++i; } } return this; }; /** * Emits an event by calling its listeners with the specified arguments. * @param {string} evt Event name * @param {...*} args Arguments * @returns {util.EventEmitter} `this` */ EventEmitter.prototype.emit = function emit(evt) { var listeners = this._listeners[evt]; if (listeners) { var args = [], i = 1; for (; i < arguments.length;) args.push(arguments[i++]); for (i = 0; i < listeners.length;) listeners[i].fn.apply(listeners[i++].ctx, args); } return this; }; },{}],4:[function(require,module,exports){ module.exports = inquire; /** * Requires a module only if available. * @memberof util * @param {string} moduleName Module to require * @returns {?Object} Required module if available and not empty, otherwise `null` */ function inquire(moduleName) { try { var mod = eval("quire".replace(/^/,"re"))(moduleName); // eslint-disable-line no-eval if (mod && (mod.length || Object.keys(mod).length)) return mod; } catch (e) {} // eslint-disable-line no-empty return null; } },{}],5:[function(require,module,exports){ module.exports = pool; /** * An allocator as used by {@link util.pool}. * @typedef PoolAllocator * @type {function} * @param {number} size Buffer size * @returns {Uint8Array} Buffer */ /** * A slicer as used by {@link util.pool}. * @typedef PoolSlicer * @type {function} * @param {number} start Start offset * @param {number} end End offset * @returns {Uint8Array} Buffer slice * @this {Uint8Array} */ /** * A general purpose buffer pool. * @memberof util * @function * @param {PoolAllocator} alloc Allocator * @param {PoolSlicer} slice Slicer * @param {number} [size=8192] Slab size * @returns {PoolAllocator} Pooled allocator */ function pool(alloc, slice, size) { var SIZE = size || 8192; var MAX = SIZE >>> 1; var slab = null; var offset = SIZE; return function pool_alloc(size) { if (size < 1 || size > MAX) return alloc(size); if (offset + size > SIZE) { slab = alloc(SIZE); offset = 0; } var buf = slice.call(slab, offset, offset += size); if (offset & 7) // align to 32 bit offset = (offset | 7) + 1; return buf; }; } },{}],6:[function(require,module,exports){ /** * A minimal UTF8 implementation for number arrays. * @memberof util * @namespace */ var utf8 = exports; /** * Calculates the UTF8 byte length of a string. * @param {string} string String * @returns {number} Byte length */ utf8.length = function utf8_length(string) { var len = 0, c = 0; for (var i = 0; i < string.length; ++i) { c = string.charCodeAt(i); if (c < 128) len += 1; else if (c < 2048) len += 2; else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) { ++i; len += 4; } else len += 3; } return len; }; /** * Reads UTF8 bytes as a string. * @param {Uint8Array} buffer Source buffer * @param {number} start Source start * @param {number} end Source end * @returns {string} String read */ utf8.read = function utf8_read(buffer, start, end) { var len = end - start; if (len < 1) return ""; var parts = null, chunk = [], i = 0, // char offset t; // temporary while (start < end) { t = buffer[start++]; if (t < 128) chunk[i++] = t; else if (t > 191 && t < 224) chunk[i++] = (t & 31) << 6 | buffer[start++] & 63; else if (t > 239 && t < 365) { t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000; chunk[i++] = 0xD800 + (t >> 10); chunk[i++] = 0xDC00 + (t & 1023); } else chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63; if (i > 8191) { (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk)); i = 0; } } if (parts) { if (i) parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))); return parts.join(""); } return String.fromCharCode.apply(String, chunk.slice(0, i)); }; /** * Writes a string as UTF8 bytes. * @param {string} string Source string * @param {Uint8Array} buffer Destination buffer * @param {number} offset Destination offset * @returns {number} Bytes written */ utf8.write = function utf8_write(string, buffer, offset) { var start = offset, c1, // character 1 c2; // character 2 for (var i = 0; i < string.length; ++i) { c1 = string.charCodeAt(i); if (c1 < 128) { buffer[offset++] = c1; } else if (c1 < 2048) { buffer[offset++] = c1 >> 6 | 192; buffer[offset++] = c1 & 63 | 128; } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) { c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF); ++i; buffer[offset++] = c1 >> 18 | 240; buffer[offset++] = c1 >> 12 & 63 | 128; buffer[offset++] = c1 >> 6 & 63 | 128; buffer[offset++] = c1 & 63 | 128; } else { buffer[offset++] = c1 >> 12 | 224; buffer[offset++] = c1 >> 6 & 63 | 128; buffer[offset++] = c1 & 63 | 128; } } return offset - start; }; },{}],7:[function(require,module,exports){ var protobuf = exports; /** * Build type, one of `"full"`, `"light"` or `"minimal"`. * @name build * @type {string} * @const */ protobuf.build = "minimal"; /** * Named roots. * This is where pbjs stores generated structures (the option `-r, --root` specifies a name). * Can also be used manually to make roots available accross modules. * @name roots * @type {Object.} * @example * // pbjs -r myroot -o compiled.js ... * * // in another module: * require("./compiled.js"); * * // in any subsequent module: * var root = protobuf.roots["myroot"]; */ protobuf.roots = {}; // Serialization protobuf.Writer = require(14); protobuf.BufferWriter = require(15); protobuf.Reader = require(8); protobuf.BufferReader = require(9); // Utility protobuf.util = require(13); protobuf.rpc = require(10); protobuf.configure = configure; /* istanbul ignore next */ /** * Reconfigures the library according to the environment. * @returns {undefined} */ function configure() { protobuf.Reader._configure(protobuf.BufferReader); protobuf.util._configure(); } // Configure serialization protobuf.Writer._configure(protobuf.BufferWriter); configure(); },{"10":10,"13":13,"14":14,"15":15,"8":8,"9":9}],8:[function(require,module,exports){ module.exports = Reader; var util = require(13); var BufferReader; // cyclic var LongBits = util.LongBits, utf8 = util.utf8; /* istanbul ignore next */ function indexOutOfRange(reader, writeLength) { return RangeError("index out of range: " + reader.pos + " + " + (writeLength || 1) + " > " + reader.len); } /** * Constructs a new reader instance using the specified buffer. * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`. * @constructor * @param {Uint8Array} buffer Buffer to read from */ function Reader(buffer) { /** * Read buffer. * @type {Uint8Array} */ this.buf = buffer; /** * Read buffer position. * @type {number} */ this.pos = 0; /** * Read buffer length. * @type {number} */ this.len = buffer.length; } var create_array = typeof Uint8Array !== "undefined" ? function create_typed_array(buffer) { if (buffer instanceof Uint8Array || Array.isArray(buffer)) return new Reader(buffer); throw Error("illegal buffer"); } /* istanbul ignore next */ : function create_array(buffer) { if (Array.isArray(buffer)) return new Reader(buffer); throw Error("illegal buffer"); }; /** * Creates a new reader using the specified buffer. * @function * @param {Uint8Array|Buffer} buffer Buffer to read from * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader} * @throws {Error} If `buffer` is not a valid buffer */ Reader.create = util.Buffer ? function create_buffer_setup(buffer) { return (Reader.create = function create_buffer(buffer) { return util.Buffer.isBuffer(buffer) ? new BufferReader(buffer) /* istanbul ignore next */ : create_array(buffer); })(buffer); } /* istanbul ignore next */ : create_array; Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice; /** * Reads a varint as an unsigned 32 bit value. * @function * @returns {number} Value read */ Reader.prototype.uint32 = (function read_uint32_setup() { var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!) return function read_uint32() { value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value; value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value; /* istanbul ignore next */ if ((this.pos += 5) > this.len) { this.pos = this.len; throw indexOutOfRange(this, 10); } return value; }; })(); /** * Reads a varint as a signed 32 bit value. * @returns {number} Value read */ Reader.prototype.int32 = function read_int32() { return this.uint32() | 0; }; /** * Reads a zig-zag encoded varint as a signed 32 bit value. * @returns {number} Value read */ Reader.prototype.sint32 = function read_sint32() { var value = this.uint32(); return value >>> 1 ^ -(value & 1) | 0; }; /* eslint-disable no-invalid-this */ function readLongVarint() { // tends to deopt with local vars for octet etc. var bits = new LongBits(0, 0); var i = 0; if (this.len - this.pos > 4) { // fast route (lo) for (; i < 4; ++i) { // 1st..4th bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; if (this.buf[this.pos++] < 128) return bits; } // 5th bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0; bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0; if (this.buf[this.pos++] < 128) return bits; i = 0; } else { for (; i < 3; ++i) { /* istanbul ignore next */ if (this.pos >= this.len) throw indexOutOfRange(this); // 1st..3th bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0; if (this.buf[this.pos++] < 128) return bits; } // 4th bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0; return bits; } if (this.len - this.pos > 4) { // fast route (hi) for (; i < 5; ++i) { // 6th..10th bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; if (this.buf[this.pos++] < 128) return bits; } } else { for (; i < 5; ++i) { /* istanbul ignore next */ if (this.pos >= this.len) throw indexOutOfRange(this); // 6th..10th bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0; if (this.buf[this.pos++] < 128) return bits; } } /* istanbul ignore next */ throw Error("invalid varint encoding"); } /* eslint-enable no-invalid-this */ /** * Reads a varint as a signed 64 bit value. * @name Reader#int64 * @function * @returns {Long|number} Value read */ /** * Reads a varint as an unsigned 64 bit value. * @name Reader#uint64 * @function * @returns {Long|number} Value read */ /** * Reads a zig-zag encoded varint as a signed 64 bit value. * @name Reader#sint64 * @function * @returns {Long|number} Value read */ /** * Reads a varint as a boolean. * @returns {boolean} Value read */ Reader.prototype.bool = function read_bool() { return this.uint32() !== 0; }; function readFixed32(buf, end) { return (buf[end - 4] | buf[end - 3] << 8 | buf[end - 2] << 16 | buf[end - 1] << 24) >>> 0; } /** * Reads fixed 32 bits as an unsigned 32 bit integer. * @returns {number} Value read */ Reader.prototype.fixed32 = function read_fixed32() { /* istanbul ignore next */ if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); return readFixed32(this.buf, this.pos += 4); }; /** * Reads fixed 32 bits as a signed 32 bit integer. * @returns {number} Value read */ Reader.prototype.sfixed32 = function read_sfixed32() { /* istanbul ignore next */ if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); return readFixed32(this.buf, this.pos += 4) | 0; }; /* eslint-disable no-invalid-this */ function readFixed64(/* this: Reader */) { /* istanbul ignore next */ if (this.pos + 8 > this.len) throw indexOutOfRange(this, 8); return new LongBits(readFixed32(this.buf, this.pos += 4), readFixed32(this.buf, this.pos += 4)); } /* eslint-enable no-invalid-this */ /** * Reads fixed 64 bits. * @name Reader#fixed64 * @function * @returns {Long|number} Value read */ /** * Reads zig-zag encoded fixed 64 bits. * @name Reader#sfixed64 * @function * @returns {Long|number} Value read */ var readFloat = typeof Float32Array !== "undefined" ? (function() { var f32 = new Float32Array(1), f8b = new Uint8Array(f32.buffer); f32[0] = -0; return f8b[3] // already le? ? function readFloat_f32(buf, pos) { f8b[0] = buf[pos ]; f8b[1] = buf[pos + 1]; f8b[2] = buf[pos + 2]; f8b[3] = buf[pos + 3]; return f32[0]; } /* istanbul ignore next */ : function readFloat_f32_le(buf, pos) { f8b[0] = buf[pos + 3]; f8b[1] = buf[pos + 2]; f8b[2] = buf[pos + 1]; f8b[3] = buf[pos ]; return f32[0]; }; })() /* istanbul ignore next */ : function readFloat_ieee754(buf, pos) { var uint = readFixed32(buf, pos + 4), sign = (uint >> 31) * 2 + 1, exponent = uint >>> 23 & 255, mantissa = uint & 8388607; return exponent === 255 ? mantissa ? NaN : sign * Infinity : exponent === 0 // denormal ? sign * 1.401298464324817e-45 * mantissa : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608); }; /** * Reads a float (32 bit) as a number. * @function * @returns {number} Value read */ Reader.prototype.float = function read_float() { /* istanbul ignore next */ if (this.pos + 4 > this.len) throw indexOutOfRange(this, 4); var value = readFloat(this.buf, this.pos); this.pos += 4; return value; }; var readDouble = typeof Float64Array !== "undefined" ? (function() { var f64 = new Float64Array(1), f8b = new Uint8Array(f64.buffer); f64[0] = -0; return f8b[7] // already le? ? function readDouble_f64(buf, pos) { f8b[0] = buf[pos ]; f8b[1] = buf[pos + 1]; f8b[2] = buf[pos + 2]; f8b[3] = buf[pos + 3]; f8b[4] = buf[pos + 4]; f8b[5] = buf[pos + 5]; f8b[6] = buf[pos + 6]; f8b[7] = buf[pos + 7]; return f64[0]; } /* istanbul ignore next */ : function readDouble_f64_le(buf, pos) { f8b[0] = buf[pos + 7]; f8b[1] = buf[pos + 6]; f8b[2] = buf[pos + 5]; f8b[3] = buf[pos + 4]; f8b[4] = buf[pos + 3]; f8b[5] = buf[pos + 2]; f8b[6] = buf[pos + 1]; f8b[7] = buf[pos ]; return f64[0]; }; })() /* istanbul ignore next */ : function readDouble_ieee754(buf, pos) { var lo = readFixed32(buf, pos + 4), hi = readFixed32(buf, pos + 8); var sign = (hi >> 31) * 2 + 1, exponent = hi >>> 20 & 2047, mantissa = 4294967296 * (hi & 1048575) + lo; return exponent === 2047 ? mantissa ? NaN : sign * Infinity : exponent === 0 // denormal ? sign * 5e-324 * mantissa : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496); }; /** * Reads a double (64 bit float) as a number. * @function * @returns {number} Value read */ Reader.prototype.double = function read_double() { /* istanbul ignore next */ if (this.pos + 8 > this.len) throw indexOutOfRange(this, 4); var value = readDouble(this.buf, this.pos); this.pos += 8; return value; }; /** * Reads a sequence of bytes preceeded by its length as a varint. * @returns {Uint8Array} Value read */ Reader.prototype.bytes = function read_bytes() { var length = this.uint32(), start = this.pos, end = this.pos + length; /* istanbul ignore next */ if (end > this.len) throw indexOutOfRange(this, length); this.pos += length; return start === end // fix for IE 10/Win8 and others' subarray returning array of size 1 ? new this.buf.constructor(0) : this._slice.call(this.buf, start, end); }; /** * Reads a string preceeded by its byte length as a varint. * @returns {string} Value read */ Reader.prototype.string = function read_string() { var bytes = this.bytes(); return utf8.read(bytes, 0, bytes.length); }; /** * Skips the specified number of bytes if specified, otherwise skips a varint. * @param {number} [length] Length if known, otherwise a varint is assumed * @returns {Reader} `this` */ Reader.prototype.skip = function skip(length) { if (typeof length === "number") { /* istanbul ignore next */ if (this.pos + length > this.len) throw indexOutOfRange(this, length); this.pos += length; } else { /* istanbul ignore next */ do { if (this.pos >= this.len) throw indexOutOfRange(this); } while (this.buf[this.pos++] & 128); } return this; }; /** * Skips the next element of the specified wire type. * @param {number} wireType Wire type received * @returns {Reader} `this` */ Reader.prototype.skipType = function(wireType) { switch (wireType) { case 0: this.skip(); break; case 1: this.skip(8); break; case 2: this.skip(this.uint32()); break; case 3: do { // eslint-disable-line no-constant-condition if ((wireType = this.uint32() & 7) === 4) break; this.skipType(wireType); } while (true); break; case 5: this.skip(4); break; /* istanbul ignore next */ default: throw Error("invalid wire type " + wireType + " at offset " + this.pos); } return this; }; Reader._configure = function(BufferReader_) { BufferReader = BufferReader_; var fn = util.Long ? "toLong" : /* istanbul ignore next */ "toNumber"; util.merge(Reader.prototype, { int64: function read_int64() { return readLongVarint.call(this)[fn](false); }, uint64: function read_uint64() { return readLongVarint.call(this)[fn](true); }, sint64: function read_sint64() { return readLongVarint.call(this).zzDecode()[fn](false); }, fixed64: function read_fixed64() { return readFixed64.call(this)[fn](true); }, sfixed64: function read_sfixed64() { return readFixed64.call(this)[fn](false); } }); }; },{"13":13}],9:[function(require,module,exports){ module.exports = BufferReader; // extends Reader var Reader = require(8); (BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader; var util = require(13); /** * Constructs a new buffer reader instance. * @classdesc Wire format reader using node buffers. * @extends Reader * @constructor * @param {Buffer} buffer Buffer to read from */ function BufferReader(buffer) { Reader.call(this, buffer); /** * Read buffer. * @name BufferReader#buf * @type {Buffer} */ } /* istanbul ignore else */ if (util.Buffer) BufferReader.prototype._slice = util.Buffer.prototype.slice; /** * @override */ BufferReader.prototype.string = function read_string_buffer() { var len = this.uint32(); // modifies pos return this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len)); }; /** * Reads a sequence of bytes preceeded by its length as a varint. * @name BufferReader#bytes * @function * @returns {Buffer} Value read */ },{"13":13,"8":8}],10:[function(require,module,exports){ /** * Streaming RPC helpers. * @namespace */ var rpc = exports; /** * RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets. * @typedef RPCImpl * @type {function} * @param {Method|rpc.ServiceMethod} method Reflected or static method being called * @param {Uint8Array} requestData Request data * @param {RPCImplCallback} callback Callback function * @returns {undefined} * @example * function rpcImpl(method, requestData, callback) { * if (protobuf.util.lcFirst(method.name) !== "myMethod") // compatible with static code * throw Error("no such method"); * asynchronouslyObtainAResponse(requestData, function(err, responseData) { * callback(err, responseData); * }); * } */ /** * Node-style callback as used by {@link RPCImpl}. * @typedef RPCImplCallback * @type {function} * @param {?Error} error Error, if any, otherwise `null` * @param {?Uint8Array} [response] Response data or `null` to signal end of stream, if there hasn't been an error * @returns {undefined} */ rpc.Service = require(11); },{"11":11}],11:[function(require,module,exports){ module.exports = Service; var util = require(13); // Extends EventEmitter (Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service; /** * A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}. * * Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`. * @typedef rpc.ServiceMethodCallback * @type {function} * @param {?Error} error Error, if any * @param {?Message} [response] Response message * @returns {undefined} */ /** * A service method part of a {@link rpc.ServiceMethodMixin|ServiceMethodMixin} and thus {@link rpc.Service} as created by {@link Service.create}. * @typedef rpc.ServiceMethod * @type {function} * @param {Message|Object.} request Request message or plain object * @param {rpc.ServiceMethodCallback} [callback] Node-style callback called with the error, if any, and the response message * @returns {Promise} Promise if `callback` has been omitted, otherwise `undefined` */ /** * A service method mixin. * * When using TypeScript, mixed in service methods are only supported directly with a type definition of a static module (used with reflection). Otherwise, explicit casting is required. * @typedef rpc.ServiceMethodMixin * @type {Object.} * @example * // Explicit casting with TypeScript * (myRpcService["myMethod"] as protobuf.rpc.ServiceMethod)(...) */ /** * Constructs a new RPC service instance. * @classdesc An RPC service as returned by {@link Service#create}. * @exports rpc.Service * @extends util.EventEmitter * @augments rpc.ServiceMethodMixin * @constructor * @param {RPCImpl} rpcImpl RPC implementation * @param {boolean} [requestDelimited=false] Whether requests are length-delimited * @param {boolean} [responseDelimited=false] Whether responses are length-delimited */ function Service(rpcImpl, requestDelimited, responseDelimited) { if (typeof rpcImpl !== "function") throw TypeError("rpcImpl must be a function"); util.EventEmitter.call(this); /** * RPC implementation. Becomes `null` once the service is ended. * @type {?RPCImpl} */ this.rpcImpl = rpcImpl; /** * Whether requests are length-delimited. * @type {boolean} */ this.requestDelimited = Boolean(requestDelimited); /** * Whether responses are length-delimited. * @type {boolean} */ this.responseDelimited = Boolean(responseDelimited); } /** * Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}. * @param {Method|rpc.ServiceMethod} method Reflected or static method * @param {function} requestCtor Request constructor * @param {function} responseCtor Response constructor * @param {Message|Object.} request Request message or plain object * @param {rpc.ServiceMethodCallback} callback Service callback * @returns {undefined} */ Service.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) { if (!request) throw TypeError("request must be specified"); var self = this; if (!callback) return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request); if (!self.rpcImpl) { setTimeout(function() { callback(Error("already ended")); }, 0); return undefined$1; } try { return self.rpcImpl( method, requestCtor[self.requestDelimited ? "encodeDelimited" : "encode"](request).finish(), function rpcCallback(err, response) { if (err) { self.emit("error", err, method); return callback(err); } if (response === null) { self.end(/* endedByRPC */ true); return undefined$1; } if (!(response instanceof responseCtor)) { try { response = responseCtor[self.responseDelimited ? "decodeDelimited" : "decode"](response); } catch (err) { self.emit("error", err, method); return callback(err); } } self.emit("data", response, method); return callback(null, response); } ); } catch (err) { self.emit("error", err, method); setTimeout(function() { callback(err); }, 0); return undefined$1; } }; /** * Ends this service and emits the `end` event. * @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation. * @returns {rpc.Service} `this` */ Service.prototype.end = function end(endedByRPC) { if (this.rpcImpl) { if (!endedByRPC) // signal end to rpcImpl this.rpcImpl(null, null, null); this.rpcImpl = null; this.emit("end").off(); } return this; }; },{"13":13}],12:[function(require,module,exports){ module.exports = LongBits; var util = require(13); /** * Any compatible Long instance. * * This is a minimal stand-alone definition of a Long instance. The actual type is that exported by long.js. * @typedef Long * @type {Object} * @property {number} low Low bits * @property {number} high High bits * @property {boolean} unsigned Whether unsigned or not */ /** * Constructs new long bits. * @classdesc Helper class for working with the low and high bits of a 64 bit value. * @memberof util * @constructor * @param {number} lo Low 32 bits, unsigned * @param {number} hi High 32 bits, unsigned */ function LongBits(lo, hi) { // note that the casts below are theoretically unnecessary as of today, but older statically // generated converter code might still call the ctor with signed 32bits. kept for compat. /** * Low bits. * @type {number} */ this.lo = lo >>> 0; /** * High bits. * @type {number} */ this.hi = hi >>> 0; } /** * Zero bits. * @memberof util.LongBits * @type {util.LongBits} */ var zero = LongBits.zero = new LongBits(0, 0); zero.toNumber = function() { return 0; }; zero.zzEncode = zero.zzDecode = function() { return this; }; zero.length = function() { return 1; }; /** * Zero hash. * @memberof util.LongBits * @type {string} */ var zeroHash = LongBits.zeroHash = "\0\0\0\0\0\0\0\0"; /** * Constructs new long bits from the specified number. * @param {number} value Value * @returns {util.LongBits} Instance */ LongBits.fromNumber = function fromNumber(value) { if (value === 0) return zero; var sign = value < 0; if (sign) value = -value; var lo = value >>> 0, hi = (value - lo) / 4294967296 >>> 0; if (sign) { hi = ~hi >>> 0; lo = ~lo >>> 0; if (++lo > 4294967295) { lo = 0; if (++hi > 4294967295) hi = 0; } } return new LongBits(lo, hi); }; /** * Constructs new long bits from a number, long or string. * @param {Long|number|string} value Value * @returns {util.LongBits} Instance */ LongBits.from = function from(value) { if (typeof value === "number") return LongBits.fromNumber(value); if (util.isString(value)) { /* istanbul ignore else */ if (util.Long) value = util.Long.fromString(value); else return LongBits.fromNumber(parseInt(value, 10)); } return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero; }; /** * Converts this long bits to a possibly unsafe JavaScript number. * @param {boolean} [unsigned=false] Whether unsigned or not * @returns {number} Possibly unsafe number */ LongBits.prototype.toNumber = function toNumber(unsigned) { if (!unsigned && this.hi >>> 31) { var lo = ~this.lo + 1 >>> 0, hi = ~this.hi >>> 0; if (!lo) hi = hi + 1 >>> 0; return -(lo + hi * 4294967296); } return this.lo + this.hi * 4294967296; }; /** * Converts this long bits to a long. * @param {boolean} [unsigned=false] Whether unsigned or not * @returns {Long} Long */ LongBits.prototype.toLong = function toLong(unsigned) { return util.Long ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned)) /* istanbul ignore next */ : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) }; }; var charCodeAt = String.prototype.charCodeAt; /** * Constructs new long bits from the specified 8 characters long hash. * @param {string} hash Hash * @returns {util.LongBits} Bits */ LongBits.fromHash = function fromHash(hash) { if (hash === zeroHash) return zero; return new LongBits( ( charCodeAt.call(hash, 0) | charCodeAt.call(hash, 1) << 8 | charCodeAt.call(hash, 2) << 16 | charCodeAt.call(hash, 3) << 24) >>> 0 , ( charCodeAt.call(hash, 4) | charCodeAt.call(hash, 5) << 8 | charCodeAt.call(hash, 6) << 16 | charCodeAt.call(hash, 7) << 24) >>> 0 ); }; /** * Converts this long bits to a 8 characters long hash. * @returns {string} Hash */ LongBits.prototype.toHash = function toHash() { return String.fromCharCode( this.lo & 255, this.lo >>> 8 & 255, this.lo >>> 16 & 255, this.lo >>> 24 , this.hi & 255, this.hi >>> 8 & 255, this.hi >>> 16 & 255, this.hi >>> 24 ); }; /** * Zig-zag encodes this long bits. * @returns {util.LongBits} `this` */ LongBits.prototype.zzEncode = function zzEncode() { var mask = this.hi >> 31; this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0; this.lo = ( this.lo << 1 ^ mask) >>> 0; return this; }; /** * Zig-zag decodes this long bits. * @returns {util.LongBits} `this` */ LongBits.prototype.zzDecode = function zzDecode() { var mask = -(this.lo & 1); this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0; this.hi = ( this.hi >>> 1 ^ mask) >>> 0; return this; }; /** * Calculates the length of this longbits when encoded as a varint. * @returns {number} Length */ LongBits.prototype.length = function length() { var part0 = this.lo, part1 = (this.lo >>> 28 | this.hi << 4) >>> 0, part2 = this.hi >>> 24; return part2 === 0 ? part1 === 0 ? part0 < 16384 ? part0 < 128 ? 1 : 2 : part0 < 2097152 ? 3 : 4 : part1 < 16384 ? part1 < 128 ? 5 : 6 : part1 < 2097152 ? 7 : 8 : part2 < 128 ? 9 : 10; }; },{"13":13}],13:[function(require,module,exports){ var util = exports; // used to return a Promise where callback is omitted util.asPromise = require(1); // converts to / from base64 encoded strings util.base64 = require(2); // base class of rpc.Service util.EventEmitter = require(3); // requires modules optionally and hides the call from bundlers util.inquire = require(4); // converts to / from utf8 encoded strings util.utf8 = require(6); // provides a node-like buffer pool in the browser util.pool = require(5); // utility to work with the low and high bits of a 64 bit value util.LongBits = require(12); /** * An immuable empty array. * @memberof util * @type {Array.<*>} */ util.emptyArray = Object.freeze ? Object.freeze([]) : /* istanbul ignore next */ []; // used on prototypes /** * An immutable empty object. * @type {Object} */ util.emptyObject = Object.freeze ? Object.freeze({}) : /* istanbul ignore next */ {}; // used on prototypes /** * Whether running within node or not. * @memberof util * @type {boolean} */ util.isNode = Boolean(global.process && global.process.versions && global.process.versions.node); /** * Tests if the specified value is an integer. * @function * @param {*} value Value to test * @returns {boolean} `true` if the value is an integer */ util.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) { return typeof value === "number" && isFinite(value) && Math.floor(value) === value; }; /** * Tests if the specified value is a string. * @param {*} value Value to test * @returns {boolean} `true` if the value is a string */ util.isString = function isString(value) { return typeof value === "string" || value instanceof String; }; /** * Tests if the specified value is a non-null object. * @param {*} value Value to test * @returns {boolean} `true` if the value is a non-null object */ util.isObject = function isObject(value) { return value && typeof value === "object"; }; /** * Node's Buffer class if available. * @type {?function(new: Buffer)} */ util.Buffer = (function() { try { var Buffer = util.inquire("buffer").Buffer; // refuse to use non-node buffers if not explicitly assigned (perf reasons): return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null; } catch (e) { /* istanbul ignore next */ return null; } })(); /** * Internal alias of or polyfull for Buffer.from. * @type {?function} * @param {string|number[]} value Value * @param {string} [encoding] Encoding if value is a string * @returns {Uint8Array} * @private */ util._Buffer_from = null; /** * Internal alias of or polyfill for Buffer.allocUnsafe. * @type {?function} * @param {number} size Buffer size * @returns {Uint8Array} * @private */ util._Buffer_allocUnsafe = null; /** * Creates a new buffer of whatever type supported by the environment. * @param {number|number[]} [sizeOrArray=0] Buffer size or number array * @returns {Uint8Array|Buffer} Buffer */ util.newBuffer = function newBuffer(sizeOrArray) { /* istanbul ignore next */ return typeof sizeOrArray === "number" ? util.Buffer ? util._Buffer_allocUnsafe(sizeOrArray) : new util.Array(sizeOrArray) : util.Buffer ? util._Buffer_from(sizeOrArray) : typeof Uint8Array === "undefined" ? sizeOrArray : new Uint8Array(sizeOrArray); }; /** * Array implementation used in the browser. `Uint8Array` if supported, otherwise `Array`. * @type {?function(new: Uint8Array, *)} */ util.Array = typeof Uint8Array !== "undefined" ? Uint8Array /* istanbul ignore next */ : Array; /** * Long.js's Long class if available. * @type {?function(new: Long)} */ util.Long = /* istanbul ignore next */ global.dcodeIO && /* istanbul ignore next */ global.dcodeIO.Long || util.inquire("long"); /** * Regular expression used to verify 2 bit (`bool`) map keys. * @type {RegExp} */ util.key2Re = /^true|false|0|1$/; /** * Regular expression used to verify 32 bit (`int32` etc.) map keys. * @type {RegExp} */ util.key32Re = /^-?(?:0|[1-9][0-9]*)$/; /** * Regular expression used to verify 64 bit (`int64` etc.) map keys. * @type {RegExp} */ util.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/; /** * Converts a number or long to an 8 characters long hash string. * @param {Long|number} value Value to convert * @returns {string} Hash */ util.longToHash = function longToHash(value) { return value ? util.LongBits.from(value).toHash() : util.LongBits.zeroHash; }; /** * Converts an 8 characters long hash string to a long or number. * @param {string} hash Hash * @param {boolean} [unsigned=false] Whether unsigned or not * @returns {Long|number} Original value */ util.longFromHash = function longFromHash(hash, unsigned) { var bits = util.LongBits.fromHash(hash); if (util.Long) return util.Long.fromBits(bits.lo, bits.hi, unsigned); return bits.toNumber(Boolean(unsigned)); }; /** * Merges the properties of the source object into the destination object. * @memberof util * @param {Object.} dst Destination object * @param {Object.} src Source object * @param {boolean} [ifNotSet=false] Merges only if the key is not already set * @returns {Object.} Destination object */ function merge(dst, src, ifNotSet) { // used by converters for (var keys = Object.keys(src), i = 0; i < keys.length; ++i) if (dst[keys[i]] === undefined$1 || !ifNotSet) dst[keys[i]] = src[keys[i]]; return dst; } util.merge = merge; /** * Converts the first character of a string to lower case. * @param {string} str String to convert * @returns {string} Converted string */ util.lcFirst = function lcFirst(str) { return str.charAt(0).toLowerCase() + str.substring(1); }; /** * Creates a custom error constructor. * @memberof util * @param {string} name Error name * @returns {function} Custom error constructor */ function newError(name) { function CustomError(message, properties) { if (!(this instanceof CustomError)) return new CustomError(message, properties); // Error.call(this, message); // ^ just returns a new error instance because the ctor can be called as a function Object.defineProperty(this, "message", { get: function() { return message; } }); /* istanbul ignore next */ if (Error.captureStackTrace) // node Error.captureStackTrace(this, CustomError); else Object.defineProperty(this, "stack", { value: (new Error()).stack || "" }); if (properties) merge(this, properties); } (CustomError.prototype = Object.create(Error.prototype)).constructor = CustomError; Object.defineProperty(CustomError.prototype, "name", { get: function() { return name; } }); CustomError.prototype.toString = function toString() { return this.name + ": " + this.message; }; return CustomError; } util.newError = newError; /** * Constructs a new protocol error. * @classdesc Error subclass indicating a protocol specifc error. * @memberof util * @extends Error * @constructor * @param {string} message Error message * @param {Object.=} properties Additional properties * @example * try { * MyMessage.decode(someBuffer); // throws if required fields are missing * } catch (e) { * if (e instanceof ProtocolError && e.instance) * console.log("decoded so far: " + JSON.stringify(e.instance)); * } */ util.ProtocolError = newError("ProtocolError"); /** * So far decoded message instance. * @name util.ProtocolError#instance * @type {Message} */ /** * Builds a getter for a oneof's present field name. * @param {string[]} fieldNames Field names * @returns {function():string|undefined} Unbound getter */ util.oneOfGetter = function getOneOf(fieldNames) { var fieldMap = {}; for (var i = 0; i < fieldNames.length; ++i) fieldMap[fieldNames[i]] = 1; /** * @returns {string|undefined} Set field name, if any * @this Object * @ignore */ return function() { // eslint-disable-line consistent-return for (var keys = Object.keys(this), i = keys.length - 1; i > -1; --i) if (fieldMap[keys[i]] === 1 && this[keys[i]] !== undefined$1 && this[keys[i]] !== null) return keys[i]; }; }; /** * Builds a setter for a oneof's present field name. * @param {string[]} fieldNames Field names * @returns {function(?string):undefined} Unbound setter */ util.oneOfSetter = function setOneOf(fieldNames) { /** * @param {string} name Field name * @returns {undefined} * @this Object * @ignore */ return function(name) { for (var i = 0; i < fieldNames.length; ++i) if (fieldNames[i] !== name) delete this[fieldNames[i]]; }; }; /** * Lazily resolves fully qualified type names against the specified root. * @param {Root} root Root instanceof * @param {Object.} lazyTypes Type names * @returns {undefined} */ util.lazyResolve = function lazyResolve(root, lazyTypes) { for (var i = 0; i < lazyTypes.length; ++i) { for (var keys = Object.keys(lazyTypes[i]), j = 0; j < keys.length; ++j) { var path = lazyTypes[i][keys[j]].split("."), ptr = root; while (path.length) ptr = ptr[path.shift()]; lazyTypes[i][keys[j]] = ptr; } } }; /** * Default conversion options used for {@link Message#toJSON} implementations. Longs, enums and bytes are converted to strings by default. * @type {ConversionOptions} */ util.toJSONOptions = { longs: String, enums: String, bytes: String }; util._configure = function() { var Buffer = util.Buffer; /* istanbul ignore if */ if (!Buffer) { util._Buffer_from = util._Buffer_allocUnsafe = null; return; } // because node 4.x buffers are incompatible & immutable // see: https://github.com/dcodeIO/protobuf.js/pull/665 util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from || /* istanbul ignore next */ function Buffer_from(value, encoding) { return new Buffer(value, encoding); }; util._Buffer_allocUnsafe = Buffer.allocUnsafe || /* istanbul ignore next */ function Buffer_allocUnsafe(size) { return new Buffer(size); }; }; },{"1":1,"12":12,"2":2,"3":3,"4":4,"5":5,"6":6}],14:[function(require,module,exports){ module.exports = Writer; var util = require(13); var BufferWriter; // cyclic var LongBits = util.LongBits, base64 = util.base64, utf8 = util.utf8; /** * Constructs a new writer operation instance. * @classdesc Scheduled writer operation. * @constructor * @param {function(*, Uint8Array, number)} fn Function to call * @param {number} len Value byte length * @param {*} val Value to write * @ignore */ function Op(fn, len, val) { /** * Function to call. * @type {function(Uint8Array, number, *)} */ this.fn = fn; /** * Value byte length. * @type {number} */ this.len = len; /** * Next operation. * @type {Writer.Op|undefined} */ this.next = undefined$1; /** * Value to write. * @type {*} */ this.val = val; // type varies } /* istanbul ignore next */ function noop() {} // eslint-disable-line no-empty-function /** * Constructs a new writer state instance. * @classdesc Copied writer state. * @memberof Writer * @constructor * @param {Writer} writer Writer to copy state from * @private * @ignore */ function State(writer) { /** * Current head. * @type {Writer.Op} */ this.head = writer.head; /** * Current tail. * @type {Writer.Op} */ this.tail = writer.tail; /** * Current buffer length. * @type {number} */ this.len = writer.len; /** * Next state. * @type {?State} */ this.next = writer.states; } /** * Constructs a new writer instance. * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`. * @constructor */ function Writer() { /** * Current length. * @type {number} */ this.len = 0; /** * Operations head. * @type {Object} */ this.head = new Op(noop, 0, 0); /** * Operations tail * @type {Object} */ this.tail = this.head; /** * Linked forked states. * @type {?Object} */ this.states = null; // When a value is written, the writer calculates its byte length and puts it into a linked // list of operations to perform when finish() is called. This both allows us to allocate // buffers of the exact required size and reduces the amount of work we have to do compared // to first calculating over objects and then encoding over objects. In our case, the encoding // part is just a linked list walk calling operations with already prepared values. } /** * Creates a new writer. * @function * @returns {BufferWriter|Writer} A {@link BufferWriter} when Buffers are supported, otherwise a {@link Writer} */ Writer.create = util.Buffer ? function create_buffer_setup() { return (Writer.create = function create_buffer() { return new BufferWriter(); })(); } /* istanbul ignore next */ : function create_array() { return new Writer(); }; /** * Allocates a buffer of the specified size. * @param {number} size Buffer size * @returns {Uint8Array} Buffer */ Writer.alloc = function alloc(size) { return new util.Array(size); }; // Use Uint8Array buffer pool in the browser, just like node does with buffers /* istanbul ignore else */ if (util.Array !== Array) Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray); /** * Pushes a new operation to the queue. * @param {function(Uint8Array, number, *)} fn Function to call * @param {number} len Value byte length * @param {number} val Value to write * @returns {Writer} `this` */ Writer.prototype.push = function push(fn, len, val) { this.tail = this.tail.next = new Op(fn, len, val); this.len += len; return this; }; function writeByte(val, buf, pos) { buf[pos] = val & 255; } function writeVarint32(val, buf, pos) { while (val > 127) { buf[pos++] = val & 127 | 128; val >>>= 7; } buf[pos] = val; } /** * Constructs a new varint writer operation instance. * @classdesc Scheduled varint writer operation. * @extends Op * @constructor * @param {number} len Value byte length * @param {number} val Value to write * @ignore */ function VarintOp(len, val) { this.len = len; this.next = undefined$1; this.val = val; } VarintOp.prototype = Object.create(Op.prototype); VarintOp.prototype.fn = writeVarint32; /** * Writes an unsigned 32 bit value as a varint. * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.uint32 = function write_uint32(value) { // here, the call to this.push has been inlined and a varint specific Op subclass is used. // uint32 is by far the most frequently used operation and benefits significantly from this. this.len += (this.tail = this.tail.next = new VarintOp( (value = value >>> 0) < 128 ? 1 : value < 16384 ? 2 : value < 2097152 ? 3 : value < 268435456 ? 4 : 5, value)).len; return this; }; /** * Writes a signed 32 bit value as a varint. * @function * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.int32 = function write_int32(value) { return value < 0 ? this.push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec : this.uint32(value); }; /** * Writes a 32 bit value as a varint, zig-zag encoded. * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.sint32 = function write_sint32(value) { return this.uint32((value << 1 ^ value >> 31) >>> 0); }; function writeVarint64(val, buf, pos) { while (val.hi) { buf[pos++] = val.lo & 127 | 128; val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0; val.hi >>>= 7; } while (val.lo > 127) { buf[pos++] = val.lo & 127 | 128; val.lo = val.lo >>> 7; } buf[pos++] = val.lo; } /** * Writes an unsigned 64 bit value as a varint. * @param {Long|number|string} value Value to write * @returns {Writer} `this` * @throws {TypeError} If `value` is a string and no long library is present. */ Writer.prototype.uint64 = function write_uint64(value) { var bits = LongBits.from(value); return this.push(writeVarint64, bits.length(), bits); }; /** * Writes a signed 64 bit value as a varint. * @function * @param {Long|number|string} value Value to write * @returns {Writer} `this` * @throws {TypeError} If `value` is a string and no long library is present. */ Writer.prototype.int64 = Writer.prototype.uint64; /** * Writes a signed 64 bit value as a varint, zig-zag encoded. * @param {Long|number|string} value Value to write * @returns {Writer} `this` * @throws {TypeError} If `value` is a string and no long library is present. */ Writer.prototype.sint64 = function write_sint64(value) { var bits = LongBits.from(value).zzEncode(); return this.push(writeVarint64, bits.length(), bits); }; /** * Writes a boolish value as a varint. * @param {boolean} value Value to write * @returns {Writer} `this` */ Writer.prototype.bool = function write_bool(value) { return this.push(writeByte, 1, value ? 1 : 0); }; function writeFixed32(val, buf, pos) { buf[pos++] = val & 255; buf[pos++] = val >>> 8 & 255; buf[pos++] = val >>> 16 & 255; buf[pos ] = val >>> 24; } /** * Writes an unsigned 32 bit value as fixed 32 bits. * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.fixed32 = function write_fixed32(value) { return this.push(writeFixed32, 4, value >>> 0); }; /** * Writes a signed 32 bit value as fixed 32 bits. * @function * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.sfixed32 = Writer.prototype.fixed32; /** * Writes an unsigned 64 bit value as fixed 64 bits. * @param {Long|number|string} value Value to write * @returns {Writer} `this` * @throws {TypeError} If `value` is a string and no long library is present. */ Writer.prototype.fixed64 = function write_fixed64(value) { var bits = LongBits.from(value); return this.push(writeFixed32, 4, bits.lo).push(writeFixed32, 4, bits.hi); }; /** * Writes a signed 64 bit value as fixed 64 bits. * @function * @param {Long|number|string} value Value to write * @returns {Writer} `this` * @throws {TypeError} If `value` is a string and no long library is present. */ Writer.prototype.sfixed64 = Writer.prototype.fixed64; var writeFloat = typeof Float32Array !== "undefined" ? (function() { var f32 = new Float32Array(1), f8b = new Uint8Array(f32.buffer); f32[0] = -0; return f8b[3] // already le? ? function writeFloat_f32(val, buf, pos) { f32[0] = val; buf[pos++] = f8b[0]; buf[pos++] = f8b[1]; buf[pos++] = f8b[2]; buf[pos ] = f8b[3]; } /* istanbul ignore next */ : function writeFloat_f32_le(val, buf, pos) { f32[0] = val; buf[pos++] = f8b[3]; buf[pos++] = f8b[2]; buf[pos++] = f8b[1]; buf[pos ] = f8b[0]; }; })() /* istanbul ignore next */ : function writeFloat_ieee754(value, buf, pos) { var sign = value < 0 ? 1 : 0; if (sign) value = -value; if (value === 0) writeFixed32(1 / value > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos); else if (isNaN(value)) writeFixed32(2147483647, buf, pos); else if (value > 3.4028234663852886e+38) // +-Infinity writeFixed32((sign << 31 | 2139095040) >>> 0, buf, pos); else if (value < 1.1754943508222875e-38) // denormal writeFixed32((sign << 31 | Math.round(value / 1.401298464324817e-45)) >>> 0, buf, pos); else { var exponent = Math.floor(Math.log(value) / Math.LN2), mantissa = Math.round(value * Math.pow(2, -exponent) * 8388608) & 8388607; writeFixed32((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos); } }; /** * Writes a float (32 bit). * @function * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.float = function write_float(value) { return this.push(writeFloat, 4, value); }; var writeDouble = typeof Float64Array !== "undefined" ? (function() { var f64 = new Float64Array(1), f8b = new Uint8Array(f64.buffer); f64[0] = -0; return f8b[7] // already le? ? function writeDouble_f64(val, buf, pos) { f64[0] = val; buf[pos++] = f8b[0]; buf[pos++] = f8b[1]; buf[pos++] = f8b[2]; buf[pos++] = f8b[3]; buf[pos++] = f8b[4]; buf[pos++] = f8b[5]; buf[pos++] = f8b[6]; buf[pos ] = f8b[7]; } /* istanbul ignore next */ : function writeDouble_f64_le(val, buf, pos) { f64[0] = val; buf[pos++] = f8b[7]; buf[pos++] = f8b[6]; buf[pos++] = f8b[5]; buf[pos++] = f8b[4]; buf[pos++] = f8b[3]; buf[pos++] = f8b[2]; buf[pos++] = f8b[1]; buf[pos ] = f8b[0]; }; })() /* istanbul ignore next */ : function writeDouble_ieee754(value, buf, pos) { var sign = value < 0 ? 1 : 0; if (sign) value = -value; if (value === 0) { writeFixed32(0, buf, pos); writeFixed32(1 / value > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos + 4); } else if (isNaN(value)) { writeFixed32(4294967295, buf, pos); writeFixed32(2147483647, buf, pos + 4); } else if (value > 1.7976931348623157e+308) { // +-Infinity writeFixed32(0, buf, pos); writeFixed32((sign << 31 | 2146435072) >>> 0, buf, pos + 4); } else { var mantissa; if (value < 2.2250738585072014e-308) { // denormal mantissa = value / 5e-324; writeFixed32(mantissa >>> 0, buf, pos); writeFixed32((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + 4); } else { var exponent = Math.floor(Math.log(value) / Math.LN2); if (exponent === 1024) exponent = 1023; mantissa = value * Math.pow(2, -exponent); writeFixed32(mantissa * 4503599627370496 >>> 0, buf, pos); writeFixed32((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + 4); } } }; /** * Writes a double (64 bit float). * @function * @param {number} value Value to write * @returns {Writer} `this` */ Writer.prototype.double = function write_double(value) { return this.push(writeDouble, 8, value); }; var writeBytes = util.Array.prototype.set ? function writeBytes_set(val, buf, pos) { buf.set(val, pos); // also works for plain array values } /* istanbul ignore next */ : function writeBytes_for(val, buf, pos) { for (var i = 0; i < val.length; ++i) buf[pos + i] = val[i]; }; /** * Writes a sequence of bytes. * @param {Uint8Array|string} value Buffer or base64 encoded string to write * @returns {Writer} `this` */ Writer.prototype.bytes = function write_bytes(value) { var len = value.length >>> 0; if (!len) return this.push(writeByte, 1, 0); if (util.isString(value)) { var buf = Writer.alloc(len = base64.length(value)); base64.decode(value, buf, 0); value = buf; } return this.uint32(len).push(writeBytes, len, value); }; /** * Writes a string. * @param {string} value Value to write * @returns {Writer} `this` */ Writer.prototype.string = function write_string(value) { var len = utf8.length(value); return len ? this.uint32(len).push(utf8.write, len, value) : this.push(writeByte, 1, 0); }; /** * Forks this writer's state by pushing it to a stack. * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state. * @returns {Writer} `this` */ Writer.prototype.fork = function fork() { this.states = new State(this); this.head = this.tail = new Op(noop, 0, 0); this.len = 0; return this; }; /** * Resets this instance to the last state. * @returns {Writer} `this` */ Writer.prototype.reset = function reset() { if (this.states) { this.head = this.states.head; this.tail = this.states.tail; this.len = this.states.len; this.states = this.states.next; } else { this.head = this.tail = new Op(noop, 0, 0); this.len = 0; } return this; }; /** * Resets to the last state and appends the fork state's current write length as a varint followed by its operations. * @returns {Writer} `this` */ Writer.prototype.ldelim = function ldelim() { var head = this.head, tail = this.tail, len = this.len; this.reset().uint32(len); if (len) { this.tail.next = head.next; // skip noop this.tail = tail; this.len += len; } return this; }; /** * Finishes the write operation. * @returns {Uint8Array} Finished buffer */ Writer.prototype.finish = function finish() { var head = this.head.next, // skip noop buf = this.constructor.alloc(this.len), pos = 0; while (head) { head.fn(head.val, buf, pos); pos += head.len; head = head.next; } // this.head = this.tail = null; return buf; }; Writer._configure = function(BufferWriter_) { BufferWriter = BufferWriter_; }; },{"13":13}],15:[function(require,module,exports){ module.exports = BufferWriter; // extends Writer var Writer = require(14); (BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter; var util = require(13); var Buffer = util.Buffer; /** * Constructs a new buffer writer instance. * @classdesc Wire format writer using node buffers. * @extends Writer * @constructor */ function BufferWriter() { Writer.call(this); } /** * Allocates a buffer of the specified size. * @param {number} size Buffer size * @returns {Buffer} Buffer */ BufferWriter.alloc = function alloc_buffer(size) { return (BufferWriter.alloc = util._Buffer_allocUnsafe)(size); }; var writeBytesBuffer = Buffer && Buffer.prototype instanceof Uint8Array && Buffer.prototype.set.name === "set" ? function writeBytesBuffer_set(val, buf, pos) { buf.set(val, pos); // faster than copy (requires node >= 4 where Buffers extend Uint8Array and set is properly inherited) // also works for plain array values } /* istanbul ignore next */ : function writeBytesBuffer_copy(val, buf, pos) { if (val.copy) // Buffer values val.copy(buf, pos, 0, val.length); else for (var i = 0; i < val.length;) // plain array values buf[pos++] = val[i++]; }; /** * @override */ BufferWriter.prototype.bytes = function write_bytes_buffer(value) { if (util.isString(value)) value = util._Buffer_from(value, "base64"); var len = value.length >>> 0; this.uint32(len); if (len) this.push(writeBytesBuffer, len, value); return this; }; function writeStringBuffer(val, buf, pos) { if (val.length < 40) // plain js is faster for short strings (probably due to redundant assertions) util.utf8.write(val, buf, pos); else buf.utf8Write(val, pos); } /** * @override */ BufferWriter.prototype.string = function write_string_buffer(value) { var len = Buffer.byteLength(value); this.uint32(len); if (len) this.push(writeStringBuffer, len, value); return this; }; /** * Finishes the write operation. * @name BufferWriter#finish * @function * @returns {Buffer} Finished buffer */ },{"13":13,"14":14}]},{},[7]); })(tmp$6); var protobuf = tmp$6.protobuf; /** * @private */ function isBitSet(bits, mask) { return (bits & mask) !== 0; } // Bitmask for checking tile properties var childrenBitmasks = [0x01, 0x02, 0x04, 0x08]; var anyChildBitmask = 0x0f; var cacheFlagBitmask = 0x10; // True if there is a child subtree var imageBitmask = 0x40; var terrainBitmask = 0x80; /** * Contains information about each tile from a Google Earth Enterprise server * * @param {Number} bits Bitmask that contains the type of data and available children for each tile. * @param {Number} cnodeVersion Version of the request for subtree metadata. * @param {Number} imageryVersion Version of the request for imagery tile. * @param {Number} terrainVersion Version of the request for terrain tile. * @param {Number} imageryProvider Id of imagery provider. * @param {Number} terrainProvider Id of terrain provider. * * @private */ function GoogleEarthEnterpriseTileInformation( bits, cnodeVersion, imageryVersion, terrainVersion, imageryProvider, terrainProvider ) { this._bits = bits; this.cnodeVersion = cnodeVersion; this.imageryVersion = imageryVersion; this.terrainVersion = terrainVersion; this.imageryProvider = imageryProvider; this.terrainProvider = terrainProvider; this.ancestorHasTerrain = false; // Set it later once we find its parent this.terrainState = undefined; } /** * Creates GoogleEarthEnterpriseTileInformation from an object * * @param {Object} info Object to be cloned * @param {GoogleEarthEnterpriseTileInformation} [result] The object onto which to store the result. * @returns {GoogleEarthEnterpriseTileInformation} The modified result parameter or a new GoogleEarthEnterpriseTileInformation instance if none was provided. */ GoogleEarthEnterpriseTileInformation.clone = function (info, result) { if (!defined(result)) { result = new GoogleEarthEnterpriseTileInformation( info._bits, info.cnodeVersion, info.imageryVersion, info.terrainVersion, info.imageryProvider, info.terrainProvider ); } else { result._bits = info._bits; result.cnodeVersion = info.cnodeVersion; result.imageryVersion = info.imageryVersion; result.terrainVersion = info.terrainVersion; result.imageryProvider = info.imageryProvider; result.terrainProvider = info.terrainProvider; } result.ancestorHasTerrain = info.ancestorHasTerrain; result.terrainState = info.terrainState; return result; }; /** * Sets the parent for the tile * * @param {GoogleEarthEnterpriseTileInformation} parent Parent tile */ GoogleEarthEnterpriseTileInformation.prototype.setParent = function (parent) { this.ancestorHasTerrain = parent.ancestorHasTerrain || this.hasTerrain(); }; /** * Gets whether a subtree is available * * @returns {Boolean} true if subtree is available, false otherwise. */ GoogleEarthEnterpriseTileInformation.prototype.hasSubtree = function () { return isBitSet(this._bits, cacheFlagBitmask); }; /** * Gets whether imagery is available * * @returns {Boolean} true if imagery is available, false otherwise. */ GoogleEarthEnterpriseTileInformation.prototype.hasImagery = function () { return isBitSet(this._bits, imageBitmask); }; /** * Gets whether terrain is available * * @returns {Boolean} true if terrain is available, false otherwise. */ GoogleEarthEnterpriseTileInformation.prototype.hasTerrain = function () { return isBitSet(this._bits, terrainBitmask); }; /** * Gets whether any children are present * * @returns {Boolean} true if any children are available, false otherwise. */ GoogleEarthEnterpriseTileInformation.prototype.hasChildren = function () { return isBitSet(this._bits, anyChildBitmask); }; /** * Gets whether a specified child is available * * @param {Number} index Index of child tile * * @returns {Boolean} true if child is available, false otherwise */ GoogleEarthEnterpriseTileInformation.prototype.hasChild = function (index) { return isBitSet(this._bits, childrenBitmasks[index]); }; /** * Gets bitmask containing children * * @returns {Number} Children bitmask */ GoogleEarthEnterpriseTileInformation.prototype.getChildBitmask = function () { return this._bits & anyChildBitmask; }; function stringToBuffer(str) { var len = str.length; var buffer = new ArrayBuffer(len); var ui8 = new Uint8Array(buffer); for (var i = 0; i < len; ++i) { ui8[i] = str.charCodeAt(i); } return buffer; } // Decodes packet with a key that has been around since the beginning of Google Earth Enterprise var defaultKey = stringToBuffer( "\x45\xf4\xbd\x0b\x79\xe2\x6a\x45\x22\x05\x92\x2c\x17\xcd\x06\x71\xf8\x49\x10\x46\x67\x51\x00\x42\x25\xc6\xe8\x61\x2c\x66\x29\x08\xc6\x34\xdc\x6a\x62\x25\x79\x0a\x77\x1d\x6d\x69\xd6\xf0\x9c\x6b\x93\xa1\xbd\x4e\x75\xe0\x41\x04\x5b\xdf\x40\x56\x0c\xd9\xbb\x72\x9b\x81\x7c\x10\x33\x53\xee\x4f\x6c\xd4\x71\x05\xb0\x7b\xc0\x7f\x45\x03\x56\x5a\xad\x77\x55\x65\x0b\x33\x92\x2a\xac\x19\x6c\x35\x14\xc5\x1d\x30\x73\xf8\x33\x3e\x6d\x46\x38\x4a\xb4\xdd\xf0\x2e\xdd\x17\x75\x16\xda\x8c\x44\x74\x22\x06\xfa\x61\x22\x0c\x33\x22\x53\x6f\xaf\x39\x44\x0b\x8c\x0e\x39\xd9\x39\x13\x4c\xb9\xbf\x7f\xab\x5c\x8c\x50\x5f\x9f\x22\x75\x78\x1f\xe9\x07\x71\x91\x68\x3b\xc1\xc4\x9b\x7f\xf0\x3c\x56\x71\x48\x82\x05\x27\x55\x66\x59\x4e\x65\x1d\x98\x75\xa3\x61\x46\x7d\x61\x3f\x15\x41\x00\x9f\x14\x06\xd7\xb4\x34\x4d\xce\x13\x87\x46\xb0\x1a\xd5\x05\x1c\xb8\x8a\x27\x7b\x8b\xdc\x2b\xbb\x4d\x67\x30\xc8\xd1\xf6\x5c\x8f\x50\xfa\x5b\x2f\x46\x9b\x6e\x35\x18\x2f\x27\x43\x2e\xeb\x0a\x0c\x5e\x10\x05\x10\xa5\x73\x1b\x65\x34\xe5\x6c\x2e\x6a\x43\x27\x63\x14\x23\x55\xa9\x3f\x71\x7b\x67\x43\x7d\x3a\xaf\xcd\xe2\x54\x55\x9c\xfd\x4b\xc6\xe2\x9f\x2f\x28\xed\xcb\x5c\xc6\x2d\x66\x07\x88\xa7\x3b\x2f\x18\x2a\x22\x4e\x0e\xb0\x6b\x2e\xdd\x0d\x95\x7d\x7d\x47\xba\x43\xb2\x11\xb2\x2b\x3e\x4d\xaa\x3e\x7d\xe6\xce\x49\x89\xc6\xe6\x78\x0c\x61\x31\x05\x2d\x01\xa4\x4f\xa5\x7e\x71\x20\x88\xec\x0d\x31\xe8\x4e\x0b\x00\x6e\x50\x68\x7d\x17\x3d\x08\x0d\x17\x95\xa6\x6e\xa3\x68\x97\x24\x5b\x6b\xf3\x17\x23\xf3\xb6\x73\xb3\x0d\x0b\x40\xc0\x9f\xd8\x04\x51\x5d\xfa\x1a\x17\x22\x2e\x15\x6a\xdf\x49\x00\xb9\xa0\x77\x55\xc6\xef\x10\x6a\xbf\x7b\x47\x4c\x7f\x83\x17\x05\xee\xdc\xdc\x46\x85\xa9\xad\x53\x07\x2b\x53\x34\x06\x07\xff\x14\x94\x59\x19\x02\xe4\x38\xe8\x31\x83\x4e\xb9\x58\x46\x6b\xcb\x2d\x23\x86\x92\x70\x00\x35\x88\x22\xcf\x31\xb2\x26\x2f\xe7\xc3\x75\x2d\x36\x2c\x72\x74\xb0\x23\x47\xb7\xd3\xd1\x26\x16\x85\x37\x72\xe2\x00\x8c\x44\xcf\x10\xda\x33\x2d\x1a\xde\x60\x86\x69\x23\x69\x2a\x7c\xcd\x4b\x51\x0d\x95\x54\x39\x77\x2e\x29\xea\x1b\xa6\x50\xa2\x6a\x8f\x6f\x50\x99\x5c\x3e\x54\xfb\xef\x50\x5b\x0b\x07\x45\x17\x89\x6d\x28\x13\x77\x37\x1d\xdb\x8e\x1e\x4a\x05\x66\x4a\x6f\x99\x20\xe5\x70\xe2\xb9\x71\x7e\x0c\x6d\x49\x04\x2d\x7a\xfe\x72\xc7\xf2\x59\x30\x8f\xbb\x02\x5d\x73\xe5\xc9\x20\xea\x78\xec\x20\x90\xf0\x8a\x7f\x42\x17\x7c\x47\x19\x60\xb0\x16\xbd\x26\xb7\x71\xb6\xc7\x9f\x0e\xd1\x33\x82\x3d\xd3\xab\xee\x63\x99\xc8\x2b\x53\xa0\x44\x5c\x71\x01\xc6\xcc\x44\x1f\x32\x4f\x3c\xca\xc0\x29\x3d\x52\xd3\x61\x19\x58\xa9\x7d\x65\xb4\xdc\xcf\x0d\xf4\x3d\xf1\x08\xa9\x42\xda\x23\x09\xd8\xbf\x5e\x50\x49\xf8\x4d\xc0\xcb\x47\x4c\x1c\x4f\xf7\x7b\x2b\xd8\x16\x18\xc5\x31\x92\x3b\xb5\x6f\xdc\x6c\x0d\x92\x88\x16\xd1\x9e\xdb\x3f\xe2\xe9\xda\x5f\xd4\x84\xe2\x46\x61\x5a\xde\x1c\x55\xcf\xa4\x00\xbe\xfd\xce\x67\xf1\x4a\x69\x1c\x97\xe6\x20\x48\xd8\x5d\x7f\x7e\xae\x71\x20\x0e\x4e\xae\xc0\x56\xa9\x91\x01\x3c\x82\x1d\x0f\x72\xe7\x76\xec\x29\x49\xd6\x5d\x2d\x83\xe3\xdb\x36\x06\xa9\x3b\x66\x13\x97\x87\x6a\xd5\xb6\x3d\x50\x5e\x52\xb9\x4b\xc7\x73\x57\x78\xc9\xf4\x2e\x59\x07\x95\x93\x6f\xd0\x4b\x17\x57\x19\x3e\x27\x27\xc7\x60\xdb\x3b\xed\x9a\x0e\x53\x44\x16\x3e\x3f\x8d\x92\x6d\x77\xa2\x0a\xeb\x3f\x52\xa8\xc6\x55\x5e\x31\x49\x37\x85\xf4\xc5\x1f\x26\x2d\xa9\x1c\xbf\x8b\x27\x54\xda\xc3\x6a\x20\xe5\x2a\x78\x04\xb0\xd6\x90\x70\x72\xaa\x8b\x68\xbd\x88\xf7\x02\x5f\x48\xb1\x7e\xc0\x58\x4c\x3f\x66\x1a\xf9\x3e\xe1\x65\xc0\x70\xa7\xcf\x38\x69\xaf\xf0\x56\x6c\x64\x49\x9c\x27\xad\x78\x74\x4f\xc2\x87\xde\x56\x39\x00\xda\x77\x0b\xcb\x2d\x1b\x89\xfb\x35\x4f\x02\xf5\x08\x51\x13\x60\xc1\x0a\x5a\x47\x4d\x26\x1c\x33\x30\x78\xda\xc0\x9c\x46\x47\xe2\x5b\x79\x60\x49\x6e\x37\x67\x53\x0a\x3e\xe9\xec\x46\x39\xb2\xf1\x34\x0d\xc6\x84\x53\x75\x6e\xe1\x0c\x59\xd9\x1e\xde\x29\x85\x10\x7b\x49\x49\xa5\x77\x79\xbe\x49\x56\x2e\x36\xe7\x0b\x3a\xbb\x4f\x03\x62\x7b\xd2\x4d\x31\x95\x2f\xbd\x38\x7b\xa8\x4f\x21\xe1\xec\x46\x70\x76\x95\x7d\x29\x22\x78\x88\x0a\x90\xdd\x9d\x5c\xda\xde\x19\x51\xcf\xf0\xfc\x59\x52\x65\x7c\x33\x13\xdf\xf3\x48\xda\xbb\x2a\x75\xdb\x60\xb2\x02\x15\xd4\xfc\x19\xed\x1b\xec\x7f\x35\xa8\xff\x28\x31\x07\x2d\x12\xc8\xdc\x88\x46\x7c\x8a\x5b\x22" ); /** * Provides metadata using the Google Earth Enterprise REST API. This is used by the GoogleEarthEnterpriseImageryProvider * and GoogleEarthEnterpriseTerrainProvider to share metadata requests. * * @alias GoogleEarthEnterpriseMetadata * @constructor * * @param {Resource|String} resourceOrUrl The url of the Google Earth Enterprise server hosting the imagery * * @see GoogleEarthEnterpriseImageryProvider * @see GoogleEarthEnterpriseTerrainProvider * */ function GoogleEarthEnterpriseMetadata(resourceOrUrl) { //>>includeStart('debug', pragmas.debug); Check.defined("resourceOrUrl", resourceOrUrl); //>>includeEnd('debug'); var url = resourceOrUrl; if (typeof url !== "string" && !(url instanceof Resource)) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("resourceOrUrl.url", resourceOrUrl.url); //>>includeEnd('debug'); url = resourceOrUrl.url; } var resource = Resource.createIfNeeded(url); resource.appendForwardSlash(); this._resource = resource; /** * True if imagery is available. * @type {Boolean} * @default true */ this.imageryPresent = true; /** * True if imagery is sent as a protocol buffer, false if sent as plain images. If undefined we will try both. * @type {Boolean} * @default undefined */ this.protoImagery = undefined; /** * True if terrain is available. * @type {Boolean} * @default true */ this.terrainPresent = true; /** * Exponent used to compute constant to calculate negative height values. * @type {Number} * @default 32 */ this.negativeAltitudeExponentBias = 32; /** * Threshold where any numbers smaller are actually negative values. They are multiplied by -2^negativeAltitudeExponentBias. * @type {Number} * @default EPSILON12 */ this.negativeAltitudeThreshold = CesiumMath.EPSILON12; /** * Dictionary of provider id to copyright strings. * @type {Object} * @default {} */ this.providers = {}; /** * Key used to decode packets * @type {ArrayBuffer} */ this.key = undefined; this._quadPacketVersion = 1; this._tileInfo = {}; this._subtreePromises = {}; var that = this; this._readyPromise = requestDbRoot(this) .then(function () { return that.getQuadTreePacket("", that._quadPacketVersion); }) .then(function () { return true; }) .otherwise(function (e) { var message = "An error occurred while accessing " + getMetadataResource(that, "", 1).url + "."; return when.reject(new RuntimeError(message)); }); } Object.defineProperties(GoogleEarthEnterpriseMetadata.prototype, { /** * Gets the name of the Google Earth Enterprise server. * @memberof GoogleEarthEnterpriseMetadata.prototype * @type {String} * @readonly */ url: { get: function () { return this._resource.url; }, }, /** * Gets the proxy used for metadata requests. * @memberof GoogleEarthEnterpriseMetadata.prototype * @type {Proxy} * @readonly */ proxy: { get: function () { return this._resource.proxy; }, }, /** * Gets the resource used for metadata requests. * @memberof GoogleEarthEnterpriseMetadata.prototype * @type {Resource} * @readonly */ resource: { get: function () { return this._resource; }, }, /** * Gets a promise that resolves to true when the metadata is ready for use. * @memberof GoogleEarthEnterpriseMetadata.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise; }, }, }); /** * Converts a tiles (x, y, level) position into a quadkey used to request an image * from a Google Earth Enterprise server. * * @param {Number} x The tile's x coordinate. * @param {Number} y The tile's y coordinate. * @param {Number} level The tile's zoom level. * * @see GoogleEarthEnterpriseMetadata#quadKeyToTileXY */ GoogleEarthEnterpriseMetadata.tileXYToQuadKey = function (x, y, level) { var quadkey = ""; for (var i = level; i >= 0; --i) { var bitmask = 1 << i; var digit = 0; // Tile Layout // ___ ___ //| | | //| 3 | 2 | //|-------| //| 0 | 1 | //|___|___| // if (!isBitSet(y, bitmask)) { // Top Row digit |= 2; if (!isBitSet(x, bitmask)) { // Right to left digit |= 1; } } else if (isBitSet(x, bitmask)) { // Left to right digit |= 1; } quadkey += digit; } return quadkey; }; /** * Converts a tile's quadkey used to request an image from a Google Earth Enterprise server into the * (x, y, level) position. * * @param {String} quadkey The tile's quad key * * @see GoogleEarthEnterpriseMetadata#tileXYToQuadKey */ GoogleEarthEnterpriseMetadata.quadKeyToTileXY = function (quadkey) { var x = 0; var y = 0; var level = quadkey.length - 1; for (var i = level; i >= 0; --i) { var bitmask = 1 << i; var digit = +quadkey[level - i]; if (isBitSet(digit, 2)) { // Top Row if (!isBitSet(digit, 1)) { // // Right to left x |= bitmask; } } else { y |= bitmask; if (isBitSet(digit, 1)) { // Left to right x |= bitmask; } } } return { x: x, y: y, level: level, }; }; GoogleEarthEnterpriseMetadata.prototype.isValid = function (quadKey) { var info = this.getTileInformationFromQuadKey(quadKey); if (defined(info)) { return info !== null; } var valid = true; var q = quadKey; var last; while (q.length > 1) { last = q.substring(q.length - 1); q = q.substring(0, q.length - 1); info = this.getTileInformationFromQuadKey(q); if (defined(info)) { if (!info.hasSubtree() && !info.hasChild(parseInt(last))) { // We have no subtree or child available at some point in this node's ancestry valid = false; } break; } else if (info === null) { // Some node in the ancestry was loaded and said there wasn't a subtree valid = false; break; } } return valid; }; var taskProcessor$1 = new TaskProcessor("decodeGoogleEarthEnterprisePacket"); /** * Retrieves a Google Earth Enterprise quadtree packet. * * @param {String} [quadKey=''] The quadkey to retrieve the packet for. * @param {Number} [version=1] The cnode version to be used in the request. * @param {Request} [request] The request object. Intended for internal use only. * * @private */ GoogleEarthEnterpriseMetadata.prototype.getQuadTreePacket = function ( quadKey, version, request ) { version = defaultValue(version, 1); quadKey = defaultValue(quadKey, ""); var resource = getMetadataResource(this, quadKey, version, request); var promise = resource.fetchArrayBuffer(); if (!defined(promise)) { return undefined; // Throttled } var tileInfo = this._tileInfo; var key = this.key; return promise.then(function (metadata) { var decodePromise = taskProcessor$1.scheduleTask( { buffer: metadata, quadKey: quadKey, type: "Metadata", key: key, }, [metadata] ); return decodePromise.then(function (result) { var root; var topLevelKeyLength = -1; if (quadKey !== "") { // Root tile has no data except children bits, so put them into the tile info topLevelKeyLength = quadKey.length + 1; var top = result[quadKey]; root = tileInfo[quadKey]; root._bits |= top._bits; delete result[quadKey]; } // Copy the resulting objects into tileInfo // Make sure we start with shorter quadkeys first, so we know the parents have // already been processed. Otherwise we can lose ancestorHasTerrain along the way. var keys = Object.keys(result); keys.sort(function (a, b) { return a.length - b.length; }); var keysLength = keys.length; for (var i = 0; i < keysLength; ++i) { var key = keys[i]; var r = result[key]; if (r !== null) { var info = GoogleEarthEnterpriseTileInformation.clone(result[key]); var keyLength = key.length; if (keyLength === topLevelKeyLength) { info.setParent(root); } else if (keyLength > 1) { var parent = tileInfo[key.substring(0, key.length - 1)]; info.setParent(parent); } tileInfo[key] = info; } else { tileInfo[key] = null; } } }); }); }; /** * Populates the metadata subtree down to the specified tile. * * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise} A promise that resolves to the tile info for the requested quad key * * @private */ GoogleEarthEnterpriseMetadata.prototype.populateSubtree = function ( x, y, level, request ) { var quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); return populateSubtree(this, quadkey, request); }; function populateSubtree(that, quadKey, request) { var tileInfo = that._tileInfo; var q = quadKey; var t = tileInfo[q]; // If we have tileInfo make sure sure it is not a node with a subtree that's not loaded if (defined(t) && (!t.hasSubtree() || t.hasChildren())) { return t; } while (t === undefined && q.length > 1) { q = q.substring(0, q.length - 1); t = tileInfo[q]; } var subtreeRequest; var subtreePromises = that._subtreePromises; var promise = subtreePromises[q]; if (defined(promise)) { return promise.then(function () { // Recursively call this in case we need multiple subtree requests subtreeRequest = new Request({ throttle: request.throttle, throttleByServer: request.throttleByServer, type: request.type, priorityFunction: request.priorityFunction, }); return populateSubtree(that, quadKey, subtreeRequest); }); } // t is either // null so one of its parents was a leaf node, so this tile doesn't exist // exists but doesn't have a subtree to request // undefined so no parent exists - this shouldn't ever happen once the provider is ready if (!defined(t) || !t.hasSubtree()) { return when.reject( new RuntimeError("Couldn't load metadata for tile " + quadKey) ); } // We need to split up the promise here because when will execute syncronously if getQuadTreePacket // is already resolved (like in the tests), so subtreePromises will never get cleared out. // Only the initial request will also remove the promise from subtreePromises. promise = that.getQuadTreePacket(q, t.cnodeVersion, request); if (!defined(promise)) { return undefined; } subtreePromises[q] = promise; return promise .then(function () { // Recursively call this in case we need multiple subtree requests subtreeRequest = new Request({ throttle: request.throttle, throttleByServer: request.throttleByServer, type: request.type, priorityFunction: request.priorityFunction, }); return populateSubtree(that, quadKey, subtreeRequest); }) .always(function () { delete subtreePromises[q]; }); } /** * Gets information about a tile * * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded. * * @private */ GoogleEarthEnterpriseMetadata.prototype.getTileInformation = function ( x, y, level ) { var quadkey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); return this._tileInfo[quadkey]; }; /** * Gets information about a tile from a quadKey * * @param {String} quadkey The quadkey for the tile * @returns {GoogleEarthEnterpriseTileInformation|undefined} Information about the tile or undefined if it isn't loaded. * * @private */ GoogleEarthEnterpriseMetadata.prototype.getTileInformationFromQuadKey = function ( quadkey ) { return this._tileInfo[quadkey]; }; function getMetadataResource(that, quadKey, version, request) { return that._resource.getDerivedResource({ url: "flatfile?q2-0" + quadKey + "-q." + version.toString(), request: request, }); } var dbrootParser; var dbrootParserPromise; function requestDbRoot(that) { var resource = that._resource.getDerivedResource({ url: "dbRoot.v5", queryParameters: { output: "proto", }, }); if (!defined(dbrootParserPromise)) { var url = buildModuleUrl("ThirdParty/google-earth-dbroot-parser.js"); var oldValue = window.cesiumGoogleEarthDbRootParser; dbrootParserPromise = loadAndExecuteScript(url).then(function () { dbrootParser = window.cesiumGoogleEarthDbRootParser(protobuf); if (defined(oldValue)) { window.cesiumGoogleEarthDbRootParser = oldValue; } else { delete window.cesiumGoogleEarthDbRootParser; } }); } return dbrootParserPromise .then(function () { return resource.fetchArrayBuffer(); }) .then(function (buf) { var encryptedDbRootProto = dbrootParser.EncryptedDbRootProto.decode( new Uint8Array(buf) ); var byteArray = encryptedDbRootProto.encryptionData; var offset = byteArray.byteOffset; var end = offset + byteArray.byteLength; var key = (that.key = byteArray.buffer.slice(offset, end)); byteArray = encryptedDbRootProto.dbrootData; offset = byteArray.byteOffset; end = offset + byteArray.byteLength; var dbRootCompressed = byteArray.buffer.slice(offset, end); return taskProcessor$1.scheduleTask( { buffer: dbRootCompressed, type: "DbRoot", key: key, }, [dbRootCompressed] ); }) .then(function (result) { var dbRoot = dbrootParser.DbRootProto.decode( new Uint8Array(result.buffer) ); that.imageryPresent = defaultValue( dbRoot.imageryPresent, that.imageryPresent ); that.protoImagery = dbRoot.protoImagery; that.terrainPresent = defaultValue( dbRoot.terrainPresent, that.terrainPresent ); if (defined(dbRoot.endSnippet) && defined(dbRoot.endSnippet.model)) { var model = dbRoot.endSnippet.model; that.negativeAltitudeExponentBias = defaultValue( model.negativeAltitudeExponentBias, that.negativeAltitudeExponentBias ); that.negativeAltitudeThreshold = defaultValue( model.compressedNegativeAltitudeThreshold, that.negativeAltitudeThreshold ); } if (defined(dbRoot.databaseVersion)) { that._quadPacketVersion = defaultValue( dbRoot.databaseVersion.quadtreeVersion, that._quadPacketVersion ); } var providers = that.providers; var providerInfo = defaultValue(dbRoot.providerInfo, []); var count = providerInfo.length; for (var i = 0; i < count; ++i) { var provider = providerInfo[i]; var copyrightString = provider.copyrightString; if (defined(copyrightString)) { providers[provider.providerId] = new Credit(copyrightString.value); } } }) .otherwise(function () { // Just eat the error and use the default values. console.log("Failed to retrieve " + resource.url + ". Using defaults."); that.key = defaultKey; }); } /** * Terrain data for a single tile from a Google Earth Enterprise server. * * @alias GoogleEarthEnterpriseTerrainData * @constructor * * @param {Object} options Object with the following properties: * @param {ArrayBuffer} options.buffer The buffer containing terrain data. * @param {Number} options.negativeAltitudeExponentBias Multiplier for negative terrain heights that are encoded as very small positive values. * @param {Number} options.negativeElevationThreshold Threshold for negative values * @param {Number} [options.childTileMask=15] A bit mask indicating which of this tile's four children exist. * If a child's bit is set, geometry will be requested for that tile as well when it * is needed. If the bit is cleared, the child tile is not requested and geometry is * instead upsampled from the parent. The bit values are as follows: * * * * * * *
Bit PositionBit ValueChild Tile
01Southwest
12Southeast
24Northeast
38Northwest
* @param {Boolean} [options.createdByUpsampling=false] True if this instance was created by upsampling another instance; * otherwise, false. * @param {Credit[]} [options.credits] Array of credits for this tile. * * * @example * var buffer = ... * var childTileMask = ... * var terrainData = new Cesium.GoogleEarthEnterpriseTerrainData({ * buffer : heightBuffer, * childTileMask : childTileMask * }); * * @see TerrainData * @see HeightmapTerrainData * @see QuantizedMeshTerrainData */ function GoogleEarthEnterpriseTerrainData(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.buffer", options.buffer); Check.typeOf.number( "options.negativeAltitudeExponentBias", options.negativeAltitudeExponentBias ); Check.typeOf.number( "options.negativeElevationThreshold", options.negativeElevationThreshold ); //>>includeEnd('debug'); this._buffer = options.buffer; this._credits = options.credits; this._negativeAltitudeExponentBias = options.negativeAltitudeExponentBias; this._negativeElevationThreshold = options.negativeElevationThreshold; // Convert from google layout to layout of other providers // 3 2 -> 2 3 // 0 1 -> 0 1 var googleChildTileMask = defaultValue(options.childTileMask, 15); var childTileMask = googleChildTileMask & 3; // Bottom row is identical childTileMask |= googleChildTileMask & 4 ? 8 : 0; // NE childTileMask |= googleChildTileMask & 8 ? 4 : 0; // NW this._childTileMask = childTileMask; this._createdByUpsampling = defaultValue(options.createdByUpsampling, false); this._skirtHeight = undefined; this._bufferType = this._buffer.constructor; this._mesh = undefined; this._minimumHeight = undefined; this._maximumHeight = undefined; } Object.defineProperties(GoogleEarthEnterpriseTerrainData.prototype, { /** * An array of credits for this tile * @memberof GoogleEarthEnterpriseTerrainData.prototype * @type {Credit[]} */ credits: { get: function () { return this._credits; }, }, /** * The water mask included in this terrain data, if any. A water mask is a rectangular * Uint8Array or image where a value of 255 indicates water and a value of 0 indicates land. * Values in between 0 and 255 are allowed as well to smoothly blend between land and water. * @memberof GoogleEarthEnterpriseTerrainData.prototype * @type {Uint8Array|HTMLImageElement|HTMLCanvasElement} */ waterMask: { get: function () { return undefined; }, }, }); var createMeshTaskName = "createVerticesFromGoogleEarthEnterpriseBuffer"; var createMeshTaskProcessorNoThrottle = new TaskProcessor(createMeshTaskName); var createMeshTaskProcessorThrottle = new TaskProcessor( createMeshTaskName, TerrainData.maximumAsynchronousTasks ); var nativeRectangleScratch = new Rectangle(); var rectangleScratch$5 = new Rectangle(); /** * Creates a {@link TerrainMesh} from this terrain data. * * @private * * @param {Object} options Object with the following properties: * @param {TilingScheme} options.tilingScheme The tiling scheme to which this tile belongs. * @param {Number} options.x The X coordinate of the tile for which to create the terrain data. * @param {Number} options.y The Y coordinate of the tile for which to create the terrain data. * @param {Number} options.level The level of the tile for which to create the terrain data. * @param {Number} [options.exaggeration=1.0] The scale used to exaggerate the terrain. * @param {Boolean} [options.throttle=true] If true, indicates that this operation will need to be retried if too many asynchronous mesh creations are already in progress. * @returns {Promise.|undefined} A promise for the terrain mesh, or undefined if too many * asynchronous mesh creations are already in progress and the operation should * be retried later. */ GoogleEarthEnterpriseTerrainData.prototype.createMesh = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.tilingScheme", options.tilingScheme); Check.typeOf.number("options.x", options.x); Check.typeOf.number("options.y", options.y); Check.typeOf.number("options.level", options.level); //>>includeEnd('debug'); var tilingScheme = options.tilingScheme; var x = options.x; var y = options.y; var level = options.level; var exaggeration = defaultValue(options.exaggeration, 1.0); var throttle = defaultValue(options.throttle, true); var ellipsoid = tilingScheme.ellipsoid; tilingScheme.tileXYToNativeRectangle(x, y, level, nativeRectangleScratch); tilingScheme.tileXYToRectangle(x, y, level, rectangleScratch$5); // Compute the center of the tile for RTC rendering. var center = ellipsoid.cartographicToCartesian( Rectangle.center(rectangleScratch$5) ); var levelZeroMaxError = 40075.16; // From Google's Doc var thisLevelMaxError = levelZeroMaxError / (1 << level); this._skirtHeight = Math.min(thisLevelMaxError * 8.0, 1000.0); var createMeshTaskProcessor = throttle ? createMeshTaskProcessorThrottle : createMeshTaskProcessorNoThrottle; var verticesPromise = createMeshTaskProcessor.scheduleTask({ buffer: this._buffer, nativeRectangle: nativeRectangleScratch, rectangle: rectangleScratch$5, relativeToCenter: center, ellipsoid: ellipsoid, skirtHeight: this._skirtHeight, exaggeration: exaggeration, includeWebMercatorT: true, negativeAltitudeExponentBias: this._negativeAltitudeExponentBias, negativeElevationThreshold: this._negativeElevationThreshold, }); if (!defined(verticesPromise)) { // Postponed return undefined; } var that = this; return verticesPromise.then(function (result) { // Clone complex result objects because the transfer from the web worker // has stripped them down to JSON-style objects. that._mesh = new TerrainMesh( center, new Float32Array(result.vertices), new Uint16Array(result.indices), result.indexCountWithoutSkirts, result.vertexCountWithoutSkirts, result.minimumHeight, result.maximumHeight, BoundingSphere.clone(result.boundingSphere3D), Cartesian3.clone(result.occludeePointInScaledSpace), result.numberOfAttributes, OrientedBoundingBox.clone(result.orientedBoundingBox), TerrainEncoding.clone(result.encoding), exaggeration, result.westIndicesSouthToNorth, result.southIndicesEastToWest, result.eastIndicesNorthToSouth, result.northIndicesWestToEast ); that._minimumHeight = result.minimumHeight; that._maximumHeight = result.maximumHeight; // Free memory received from server after mesh is created. that._buffer = undefined; return that._mesh; }); }; /** * Computes the terrain height at a specified longitude and latitude. * * @param {Rectangle} rectangle The rectangle covered by this terrain data. * @param {Number} longitude The longitude in radians. * @param {Number} latitude The latitude in radians. * @returns {Number} The terrain height at the specified position. If the position * is outside the rectangle, this method will extrapolate the height, which is likely to be wildly * incorrect for positions far outside the rectangle. */ GoogleEarthEnterpriseTerrainData.prototype.interpolateHeight = function ( rectangle, longitude, latitude ) { var u = CesiumMath.clamp( (longitude - rectangle.west) / rectangle.width, 0.0, 1.0 ); var v = CesiumMath.clamp( (latitude - rectangle.south) / rectangle.height, 0.0, 1.0 ); if (!defined(this._mesh)) { return interpolateHeight(this, u, v, rectangle); } return interpolateMeshHeight(this, u, v); }; var upsampleTaskProcessor = new TaskProcessor( "upsampleQuantizedTerrainMesh", TerrainData.maximumAsynchronousTasks ); /** * Upsamples this terrain data for use by a descendant tile. The resulting instance will contain a subset of the * height samples in this instance, interpolated if necessary. * * @param {TilingScheme} tilingScheme The tiling scheme of this terrain data. * @param {Number} thisX The X coordinate of this tile in the tiling scheme. * @param {Number} thisY The Y coordinate of this tile in the tiling scheme. * @param {Number} thisLevel The level of this tile in the tiling scheme. * @param {Number} descendantX The X coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantY The Y coordinate within the tiling scheme of the descendant tile for which we are upsampling. * @param {Number} descendantLevel The level within the tiling scheme of the descendant tile for which we are upsampling. * @returns {Promise.|undefined} A promise for upsampled heightmap terrain data for the descendant tile, * or undefined if too many asynchronous upsample operations are in progress and the request has been * deferred. */ GoogleEarthEnterpriseTerrainData.prototype.upsample = function ( tilingScheme, thisX, thisY, thisLevel, descendantX, descendantY, descendantLevel ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("tilingScheme", tilingScheme); Check.typeOf.number("thisX", thisX); Check.typeOf.number("thisY", thisY); Check.typeOf.number("thisLevel", thisLevel); Check.typeOf.number("descendantX", descendantX); Check.typeOf.number("descendantY", descendantY); Check.typeOf.number("descendantLevel", descendantLevel); var levelDifference = descendantLevel - thisLevel; if (levelDifference > 1) { throw new DeveloperError( "Upsampling through more than one level at a time is not currently supported." ); } //>>includeEnd('debug'); var mesh = this._mesh; if (!defined(this._mesh)) { return undefined; } var isEastChild = thisX * 2 !== descendantX; var isNorthChild = thisY * 2 === descendantY; var ellipsoid = tilingScheme.ellipsoid; var childRectangle = tilingScheme.tileXYToRectangle( descendantX, descendantY, descendantLevel ); var upsamplePromise = upsampleTaskProcessor.scheduleTask({ vertices: mesh.vertices, indices: mesh.indices, indexCountWithoutSkirts: mesh.indexCountWithoutSkirts, vertexCountWithoutSkirts: mesh.vertexCountWithoutSkirts, encoding: mesh.encoding, minimumHeight: this._minimumHeight, maximumHeight: this._maximumHeight, isEastChild: isEastChild, isNorthChild: isNorthChild, childRectangle: childRectangle, ellipsoid: ellipsoid, exaggeration: mesh.exaggeration, }); if (!defined(upsamplePromise)) { // Postponed return undefined; } var that = this; return upsamplePromise.then(function (result) { var quantizedVertices = new Uint16Array(result.vertices); var indicesTypedArray = IndexDatatype$1.createTypedArray( quantizedVertices.length / 3, result.indices ); var skirtHeight = that._skirtHeight; // Use QuantizedMeshTerrainData since we have what we need already parsed return new QuantizedMeshTerrainData({ quantizedVertices: quantizedVertices, indices: indicesTypedArray, minimumHeight: result.minimumHeight, maximumHeight: result.maximumHeight, boundingSphere: BoundingSphere.clone(result.boundingSphere), orientedBoundingBox: OrientedBoundingBox.clone( result.orientedBoundingBox ), horizonOcclusionPoint: Cartesian3.clone(result.horizonOcclusionPoint), westIndices: result.westIndices, southIndices: result.southIndices, eastIndices: result.eastIndices, northIndices: result.northIndices, westSkirtHeight: skirtHeight, southSkirtHeight: skirtHeight, eastSkirtHeight: skirtHeight, northSkirtHeight: skirtHeight, childTileMask: 0, createdByUpsampling: true, credits: that._credits, }); }); }; /** * Determines if a given child tile is available, based on the * {@link HeightmapTerrainData.childTileMask}. The given child tile coordinates are assumed * to be one of the four children of this tile. If non-child tile coordinates are * given, the availability of the southeast child tile is returned. * * @param {Number} thisX The tile X coordinate of this (the parent) tile. * @param {Number} thisY The tile Y coordinate of this (the parent) tile. * @param {Number} childX The tile X coordinate of the child tile to check for availability. * @param {Number} childY The tile Y coordinate of the child tile to check for availability. * @returns {Boolean} True if the child tile is available; otherwise, false. */ GoogleEarthEnterpriseTerrainData.prototype.isChildAvailable = function ( thisX, thisY, childX, childY ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("thisX", thisX); Check.typeOf.number("thisY", thisY); Check.typeOf.number("childX", childX); Check.typeOf.number("childY", childY); //>>includeEnd('debug'); var bitNumber = 2; // northwest child if (childX !== thisX * 2) { ++bitNumber; // east child } if (childY !== thisY * 2) { bitNumber -= 2; // south child } return (this._childTileMask & (1 << bitNumber)) !== 0; }; /** * Gets a value indicating whether or not this terrain data was created by upsampling lower resolution * terrain data. If this value is false, the data was obtained from some other source, such * as by downloading it from a remote server. This method should return true for instances * returned from a call to {@link HeightmapTerrainData#upsample}. * * @returns {Boolean} True if this instance was created by upsampling; otherwise, false. */ GoogleEarthEnterpriseTerrainData.prototype.wasCreatedByUpsampling = function () { return this._createdByUpsampling; }; var texCoordScratch0 = new Cartesian2(); var texCoordScratch1 = new Cartesian2(); var texCoordScratch2 = new Cartesian2(); var barycentricCoordinateScratch = new Cartesian3(); function interpolateMeshHeight(terrainData, u, v) { var mesh = terrainData._mesh; var vertices = mesh.vertices; var encoding = mesh.encoding; var indices = mesh.indices; for (var i = 0, len = indices.length; i < len; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; var i2 = indices[i + 2]; var uv0 = encoding.decodeTextureCoordinates(vertices, i0, texCoordScratch0); var uv1 = encoding.decodeTextureCoordinates(vertices, i1, texCoordScratch1); var uv2 = encoding.decodeTextureCoordinates(vertices, i2, texCoordScratch2); var barycentric = Intersections2D.computeBarycentricCoordinates( u, v, uv0.x, uv0.y, uv1.x, uv1.y, uv2.x, uv2.y, barycentricCoordinateScratch ); if ( barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15 ) { var h0 = encoding.decodeHeight(vertices, i0); var h1 = encoding.decodeHeight(vertices, i1); var h2 = encoding.decodeHeight(vertices, i2); return barycentric.x * h0 + barycentric.y * h1 + barycentric.z * h2; } } // Position does not lie in any triangle in this mesh. return undefined; } var sizeOfUint16 = Uint16Array.BYTES_PER_ELEMENT; var sizeOfUint32$8 = Uint32Array.BYTES_PER_ELEMENT; var sizeOfInt32 = Int32Array.BYTES_PER_ELEMENT; var sizeOfFloat = Float32Array.BYTES_PER_ELEMENT; var sizeOfDouble = Float64Array.BYTES_PER_ELEMENT; function interpolateHeight(terrainData, u, v, rectangle) { var buffer = terrainData._buffer; var quad = 0; // SW var uStart = 0.0; var vStart = 0.0; if (v > 0.5) { // Upper row if (u > 0.5) { // NE quad = 2; uStart = 0.5; } else { // NW quad = 3; } vStart = 0.5; } else if (u > 0.5) { // SE quad = 1; uStart = 0.5; } var dv = new DataView(buffer); var offset = 0; for (var q = 0; q < quad; ++q) { offset += dv.getUint32(offset, true); offset += sizeOfUint32$8; } offset += sizeOfUint32$8; // Skip length of quad offset += 2 * sizeOfDouble; // Skip origin // Read sizes var xSize = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); offset += sizeOfDouble; var ySize = CesiumMath.toRadians(dv.getFloat64(offset, true) * 180.0); offset += sizeOfDouble; // Samples per quad var xScale = rectangle.width / xSize / 2; var yScale = rectangle.height / ySize / 2; // Number of points var numPoints = dv.getInt32(offset, true); offset += sizeOfInt32; // Number of faces var numIndices = dv.getInt32(offset, true) * 3; offset += sizeOfInt32; offset += sizeOfInt32; // Skip Level var uBuffer = new Array(numPoints); var vBuffer = new Array(numPoints); var heights = new Array(numPoints); var i; for (i = 0; i < numPoints; ++i) { uBuffer[i] = uStart + dv.getUint8(offset++) * xScale; vBuffer[i] = vStart + dv.getUint8(offset++) * yScale; // Height is stored in units of (1/EarthRadius) or (1/6371010.0) heights[i] = dv.getFloat32(offset, true) * 6371010.0; offset += sizeOfFloat; } var indices = new Array(numIndices); for (i = 0; i < numIndices; ++i) { indices[i] = dv.getUint16(offset, true); offset += sizeOfUint16; } for (i = 0; i < numIndices; i += 3) { var i0 = indices[i]; var i1 = indices[i + 1]; var i2 = indices[i + 2]; var u0 = uBuffer[i0]; var u1 = uBuffer[i1]; var u2 = uBuffer[i2]; var v0 = vBuffer[i0]; var v1 = vBuffer[i1]; var v2 = vBuffer[i2]; var barycentric = Intersections2D.computeBarycentricCoordinates( u, v, u0, v0, u1, v1, u2, v2, barycentricCoordinateScratch ); if ( barycentric.x >= -1e-15 && barycentric.y >= -1e-15 && barycentric.z >= -1e-15 ) { return ( barycentric.x * heights[i0] + barycentric.y * heights[i1] + barycentric.z * heights[i2] ); } } // Position does not lie in any triangle in this mesh. return undefined; } var TerrainState$2 = { UNKNOWN: 0, NONE: 1, SELF: 2, PARENT: 3, }; var julianDateScratch = new JulianDate(); function TerrainCache() { this._terrainCache = {}; this._lastTidy = JulianDate.now(); } TerrainCache.prototype.add = function (quadKey, buffer) { this._terrainCache[quadKey] = { buffer: buffer, timestamp: JulianDate.now(), }; }; TerrainCache.prototype.get = function (quadKey) { var terrainCache = this._terrainCache; var result = terrainCache[quadKey]; if (defined(result)) { delete this._terrainCache[quadKey]; return result.buffer; } }; TerrainCache.prototype.tidy = function () { JulianDate.now(julianDateScratch); if (JulianDate.secondsDifference(julianDateScratch, this._lastTidy) > 10) { var terrainCache = this._terrainCache; var keys = Object.keys(terrainCache); var count = keys.length; for (var i = 0; i < count; ++i) { var k = keys[i]; var e = terrainCache[k]; if (JulianDate.secondsDifference(julianDateScratch, e.timestamp) > 10) { delete terrainCache[k]; } } JulianDate.clone(julianDateScratch, this._lastTidy); } }; /** * Provides tiled terrain using the Google Earth Enterprise REST API. * * @alias GoogleEarthEnterpriseTerrainProvider * @constructor * * @param {Object} options Object with the following properties: * @param {Resource|String} options.url The url of the Google Earth Enterprise server hosting the imagery. * @param {GoogleEarthEnterpriseMetadata} options.metadata A metadata object that can be used to share metadata requests with a GoogleEarthEnterpriseImageryProvider. * @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If not specified, the WGS84 ellipsoid is used. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * * @see GoogleEarthEnterpriseImageryProvider * @see CesiumTerrainProvider * * @example * var geeMetadata = new GoogleEarthEnterpriseMetadata('http://www.earthenterprise.org/3d'); * var gee = new Cesium.GoogleEarthEnterpriseTerrainProvider({ * metadata : geeMetadata * }); * * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} */ function GoogleEarthEnterpriseTerrainProvider(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!(defined(options.url) || defined(options.metadata))) { throw new DeveloperError("options.url or options.metadata is required."); } //>>includeEnd('debug'); var metadata; if (defined(options.metadata)) { metadata = options.metadata; } else { var resource = Resource.createIfNeeded(options.url); metadata = new GoogleEarthEnterpriseMetadata(resource); } this._metadata = metadata; this._tilingScheme = new GeographicTilingScheme({ numberOfLevelZeroTilesX: 2, numberOfLevelZeroTilesY: 2, rectangle: new Rectangle( -CesiumMath.PI, -CesiumMath.PI, CesiumMath.PI, CesiumMath.PI ), ellipsoid: options.ellipsoid, }); var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } this._credit = credit; // Pulled from Google's documentation this._levelZeroMaximumGeometricError = 40075.16; this._terrainCache = new TerrainCache(); this._terrainPromises = {}; this._terrainRequests = {}; this._errorEvent = new Event(); this._ready = false; var that = this; var metadataError; this._readyPromise = metadata.readyPromise .then(function (result) { if (!metadata.terrainPresent) { var e = new RuntimeError( "The server " + metadata.url + " doesn't have terrain" ); metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e ); return when.reject(e); } TileProviderError.handleSuccess(metadataError); that._ready = result; return result; }) .otherwise(function (e) { metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, e.message, undefined, undefined, undefined, e ); return when.reject(e); }); } Object.defineProperties(GoogleEarthEnterpriseTerrainProvider.prototype, { /** * Gets the name of the Google Earth Enterprise server url hosting the imagery. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {String} * @readonly */ url: { get: function () { return this._metadata.url; }, }, /** * Gets the proxy used by this provider. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Proxy} * @readonly */ proxy: { get: function () { return this._metadata.proxy; }, }, /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {TilingScheme} * @readonly */ tilingScheme: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this._ready) { throw new DeveloperError( "tilingScheme must not be called before the imagery provider is ready." ); } //>>includeEnd('debug'); return this._tilingScheme; }, }, /** * Gets an event that is raised when the imagery provider encounters an asynchronous error. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Event} * @readonly */ errorEvent: { get: function () { return this._errorEvent; }, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise; }, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should not be called before {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Credit} * @readonly */ credit: { get: function () { return this._credit; }, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Boolean} */ hasWaterMask: { get: function () { return false; }, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {Boolean} */ hasVertexNormals: { get: function () { return false; }, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. * @memberof GoogleEarthEnterpriseTerrainProvider.prototype * @type {TileAvailability} */ availability: { get: function () { return undefined; }, }, }); var taskProcessor = new TaskProcessor("decodeGoogleEarthEnterprisePacket"); // If the tile has its own terrain, then you can just use its child bitmask. If it was requested using it's parent // then you need to check all of its children to see if they have terrain. function computeChildMask(quadKey, info, metadata) { var childMask = info.getChildBitmask(); if (info.terrainState === TerrainState$2.PARENT) { childMask = 0; for (var i = 0; i < 4; ++i) { var child = metadata.getTileInformationFromQuadKey( quadKey + i.toString() ); if (defined(child) && child.hasTerrain()) { childMask |= 1 << i; } } } return childMask; } /** * Requests the geometry for a given tile. This function should not be called before * {@link GoogleEarthEnterpriseTerrainProvider#ready} returns true. The result must include terrain data and * may optionally include a water mask and an indication of which child tiles are available. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. * * @exception {DeveloperError} This function must not be called before {@link GoogleEarthEnterpriseTerrainProvider#ready} * returns true. */ GoogleEarthEnterpriseTerrainProvider.prototype.requestTileGeometry = function ( x, y, level, request ) { //>>includeStart('debug', pragmas.debug) if (!this._ready) { throw new DeveloperError( "requestTileGeometry must not be called before the terrain provider is ready." ); } //>>includeEnd('debug'); var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); var terrainCache = this._terrainCache; var metadata = this._metadata; var info = metadata.getTileInformationFromQuadKey(quadKey); // Check if this tile is even possibly available if (!defined(info)) { return when.reject(new RuntimeError("Terrain tile doesn't exist")); } var terrainState = info.terrainState; if (!defined(terrainState)) { // First time we have tried to load this tile, so set terrain state to UNKNOWN terrainState = info.terrainState = TerrainState$2.UNKNOWN; } // If its in the cache, return it var buffer = terrainCache.get(quadKey); if (defined(buffer)) { var credit = metadata.providers[info.terrainProvider]; return when.resolve( new GoogleEarthEnterpriseTerrainData({ buffer: buffer, childTileMask: computeChildMask(quadKey, info, metadata), credits: defined(credit) ? [credit] : undefined, negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias, negativeElevationThreshold: metadata.negativeAltitudeThreshold, }) ); } // Clean up the cache terrainCache.tidy(); // We have a tile, check to see if no ancestors have terrain or that we know for sure it doesn't if (!info.ancestorHasTerrain) { // We haven't reached a level with terrain, so return the ellipsoid return when.resolve( new HeightmapTerrainData({ buffer: new Uint8Array(16 * 16), width: 16, height: 16, }) ); } else if (terrainState === TerrainState$2.NONE) { // Already have info and there isn't any terrain here return when.reject(new RuntimeError("Terrain tile doesn't exist")); } // Figure out where we are getting the terrain and what version var parentInfo; var q = quadKey; var terrainVersion = -1; switch (terrainState) { case TerrainState$2.SELF: // We have terrain and have retrieved it before terrainVersion = info.terrainVersion; break; case TerrainState$2.PARENT: // We have terrain in our parent q = q.substring(0, q.length - 1); parentInfo = metadata.getTileInformationFromQuadKey(q); terrainVersion = parentInfo.terrainVersion; break; case TerrainState$2.UNKNOWN: // We haven't tried to retrieve terrain yet if (info.hasTerrain()) { terrainVersion = info.terrainVersion; // We should have terrain } else { q = q.substring(0, q.length - 1); parentInfo = metadata.getTileInformationFromQuadKey(q); if (defined(parentInfo) && parentInfo.hasTerrain()) { terrainVersion = parentInfo.terrainVersion; // Try checking in the parent } } break; } // We can't figure out where to get the terrain if (terrainVersion < 0) { return when.reject(new RuntimeError("Terrain tile doesn't exist")); } // Load that terrain var terrainPromises = this._terrainPromises; var terrainRequests = this._terrainRequests; var sharedPromise; var sharedRequest; if (defined(terrainPromises[q])) { // Already being loaded possibly from another child, so return existing promise sharedPromise = terrainPromises[q]; sharedRequest = terrainRequests[q]; } else { // Create new request for terrain sharedRequest = request; var requestPromise = buildTerrainResource( this, q, terrainVersion, sharedRequest ).fetchArrayBuffer(); if (!defined(requestPromise)) { return undefined; // Throttled } sharedPromise = requestPromise.then(function (terrain) { if (defined(terrain)) { return taskProcessor .scheduleTask( { buffer: terrain, type: "Terrain", key: metadata.key, }, [terrain] ) .then(function (terrainTiles) { // Add requested tile and mark it as SELF var requestedInfo = metadata.getTileInformationFromQuadKey(q); requestedInfo.terrainState = TerrainState$2.SELF; terrainCache.add(q, terrainTiles[0]); var provider = requestedInfo.terrainProvider; // Add children to cache var count = terrainTiles.length - 1; for (var j = 0; j < count; ++j) { var childKey = q + j.toString(); var child = metadata.getTileInformationFromQuadKey(childKey); if (defined(child)) { terrainCache.add(childKey, terrainTiles[j + 1]); child.terrainState = TerrainState$2.PARENT; if (child.terrainProvider === 0) { child.terrainProvider = provider; } } } }); } return when.reject(new RuntimeError("Failed to load terrain.")); }); terrainPromises[q] = sharedPromise; // Store promise without delete from terrainPromises terrainRequests[q] = sharedRequest; // Set promise so we remove from terrainPromises just one time sharedPromise = sharedPromise.always(function () { delete terrainPromises[q]; delete terrainRequests[q]; }); } return sharedPromise .then(function () { var buffer = terrainCache.get(quadKey); if (defined(buffer)) { var credit = metadata.providers[info.terrainProvider]; return new GoogleEarthEnterpriseTerrainData({ buffer: buffer, childTileMask: computeChildMask(quadKey, info, metadata), credits: defined(credit) ? [credit] : undefined, negativeAltitudeExponentBias: metadata.negativeAltitudeExponentBias, negativeElevationThreshold: metadata.negativeAltitudeThreshold, }); } return when.reject(new RuntimeError("Failed to load terrain.")); }) .otherwise(function (error) { if (sharedRequest.state === RequestState$1.CANCELLED) { request.state = sharedRequest.state; return when.reject(error); } info.terrainState = TerrainState$2.NONE; return when.reject(error); }); }; /** * Gets the maximum geometric error allowed in a tile at a given level. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ GoogleEarthEnterpriseTerrainProvider.prototype.getLevelMaximumGeometricError = function ( level ) { return this._levelZeroMaximumGeometricError / (1 << level); }; /** * Determines whether data for a tile is available to be loaded. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported, otherwise true or false. */ GoogleEarthEnterpriseTerrainProvider.prototype.getTileDataAvailable = function ( x, y, level ) { var metadata = this._metadata; var quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level); var info = metadata.getTileInformation(x, y, level); if (info === null) { return false; } if (defined(info)) { if (!info.ancestorHasTerrain) { return true; // We'll just return the ellipsoid } var terrainState = info.terrainState; if (terrainState === TerrainState$2.NONE) { return false; // Terrain is not available } if (!defined(terrainState) || terrainState === TerrainState$2.UNKNOWN) { info.terrainState = TerrainState$2.UNKNOWN; if (!info.hasTerrain()) { quadKey = quadKey.substring(0, quadKey.length - 1); var parentInfo = metadata.getTileInformationFromQuadKey(quadKey); if (!defined(parentInfo) || !parentInfo.hasTerrain()) { return false; } } } return true; } if (metadata.isValid(quadKey)) { // We will need this tile, so request metadata and return false for now var request = new Request({ throttle: false, throttleByServer: true, type: RequestType$1.TERRAIN, }); metadata.populateSubtree(x, y, level, request); } return false; }; /** * Makes sure we load availability data for a tile * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ GoogleEarthEnterpriseTerrainProvider.prototype.loadTileDataAvailability = function ( x, y, level ) { return undefined; }; // // Functions to handle imagery packets // function buildTerrainResource(terrainProvider, quadKey, version, request) { version = defined(version) && version > 0 ? version : 1; return terrainProvider._metadata.resource.getDerivedResource({ url: "flatfile?f1c-0" + quadKey + "-t." + version.toString(), request: request, }); } var PROJECTIONS = [GeographicProjection, WebMercatorProjection]; var PROJECTION_COUNT = PROJECTIONS.length; var MITER_BREAK_SMALL = Math.cos(CesiumMath.toRadians(30.0)); var MITER_BREAK_LARGE = Math.cos(CesiumMath.toRadians(150.0)); // Initial heights for constructing the wall. // Keeping WALL_INITIAL_MIN_HEIGHT near the ellipsoid surface helps // prevent precision problems with planes in the shader. // Putting the start point of a plane at ApproximateTerrainHeights._defaultMinTerrainHeight, // which is a highly conservative bound, usually puts the plane origin several thousands // of meters away from the actual terrain, causing floating point problems when checking // fragments on terrain against the plane. // Ellipsoid height is generally much closer. // The initial max height is arbitrary. // Both heights are corrected using ApproximateTerrainHeights for computing the actual volume geometry. var WALL_INITIAL_MIN_HEIGHT = 0.0; var WALL_INITIAL_MAX_HEIGHT = 1000.0; /** * A description of a polyline on terrain or 3D Tiles. Only to be used with {@link GroundPolylinePrimitive}. * * @alias GroundPolylineGeometry * @constructor * * @param {Object} options Options with the following properties: * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the polyline's points. Heights above the ellipsoid will be ignored. * @param {Number} [options.width=1.0] The screen space width in pixels. * @param {Number} [options.granularity=9999.0] The distance interval in meters used for interpolating options.points. Defaults to 9999.0 meters. Zero indicates no interpolation. * @param {Boolean} [options.loop=false] Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polyline segments must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * * @exception {DeveloperError} At least two positions are required. * * @see GroundPolylinePrimitive * * @example * var positions = Cesium.Cartesian3.fromDegreesArray([ * -112.1340164450331, 36.05494287836128, * -112.08821010582645, 36.097804071380715, * -112.13296079730024, 36.168769146801104 * ]); * * var geometry = new Cesium.GroundPolylineGeometry({ * positions : positions * }); */ function GroundPolylineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; //>>includeStart('debug', pragmas.debug); if (!defined(positions) || positions.length < 2) { throw new DeveloperError("At least two positions are required."); } if ( defined(options.arcType) && options.arcType !== ArcType$1.GEODESIC && options.arcType !== ArcType$1.RHUMB ) { throw new DeveloperError( "Valid options for arcType are ArcType.GEODESIC and ArcType.RHUMB." ); } //>>includeEnd('debug'); /** * The screen space width in pixels. * @type {Number} */ this.width = defaultValue(options.width, 1.0); // Doesn't get packed, not necessary for computing geometry. this._positions = positions; /** * The distance interval used for interpolating options.points. Zero indicates no interpolation. * Default of 9999.0 allows centimeter accuracy with 32 bit floating point. * @type {Boolean} * @default 9999.0 */ this.granularity = defaultValue(options.granularity, 9999.0); /** * Whether during geometry creation a line segment will be added between the last and first line positions to make this Polyline a loop. * If the geometry has two positions this parameter will be ignored. * @type {Boolean} * @default false */ this.loop = defaultValue(options.loop, false); /** * The type of path the polyline must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * @type {ArcType} * @default ArcType.GEODESIC */ this.arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); this._ellipsoid = Ellipsoid.WGS84; // MapProjections can't be packed, so store the index to a known MapProjection. this._projectionIndex = 0; this._workerName = "createGroundPolylineGeometry"; // Used by GroundPolylinePrimitive to signal worker that scenemode is 3D only. this._scene3DOnly = false; } Object.defineProperties(GroundPolylineGeometry.prototype, { /** * The number of elements used to pack the object into an array. * @memberof GroundPolylineGeometry.prototype * @type {Number} * @readonly * @private */ packedLength: { get: function () { return ( 1.0 + this._positions.length * 3 + 1.0 + 1.0 + 1.0 + Ellipsoid.packedLength + 1.0 + 1.0 ); }, }, }); /** * Set the GroundPolylineGeometry's projection and ellipsoid. * Used by GroundPolylinePrimitive to signal scene information to the geometry for generating 2D attributes. * * @param {GroundPolylineGeometry} groundPolylineGeometry GroundPolylinGeometry describing a polyline on terrain or 3D Tiles. * @param {Projection} mapProjection A MapProjection used for projecting cartographic coordinates to 2D. * @private */ GroundPolylineGeometry.setProjectionAndEllipsoid = function ( groundPolylineGeometry, mapProjection ) { var projectionIndex = 0; for (var i = 0; i < PROJECTION_COUNT; i++) { if (mapProjection instanceof PROJECTIONS[i]) { projectionIndex = i; break; } } groundPolylineGeometry._projectionIndex = projectionIndex; groundPolylineGeometry._ellipsoid = mapProjection.ellipsoid; }; var cart3Scratch1 = new Cartesian3(); var cart3Scratch2 = new Cartesian3(); var cart3Scratch3 = new Cartesian3(); function computeRightNormal(start, end, maxHeight, ellipsoid, result) { var startBottom = getPosition$2(ellipsoid, start, 0.0, cart3Scratch1); var startTop = getPosition$2(ellipsoid, start, maxHeight, cart3Scratch2); var endBottom = getPosition$2(ellipsoid, end, 0.0, cart3Scratch3); var up = direction(startTop, startBottom, cart3Scratch2); var forward = direction(endBottom, startBottom, cart3Scratch3); Cartesian3.cross(forward, up, result); return Cartesian3.normalize(result, result); } var interpolatedCartographicScratch$1 = new Cartographic(); var interpolatedBottomScratch = new Cartesian3(); var interpolatedTopScratch = new Cartesian3(); var interpolatedNormalScratch = new Cartesian3(); function interpolateSegment( start, end, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray ) { if (granularity === 0.0) { return; } var ellipsoidLine; if (arcType === ArcType$1.GEODESIC) { ellipsoidLine = new EllipsoidGeodesic(start, end, ellipsoid); } else if (arcType === ArcType$1.RHUMB) { ellipsoidLine = new EllipsoidRhumbLine(start, end, ellipsoid); } var surfaceDistance = ellipsoidLine.surfaceDistance; if (surfaceDistance < granularity) { return; } // Compute rightwards normal applicable at all interpolated points var interpolatedNormal = computeRightNormal( start, end, maxHeight, ellipsoid, interpolatedNormalScratch ); var segments = Math.ceil(surfaceDistance / granularity); var interpointDistance = surfaceDistance / segments; var distanceFromStart = interpointDistance; var pointsToAdd = segments - 1; var packIndex = normalsArray.length; for (var i = 0; i < pointsToAdd; i++) { var interpolatedCartographic = ellipsoidLine.interpolateUsingSurfaceDistance( distanceFromStart, interpolatedCartographicScratch$1 ); var interpolatedBottom = getPosition$2( ellipsoid, interpolatedCartographic, minHeight, interpolatedBottomScratch ); var interpolatedTop = getPosition$2( ellipsoid, interpolatedCartographic, maxHeight, interpolatedTopScratch ); Cartesian3.pack(interpolatedNormal, normalsArray, packIndex); Cartesian3.pack(interpolatedBottom, bottomPositionsArray, packIndex); Cartesian3.pack(interpolatedTop, topPositionsArray, packIndex); cartographicsArray.push(interpolatedCartographic.latitude); cartographicsArray.push(interpolatedCartographic.longitude); packIndex += 3; distanceFromStart += interpointDistance; } } var heightlessCartographicScratch = new Cartographic(); function getPosition$2(ellipsoid, cartographic, height, result) { Cartographic.clone(cartographic, heightlessCartographicScratch); heightlessCartographicScratch.height = height; return Cartographic.toCartesian( heightlessCartographicScratch, ellipsoid, result ); } /** * Stores the provided instance into the provided array. * * @param {PolygonGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ GroundPolylineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); var index = defaultValue(startingIndex, 0); var positions = value._positions; var positionsLength = positions.length; array[index++] = positionsLength; for (var i = 0; i < positionsLength; ++i) { var cartesian = positions[i]; Cartesian3.pack(cartesian, array, index); index += 3; } array[index++] = value.granularity; array[index++] = value.loop ? 1.0 : 0.0; array[index++] = value.arcType; Ellipsoid.pack(value._ellipsoid, array, index); index += Ellipsoid.packedLength; array[index++] = value._projectionIndex; array[index++] = value._scene3DOnly ? 1.0 : 0.0; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolygonGeometry} [result] The object into which to store the result. */ GroundPolylineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); var index = defaultValue(startingIndex, 0); var positionsLength = array[index++]; var positions = new Array(positionsLength); for (var i = 0; i < positionsLength; i++) { positions[i] = Cartesian3.unpack(array, index); index += 3; } var granularity = array[index++]; var loop = array[index++] === 1.0; var arcType = array[index++]; var ellipsoid = Ellipsoid.unpack(array, index); index += Ellipsoid.packedLength; var projectionIndex = array[index++]; var scene3DOnly = array[index++] === 1.0; if (!defined(result)) { result = new GroundPolylineGeometry({ positions: positions, }); } result._positions = positions; result.granularity = granularity; result.loop = loop; result.arcType = arcType; result._ellipsoid = ellipsoid; result._projectionIndex = projectionIndex; result._scene3DOnly = scene3DOnly; return result; }; function direction(target, origin, result) { Cartesian3.subtract(target, origin, result); Cartesian3.normalize(result, result); return result; } function tangentDirection(target, origin, up, result) { result = direction(target, origin, result); // orthogonalize result = Cartesian3.cross(result, up, result); result = Cartesian3.normalize(result, result); result = Cartesian3.cross(up, result, result); return result; } var toPreviousScratch = new Cartesian3(); var toNextScratch = new Cartesian3(); var forwardScratch = new Cartesian3(); var vertexUpScratch = new Cartesian3(); var cosine90 = 0.0; var cosine180 = -1.0; function computeVertexMiterNormal( previousBottom, vertexBottom, vertexTop, nextBottom, result ) { var up = direction(vertexTop, vertexBottom, vertexUpScratch); // Compute vectors pointing towards neighboring points but tangent to this point on the ellipsoid var toPrevious = tangentDirection( previousBottom, vertexBottom, up, toPreviousScratch ); var toNext = tangentDirection(nextBottom, vertexBottom, up, toNextScratch); // Check if tangents are almost opposite - if so, no need to miter. if ( CesiumMath.equalsEpsilon( Cartesian3.dot(toPrevious, toNext), cosine180, CesiumMath.EPSILON5 ) ) { result = Cartesian3.cross(up, toPrevious, result); result = Cartesian3.normalize(result, result); return result; } // Average directions to previous and to next in the plane of Up result = Cartesian3.add(toNext, toPrevious, result); result = Cartesian3.normalize(result, result); // Flip the normal if it isn't pointing roughly bound right (aka if forward is pointing more "backwards") var forward = Cartesian3.cross(up, result, forwardScratch); if (Cartesian3.dot(toNext, forward) < cosine90) { result = Cartesian3.negate(result, result); } return result; } var XZ_PLANE = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Y); var previousBottomScratch = new Cartesian3(); var vertexBottomScratch = new Cartesian3(); var vertexTopScratch = new Cartesian3(); var nextBottomScratch = new Cartesian3(); var vertexNormalScratch = new Cartesian3(); var intersectionScratch$1 = new Cartesian3(); var cartographicScratch0 = new Cartographic(); var cartographicScratch1 = new Cartographic(); var cartographicIntersectionScratch = new Cartographic(); /** * Computes shadow volumes for the ground polyline, consisting of its vertices, indices, and a bounding sphere. * Vertices are "fat," packing all the data needed in each volume to describe a line on terrain or 3D Tiles. * Should not be called independent of {@link GroundPolylinePrimitive}. * * @param {GroundPolylineGeometry} groundPolylineGeometry * @private */ GroundPolylineGeometry.createGeometry = function (groundPolylineGeometry) { var compute2dAttributes = !groundPolylineGeometry._scene3DOnly; var loop = groundPolylineGeometry.loop; var ellipsoid = groundPolylineGeometry._ellipsoid; var granularity = groundPolylineGeometry.granularity; var arcType = groundPolylineGeometry.arcType; var projection = new PROJECTIONS[groundPolylineGeometry._projectionIndex]( ellipsoid ); var minHeight = WALL_INITIAL_MIN_HEIGHT; var maxHeight = WALL_INITIAL_MAX_HEIGHT; var index; var i; var positions = groundPolylineGeometry._positions; var positionsLength = positions.length; if (positionsLength === 2) { loop = false; } // Split positions across the IDL and the Prime Meridian as well. // Split across prime meridian because very large geometries crossing the Prime Meridian but not the IDL // may get split by the plane of IDL + Prime Meridian. var p0; var p1; var c0; var c1; var rhumbLine = new EllipsoidRhumbLine(undefined, undefined, ellipsoid); var intersection; var intersectionCartographic; var intersectionLongitude; var splitPositions = [positions[0]]; for (i = 0; i < positionsLength - 1; i++) { p0 = positions[i]; p1 = positions[i + 1]; intersection = IntersectionTests.lineSegmentPlane( p0, p1, XZ_PLANE, intersectionScratch$1 ); if ( defined(intersection) && !Cartesian3.equalsEpsilon(intersection, p0, CesiumMath.EPSILON7) && !Cartesian3.equalsEpsilon(intersection, p1, CesiumMath.EPSILON7) ) { if (groundPolylineGeometry.arcType === ArcType$1.GEODESIC) { splitPositions.push(Cartesian3.clone(intersection)); } else if (groundPolylineGeometry.arcType === ArcType$1.RHUMB) { intersectionLongitude = ellipsoid.cartesianToCartographic( intersection, cartographicScratch0 ).longitude; c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0); c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1); rhumbLine.setEndPoints(c0, c1); intersectionCartographic = rhumbLine.findIntersectionWithLongitude( intersectionLongitude, cartographicIntersectionScratch ); intersection = ellipsoid.cartographicToCartesian( intersectionCartographic, intersectionScratch$1 ); if ( defined(intersection) && !Cartesian3.equalsEpsilon(intersection, p0, CesiumMath.EPSILON7) && !Cartesian3.equalsEpsilon(intersection, p1, CesiumMath.EPSILON7) ) { splitPositions.push(Cartesian3.clone(intersection)); } } } splitPositions.push(p1); } if (loop) { p0 = positions[positionsLength - 1]; p1 = positions[0]; intersection = IntersectionTests.lineSegmentPlane( p0, p1, XZ_PLANE, intersectionScratch$1 ); if ( defined(intersection) && !Cartesian3.equalsEpsilon(intersection, p0, CesiumMath.EPSILON7) && !Cartesian3.equalsEpsilon(intersection, p1, CesiumMath.EPSILON7) ) { if (groundPolylineGeometry.arcType === ArcType$1.GEODESIC) { splitPositions.push(Cartesian3.clone(intersection)); } else if (groundPolylineGeometry.arcType === ArcType$1.RHUMB) { intersectionLongitude = ellipsoid.cartesianToCartographic( intersection, cartographicScratch0 ).longitude; c0 = ellipsoid.cartesianToCartographic(p0, cartographicScratch0); c1 = ellipsoid.cartesianToCartographic(p1, cartographicScratch1); rhumbLine.setEndPoints(c0, c1); intersectionCartographic = rhumbLine.findIntersectionWithLongitude( intersectionLongitude, cartographicIntersectionScratch ); intersection = ellipsoid.cartographicToCartesian( intersectionCartographic, intersectionScratch$1 ); if ( defined(intersection) && !Cartesian3.equalsEpsilon(intersection, p0, CesiumMath.EPSILON7) && !Cartesian3.equalsEpsilon(intersection, p1, CesiumMath.EPSILON7) ) { splitPositions.push(Cartesian3.clone(intersection)); } } } } var cartographicsLength = splitPositions.length; var cartographics = new Array(cartographicsLength); for (i = 0; i < cartographicsLength; i++) { var cartographic = Cartographic.fromCartesian(splitPositions[i], ellipsoid); cartographic.height = 0.0; cartographics[i] = cartographic; } cartographics = arrayRemoveDuplicates( cartographics, Cartographic.equalsEpsilon ); cartographicsLength = cartographics.length; if (cartographicsLength < 2) { return undefined; } /**** Build heap-side arrays for positions, interpolated cartographics, and normals from which to compute vertices ****/ // We build a "wall" and then decompose it into separately connected component "volumes" because we need a lot // of information about the wall. Also, this simplifies interpolation. // Convention: "next" and "end" are locally forward to each segment of the wall, // and we are computing normals pointing towards the local right side of the vertices in each segment. var cartographicsArray = []; var normalsArray = []; var bottomPositionsArray = []; var topPositionsArray = []; var previousBottom = previousBottomScratch; var vertexBottom = vertexBottomScratch; var vertexTop = vertexTopScratch; var nextBottom = nextBottomScratch; var vertexNormal = vertexNormalScratch; // First point - either loop or attach a "perpendicular" normal var startCartographic = cartographics[0]; var nextCartographic = cartographics[1]; var prestartCartographic = cartographics[cartographicsLength - 1]; previousBottom = getPosition$2( ellipsoid, prestartCartographic, minHeight, previousBottom ); nextBottom = getPosition$2(ellipsoid, nextCartographic, minHeight, nextBottom); vertexBottom = getPosition$2( ellipsoid, startCartographic, minHeight, vertexBottom ); vertexTop = getPosition$2(ellipsoid, startCartographic, maxHeight, vertexTop); if (loop) { vertexNormal = computeVertexMiterNormal( previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal ); } else { vertexNormal = computeRightNormal( startCartographic, nextCartographic, maxHeight, ellipsoid, vertexNormal ); } Cartesian3.pack(vertexNormal, normalsArray, 0); Cartesian3.pack(vertexBottom, bottomPositionsArray, 0); Cartesian3.pack(vertexTop, topPositionsArray, 0); cartographicsArray.push(startCartographic.latitude); cartographicsArray.push(startCartographic.longitude); interpolateSegment( startCartographic, nextCartographic, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray ); // All inbetween points for (i = 1; i < cartographicsLength - 1; ++i) { previousBottom = Cartesian3.clone(vertexBottom, previousBottom); vertexBottom = Cartesian3.clone(nextBottom, vertexBottom); var vertexCartographic = cartographics[i]; getPosition$2(ellipsoid, vertexCartographic, maxHeight, vertexTop); getPosition$2(ellipsoid, cartographics[i + 1], minHeight, nextBottom); computeVertexMiterNormal( previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal ); index = normalsArray.length; Cartesian3.pack(vertexNormal, normalsArray, index); Cartesian3.pack(vertexBottom, bottomPositionsArray, index); Cartesian3.pack(vertexTop, topPositionsArray, index); cartographicsArray.push(vertexCartographic.latitude); cartographicsArray.push(vertexCartographic.longitude); interpolateSegment( cartographics[i], cartographics[i + 1], minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray ); } // Last point - either loop or attach a normal "perpendicular" to the wall. var endCartographic = cartographics[cartographicsLength - 1]; var preEndCartographic = cartographics[cartographicsLength - 2]; vertexBottom = getPosition$2( ellipsoid, endCartographic, minHeight, vertexBottom ); vertexTop = getPosition$2(ellipsoid, endCartographic, maxHeight, vertexTop); if (loop) { var postEndCartographic = cartographics[0]; previousBottom = getPosition$2( ellipsoid, preEndCartographic, minHeight, previousBottom ); nextBottom = getPosition$2( ellipsoid, postEndCartographic, minHeight, nextBottom ); vertexNormal = computeVertexMiterNormal( previousBottom, vertexBottom, vertexTop, nextBottom, vertexNormal ); } else { vertexNormal = computeRightNormal( preEndCartographic, endCartographic, maxHeight, ellipsoid, vertexNormal ); } index = normalsArray.length; Cartesian3.pack(vertexNormal, normalsArray, index); Cartesian3.pack(vertexBottom, bottomPositionsArray, index); Cartesian3.pack(vertexTop, topPositionsArray, index); cartographicsArray.push(endCartographic.latitude); cartographicsArray.push(endCartographic.longitude); if (loop) { interpolateSegment( endCartographic, startCartographic, minHeight, maxHeight, granularity, arcType, ellipsoid, normalsArray, bottomPositionsArray, topPositionsArray, cartographicsArray ); index = normalsArray.length; for (i = 0; i < 3; ++i) { normalsArray[index + i] = normalsArray[i]; bottomPositionsArray[index + i] = bottomPositionsArray[i]; topPositionsArray[index + i] = topPositionsArray[i]; } cartographicsArray.push(startCartographic.latitude); cartographicsArray.push(startCartographic.longitude); } return generateGeometryAttributes( loop, projection, bottomPositionsArray, topPositionsArray, normalsArray, cartographicsArray, compute2dAttributes ); }; // If the end normal angle is too steep compared to the direction of the line segment, // "break" the miter by rotating the normal 90 degrees around the "up" direction at the point // For ultra precision we would want to project into a plane, but in practice this is sufficient. var lineDirectionScratch = new Cartesian3(); var matrix3Scratch$1 = new Matrix3(); var quaternionScratch$1 = new Quaternion(); function breakMiter(endGeometryNormal, startBottom, endBottom, endTop) { var lineDirection = direction(endBottom, startBottom, lineDirectionScratch); var dot = Cartesian3.dot(lineDirection, endGeometryNormal); if (dot > MITER_BREAK_SMALL || dot < MITER_BREAK_LARGE) { var vertexUp = direction(endTop, endBottom, vertexUpScratch); var angle = dot < MITER_BREAK_LARGE ? CesiumMath.PI_OVER_TWO : -CesiumMath.PI_OVER_TWO; var quaternion = Quaternion.fromAxisAngle( vertexUp, angle, quaternionScratch$1 ); var rotationMatrix = Matrix3.fromQuaternion(quaternion, matrix3Scratch$1); Matrix3.multiplyByVector( rotationMatrix, endGeometryNormal, endGeometryNormal ); return true; } return false; } var endPosCartographicScratch = new Cartographic(); var normalStartpointScratch = new Cartesian3(); var normalEndpointScratch = new Cartesian3(); function projectNormal( projection, cartographic, normal, projectedPosition, result ) { var position = Cartographic.toCartesian( cartographic, projection._ellipsoid, normalStartpointScratch ); var normalEndpoint = Cartesian3.add(position, normal, normalEndpointScratch); var flipNormal = false; var ellipsoid = projection._ellipsoid; var normalEndpointCartographic = ellipsoid.cartesianToCartographic( normalEndpoint, endPosCartographicScratch ); // If normal crosses the IDL, go the other way and flip the result. // In practice this almost never happens because the cartographic start // and end points of each segment are "nudged" to be on the same side // of the IDL and slightly away from the IDL. if ( Math.abs(cartographic.longitude - normalEndpointCartographic.longitude) > CesiumMath.PI_OVER_TWO ) { flipNormal = true; normalEndpoint = Cartesian3.subtract( position, normal, normalEndpointScratch ); normalEndpointCartographic = ellipsoid.cartesianToCartographic( normalEndpoint, endPosCartographicScratch ); } normalEndpointCartographic.height = 0.0; var normalEndpointProjected = projection.project( normalEndpointCartographic, result ); result = Cartesian3.subtract( normalEndpointProjected, projectedPosition, result ); result.z = 0.0; result = Cartesian3.normalize(result, result); if (flipNormal) { Cartesian3.negate(result, result); } return result; } var adjustHeightNormalScratch = new Cartesian3(); var adjustHeightOffsetScratch = new Cartesian3(); function adjustHeights( bottom, top, minHeight, maxHeight, adjustHeightBottom, adjustHeightTop ) { // bottom and top should be at WALL_INITIAL_MIN_HEIGHT and WALL_INITIAL_MAX_HEIGHT, respectively var adjustHeightNormal = Cartesian3.subtract( top, bottom, adjustHeightNormalScratch ); Cartesian3.normalize(adjustHeightNormal, adjustHeightNormal); var distanceForBottom = minHeight - WALL_INITIAL_MIN_HEIGHT; var adjustHeightOffset = Cartesian3.multiplyByScalar( adjustHeightNormal, distanceForBottom, adjustHeightOffsetScratch ); Cartesian3.add(bottom, adjustHeightOffset, adjustHeightBottom); var distanceForTop = maxHeight - WALL_INITIAL_MAX_HEIGHT; adjustHeightOffset = Cartesian3.multiplyByScalar( adjustHeightNormal, distanceForTop, adjustHeightOffsetScratch ); Cartesian3.add(top, adjustHeightOffset, adjustHeightTop); } var nudgeDirectionScratch = new Cartesian3(); function nudgeXZ(start, end) { var startToXZdistance = Plane.getPointDistance(XZ_PLANE, start); var endToXZdistance = Plane.getPointDistance(XZ_PLANE, end); var offset = nudgeDirectionScratch; // Larger epsilon than what's used in GeometryPipeline, a centimeter in world space if (CesiumMath.equalsEpsilon(startToXZdistance, 0.0, CesiumMath.EPSILON2)) { offset = direction(end, start, offset); Cartesian3.multiplyByScalar(offset, CesiumMath.EPSILON2, offset); Cartesian3.add(start, offset, start); } else if ( CesiumMath.equalsEpsilon(endToXZdistance, 0.0, CesiumMath.EPSILON2) ) { offset = direction(start, end, offset); Cartesian3.multiplyByScalar(offset, CesiumMath.EPSILON2, offset); Cartesian3.add(end, offset, end); } } // "Nudge" cartographic coordinates so start and end are on the same side of the IDL. // Nudge amounts are tiny, basically just an IDL flip. // Only used for 2D/CV. function nudgeCartographic(start, end) { var absStartLon = Math.abs(start.longitude); var absEndLon = Math.abs(end.longitude); if ( CesiumMath.equalsEpsilon(absStartLon, CesiumMath.PI, CesiumMath.EPSILON11) ) { var endSign = CesiumMath.sign(end.longitude); start.longitude = endSign * (absStartLon - CesiumMath.EPSILON11); return 1; } else if ( CesiumMath.equalsEpsilon(absEndLon, CesiumMath.PI, CesiumMath.EPSILON11) ) { var startSign = CesiumMath.sign(start.longitude); end.longitude = startSign * (absEndLon - CesiumMath.EPSILON11); return 2; } return 0; } var startCartographicScratch$1 = new Cartographic(); var endCartographicScratch$1 = new Cartographic(); var segmentStartTopScratch = new Cartesian3(); var segmentEndTopScratch = new Cartesian3(); var segmentStartBottomScratch = new Cartesian3(); var segmentEndBottomScratch = new Cartesian3(); var segmentStartNormalScratch = new Cartesian3(); var segmentEndNormalScratch = new Cartesian3(); var getHeightCartographics = [startCartographicScratch$1, endCartographicScratch$1]; var getHeightRectangleScratch = new Rectangle(); var adjustHeightStartTopScratch = new Cartesian3(); var adjustHeightEndTopScratch = new Cartesian3(); var adjustHeightStartBottomScratch = new Cartesian3(); var adjustHeightEndBottomScratch = new Cartesian3(); var segmentStart2DScratch = new Cartesian3(); var segmentEnd2DScratch = new Cartesian3(); var segmentStartNormal2DScratch = new Cartesian3(); var segmentEndNormal2DScratch = new Cartesian3(); var offsetScratch$b = new Cartesian3(); var startUpScratch = new Cartesian3(); var endUpScratch = new Cartesian3(); var rightScratch = new Cartesian3(); var startPlaneNormalScratch = new Cartesian3(); var endPlaneNormalScratch = new Cartesian3(); var encodeScratch$1 = new EncodedCartesian3(); var encodeScratch2D = new EncodedCartesian3(); var forwardOffset2DScratch = new Cartesian3(); var right2DScratch = new Cartesian3(); var normalNudgeScratch = new Cartesian3(); var scratchBoundingSpheres = [new BoundingSphere(), new BoundingSphere()]; // Winding order is reversed so each segment's volume is inside-out var REFERENCE_INDICES = [ 0, 2, 1, 0, 3, 2, // right 0, 7, 3, 0, 4, 7, // start 0, 5, 4, 0, 1, 5, // bottom 5, 7, 4, 5, 6, 7, // left 5, 2, 6, 5, 1, 2, // end 3, 6, 2, 3, 7, 6, // top ]; var REFERENCE_INDICES_LENGTH = REFERENCE_INDICES.length; // Decompose the "wall" into a series of shadow volumes. // Each shadow volume's vertices encode a description of the line it contains, // including mitering planes at the end points, a plane along the line itself, // and attributes for computing length-wise texture coordinates. function generateGeometryAttributes( loop, projection, bottomPositionsArray, topPositionsArray, normalsArray, cartographicsArray, compute2dAttributes ) { var i; var index; var ellipsoid = projection._ellipsoid; // Each segment will have 8 vertices var segmentCount = bottomPositionsArray.length / 3 - 1; var vertexCount = segmentCount * 8; var arraySizeVec4 = vertexCount * 4; var indexCount = segmentCount * 36; var indices = vertexCount > 65535 ? new Uint32Array(indexCount) : new Uint16Array(indexCount); var positionsArray = new Float64Array(vertexCount * 3); var startHiAndForwardOffsetX = new Float32Array(arraySizeVec4); var startLoAndForwardOffsetY = new Float32Array(arraySizeVec4); var startNormalAndForwardOffsetZ = new Float32Array(arraySizeVec4); var endNormalAndTextureCoordinateNormalizationX = new Float32Array( arraySizeVec4 ); var rightNormalAndTextureCoordinateNormalizationY = new Float32Array( arraySizeVec4 ); var startHiLo2D; var offsetAndRight2D; var startEndNormals2D; var texcoordNormalization2D; if (compute2dAttributes) { startHiLo2D = new Float32Array(arraySizeVec4); offsetAndRight2D = new Float32Array(arraySizeVec4); startEndNormals2D = new Float32Array(arraySizeVec4); texcoordNormalization2D = new Float32Array(vertexCount * 2); } /*** Compute total lengths for texture coordinate normalization ***/ // 2D var cartographicsLength = cartographicsArray.length / 2; var length2D = 0.0; var startCartographic = startCartographicScratch$1; startCartographic.height = 0.0; var endCartographic = endCartographicScratch$1; endCartographic.height = 0.0; var segmentStartCartesian = segmentStartTopScratch; var segmentEndCartesian = segmentEndTopScratch; if (compute2dAttributes) { index = 0; for (i = 1; i < cartographicsLength; i++) { // Don't clone anything from previous segment b/c possible IDL touch startCartographic.latitude = cartographicsArray[index]; startCartographic.longitude = cartographicsArray[index + 1]; endCartographic.latitude = cartographicsArray[index + 2]; endCartographic.longitude = cartographicsArray[index + 3]; segmentStartCartesian = projection.project( startCartographic, segmentStartCartesian ); segmentEndCartesian = projection.project( endCartographic, segmentEndCartesian ); length2D += Cartesian3.distance( segmentStartCartesian, segmentEndCartesian ); index += 2; } } // 3D var positionsLength = topPositionsArray.length / 3; segmentEndCartesian = Cartesian3.unpack( topPositionsArray, 0, segmentEndCartesian ); var length3D = 0.0; index = 3; for (i = 1; i < positionsLength; i++) { segmentStartCartesian = Cartesian3.clone( segmentEndCartesian, segmentStartCartesian ); segmentEndCartesian = Cartesian3.unpack( topPositionsArray, index, segmentEndCartesian ); length3D += Cartesian3.distance(segmentStartCartesian, segmentEndCartesian); index += 3; } /*** Generate segments ***/ var j; index = 3; var cartographicsIndex = 0; var vec2sWriteIndex = 0; var vec3sWriteIndex = 0; var vec4sWriteIndex = 0; var miterBroken = false; var endBottom = Cartesian3.unpack( bottomPositionsArray, 0, segmentEndBottomScratch ); var endTop = Cartesian3.unpack(topPositionsArray, 0, segmentEndTopScratch); var endGeometryNormal = Cartesian3.unpack( normalsArray, 0, segmentEndNormalScratch ); if (loop) { var preEndBottom = Cartesian3.unpack( bottomPositionsArray, bottomPositionsArray.length - 6, segmentStartBottomScratch ); if (breakMiter(endGeometryNormal, preEndBottom, endBottom, endTop)) { // Miter broken as if for the last point in the loop, needs to be inverted for first point (clone of endBottom) endGeometryNormal = Cartesian3.negate( endGeometryNormal, endGeometryNormal ); } } var lengthSoFar3D = 0.0; var lengthSoFar2D = 0.0; // For translating bounding volume var sumHeights = 0.0; for (i = 0; i < segmentCount; i++) { var startBottom = Cartesian3.clone(endBottom, segmentStartBottomScratch); var startTop = Cartesian3.clone(endTop, segmentStartTopScratch); var startGeometryNormal = Cartesian3.clone( endGeometryNormal, segmentStartNormalScratch ); if (miterBroken) { startGeometryNormal = Cartesian3.negate( startGeometryNormal, startGeometryNormal ); } endBottom = Cartesian3.unpack( bottomPositionsArray, index, segmentEndBottomScratch ); endTop = Cartesian3.unpack(topPositionsArray, index, segmentEndTopScratch); endGeometryNormal = Cartesian3.unpack( normalsArray, index, segmentEndNormalScratch ); miterBroken = breakMiter(endGeometryNormal, startBottom, endBottom, endTop); // 2D - don't clone anything from previous segment b/c possible IDL touch startCartographic.latitude = cartographicsArray[cartographicsIndex]; startCartographic.longitude = cartographicsArray[cartographicsIndex + 1]; endCartographic.latitude = cartographicsArray[cartographicsIndex + 2]; endCartographic.longitude = cartographicsArray[cartographicsIndex + 3]; var start2D; var end2D; var startGeometryNormal2D; var endGeometryNormal2D; if (compute2dAttributes) { var nudgeResult = nudgeCartographic(startCartographic, endCartographic); start2D = projection.project(startCartographic, segmentStart2DScratch); end2D = projection.project(endCartographic, segmentEnd2DScratch); var direction2D = direction(end2D, start2D, forwardOffset2DScratch); direction2D.y = Math.abs(direction2D.y); startGeometryNormal2D = segmentStartNormal2DScratch; endGeometryNormal2D = segmentEndNormal2DScratch; if ( nudgeResult === 0 || Cartesian3.dot(direction2D, Cartesian3.UNIT_Y) > MITER_BREAK_SMALL ) { // No nudge - project the original normal // Or, if the line's angle relative to the IDL is very acute, // in which case snapping will produce oddly shaped volumes. startGeometryNormal2D = projectNormal( projection, startCartographic, startGeometryNormal, start2D, segmentStartNormal2DScratch ); endGeometryNormal2D = projectNormal( projection, endCartographic, endGeometryNormal, end2D, segmentEndNormal2DScratch ); } else if (nudgeResult === 1) { // Start is close to IDL - snap start normal to align with IDL endGeometryNormal2D = projectNormal( projection, endCartographic, endGeometryNormal, end2D, segmentEndNormal2DScratch ); startGeometryNormal2D.x = 0.0; // If start longitude is negative and end longitude is less negative, relative right is unit -Y // If start longitude is positive and end longitude is less positive, relative right is unit +Y startGeometryNormal2D.y = CesiumMath.sign( startCartographic.longitude - Math.abs(endCartographic.longitude) ); startGeometryNormal2D.z = 0.0; } else { // End is close to IDL - snap end normal to align with IDL startGeometryNormal2D = projectNormal( projection, startCartographic, startGeometryNormal, start2D, segmentStartNormal2DScratch ); endGeometryNormal2D.x = 0.0; // If end longitude is negative and start longitude is less negative, relative right is unit Y // If end longitude is positive and start longitude is less positive, relative right is unit -Y endGeometryNormal2D.y = CesiumMath.sign( startCartographic.longitude - endCartographic.longitude ); endGeometryNormal2D.z = 0.0; } } /**************************************** * Geometry descriptors of a "line on terrain," * as opposed to the "shadow volume used to draw * the line on terrain": * - position of start + offset to end * - start, end, and right-facing planes * - encoded texture coordinate offsets ****************************************/ /* 3D */ var segmentLength3D = Cartesian3.distance(startTop, endTop); var encodedStart = EncodedCartesian3.fromCartesian( startBottom, encodeScratch$1 ); var forwardOffset = Cartesian3.subtract( endBottom, startBottom, offsetScratch$b ); var forward = Cartesian3.normalize(forwardOffset, rightScratch); var startUp = Cartesian3.subtract(startTop, startBottom, startUpScratch); startUp = Cartesian3.normalize(startUp, startUp); var rightNormal = Cartesian3.cross(forward, startUp, rightScratch); rightNormal = Cartesian3.normalize(rightNormal, rightNormal); var startPlaneNormal = Cartesian3.cross( startUp, startGeometryNormal, startPlaneNormalScratch ); startPlaneNormal = Cartesian3.normalize(startPlaneNormal, startPlaneNormal); var endUp = Cartesian3.subtract(endTop, endBottom, endUpScratch); endUp = Cartesian3.normalize(endUp, endUp); var endPlaneNormal = Cartesian3.cross( endGeometryNormal, endUp, endPlaneNormalScratch ); endPlaneNormal = Cartesian3.normalize(endPlaneNormal, endPlaneNormal); var texcoordNormalization3DX = segmentLength3D / length3D; var texcoordNormalization3DY = lengthSoFar3D / length3D; /* 2D */ var segmentLength2D = 0.0; var encodedStart2D; var forwardOffset2D; var right2D; var texcoordNormalization2DX = 0.0; var texcoordNormalization2DY = 0.0; if (compute2dAttributes) { segmentLength2D = Cartesian3.distance(start2D, end2D); encodedStart2D = EncodedCartesian3.fromCartesian( start2D, encodeScratch2D ); forwardOffset2D = Cartesian3.subtract( end2D, start2D, forwardOffset2DScratch ); // Right direction is just forward direction rotated by -90 degrees around Z // Similarly with plane normals right2D = Cartesian3.normalize(forwardOffset2D, right2DScratch); var swap = right2D.x; right2D.x = right2D.y; right2D.y = -swap; texcoordNormalization2DX = segmentLength2D / length2D; texcoordNormalization2DY = lengthSoFar2D / length2D; } /** Pack **/ for (j = 0; j < 8; j++) { var vec4Index = vec4sWriteIndex + j * 4; var vec2Index = vec2sWriteIndex + j * 2; var wIndex = vec4Index + 3; // Encode sidedness of vertex relative to right plane in texture coordinate normalization X, // whether vertex is top or bottom of volume in sign/magnitude of normalization Y. var rightPlaneSide = j < 4 ? 1.0 : -1.0; var topBottomSide = j === 2 || j === 3 || j === 6 || j === 7 ? 1.0 : -1.0; // 3D Cartesian3.pack(encodedStart.high, startHiAndForwardOffsetX, vec4Index); startHiAndForwardOffsetX[wIndex] = forwardOffset.x; Cartesian3.pack(encodedStart.low, startLoAndForwardOffsetY, vec4Index); startLoAndForwardOffsetY[wIndex] = forwardOffset.y; Cartesian3.pack( startPlaneNormal, startNormalAndForwardOffsetZ, vec4Index ); startNormalAndForwardOffsetZ[wIndex] = forwardOffset.z; Cartesian3.pack( endPlaneNormal, endNormalAndTextureCoordinateNormalizationX, vec4Index ); endNormalAndTextureCoordinateNormalizationX[wIndex] = texcoordNormalization3DX * rightPlaneSide; Cartesian3.pack( rightNormal, rightNormalAndTextureCoordinateNormalizationY, vec4Index ); var texcoordNormalization = texcoordNormalization3DY * topBottomSide; if (texcoordNormalization === 0.0 && topBottomSide < 0.0) { texcoordNormalization = 9.0; // some value greater than 1.0 } rightNormalAndTextureCoordinateNormalizationY[ wIndex ] = texcoordNormalization; // 2D if (compute2dAttributes) { startHiLo2D[vec4Index] = encodedStart2D.high.x; startHiLo2D[vec4Index + 1] = encodedStart2D.high.y; startHiLo2D[vec4Index + 2] = encodedStart2D.low.x; startHiLo2D[vec4Index + 3] = encodedStart2D.low.y; startEndNormals2D[vec4Index] = -startGeometryNormal2D.y; startEndNormals2D[vec4Index + 1] = startGeometryNormal2D.x; startEndNormals2D[vec4Index + 2] = endGeometryNormal2D.y; startEndNormals2D[vec4Index + 3] = -endGeometryNormal2D.x; offsetAndRight2D[vec4Index] = forwardOffset2D.x; offsetAndRight2D[vec4Index + 1] = forwardOffset2D.y; offsetAndRight2D[vec4Index + 2] = right2D.x; offsetAndRight2D[vec4Index + 3] = right2D.y; texcoordNormalization2D[vec2Index] = texcoordNormalization2DX * rightPlaneSide; texcoordNormalization = texcoordNormalization2DY * topBottomSide; if (texcoordNormalization === 0.0 && topBottomSide < 0.0) { texcoordNormalization = 9.0; // some value greater than 1.0 } texcoordNormalization2D[vec2Index + 1] = texcoordNormalization; } } // Adjust height of volume in 3D var adjustHeightStartBottom = adjustHeightStartBottomScratch; var adjustHeightEndBottom = adjustHeightEndBottomScratch; var adjustHeightStartTop = adjustHeightStartTopScratch; var adjustHeightEndTop = adjustHeightEndTopScratch; var getHeightsRectangle = Rectangle.fromCartographicArray( getHeightCartographics, getHeightRectangleScratch ); var minMaxHeights = ApproximateTerrainHeights.getMinimumMaximumHeights( getHeightsRectangle, ellipsoid ); var minHeight = minMaxHeights.minimumTerrainHeight; var maxHeight = minMaxHeights.maximumTerrainHeight; sumHeights += minHeight; sumHeights += maxHeight; adjustHeights( startBottom, startTop, minHeight, maxHeight, adjustHeightStartBottom, adjustHeightStartTop ); adjustHeights( endBottom, endTop, minHeight, maxHeight, adjustHeightEndBottom, adjustHeightEndTop ); // Nudge the positions away from the "polyline" a little bit to prevent errors in GeometryPipeline var normalNudge = Cartesian3.multiplyByScalar( rightNormal, CesiumMath.EPSILON5, normalNudgeScratch ); Cartesian3.add( adjustHeightStartBottom, normalNudge, adjustHeightStartBottom ); Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom); Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop); Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop); // If the segment is very close to the XZ plane, nudge the vertices slightly to avoid touching it. nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom); nudgeXZ(adjustHeightStartTop, adjustHeightEndTop); Cartesian3.pack(adjustHeightStartBottom, positionsArray, vec3sWriteIndex); Cartesian3.pack(adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 3); Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 6); Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 9); normalNudge = Cartesian3.multiplyByScalar( rightNormal, -2.0 * CesiumMath.EPSILON5, normalNudgeScratch ); Cartesian3.add( adjustHeightStartBottom, normalNudge, adjustHeightStartBottom ); Cartesian3.add(adjustHeightEndBottom, normalNudge, adjustHeightEndBottom); Cartesian3.add(adjustHeightStartTop, normalNudge, adjustHeightStartTop); Cartesian3.add(adjustHeightEndTop, normalNudge, adjustHeightEndTop); nudgeXZ(adjustHeightStartBottom, adjustHeightEndBottom); nudgeXZ(adjustHeightStartTop, adjustHeightEndTop); Cartesian3.pack( adjustHeightStartBottom, positionsArray, vec3sWriteIndex + 12 ); Cartesian3.pack( adjustHeightEndBottom, positionsArray, vec3sWriteIndex + 15 ); Cartesian3.pack(adjustHeightEndTop, positionsArray, vec3sWriteIndex + 18); Cartesian3.pack(adjustHeightStartTop, positionsArray, vec3sWriteIndex + 21); cartographicsIndex += 2; index += 3; vec2sWriteIndex += 16; vec3sWriteIndex += 24; vec4sWriteIndex += 32; lengthSoFar3D += segmentLength3D; lengthSoFar2D += segmentLength2D; } index = 0; var indexOffset = 0; for (i = 0; i < segmentCount; i++) { for (j = 0; j < REFERENCE_INDICES_LENGTH; j++) { indices[index + j] = REFERENCE_INDICES[j] + indexOffset; } indexOffset += 8; index += REFERENCE_INDICES_LENGTH; } var boundingSpheres = scratchBoundingSpheres; BoundingSphere.fromVertices( bottomPositionsArray, Cartesian3.ZERO, 3, boundingSpheres[0] ); BoundingSphere.fromVertices( topPositionsArray, Cartesian3.ZERO, 3, boundingSpheres[1] ); var boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres); // Adjust bounding sphere height and radius to cover more of the volume boundingSphere.radius += sumHeights / (segmentCount * 2.0); var attributes = { position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, normalize: false, values: positionsArray, }), startHiAndForwardOffsetX: getVec4GeometryAttribute( startHiAndForwardOffsetX ), startLoAndForwardOffsetY: getVec4GeometryAttribute( startLoAndForwardOffsetY ), startNormalAndForwardOffsetZ: getVec4GeometryAttribute( startNormalAndForwardOffsetZ ), endNormalAndTextureCoordinateNormalizationX: getVec4GeometryAttribute( endNormalAndTextureCoordinateNormalizationX ), rightNormalAndTextureCoordinateNormalizationY: getVec4GeometryAttribute( rightNormalAndTextureCoordinateNormalizationY ), }; if (compute2dAttributes) { attributes.startHiLo2D = getVec4GeometryAttribute(startHiLo2D); attributes.offsetAndRight2D = getVec4GeometryAttribute(offsetAndRight2D); attributes.startEndNormals2D = getVec4GeometryAttribute(startEndNormals2D); attributes.texcoordNormalization2D = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, normalize: false, values: texcoordNormalization2D, }); } return new Geometry({ attributes: attributes, indices: indices, boundingSphere: boundingSphere, }); } function getVec4GeometryAttribute(typedArray) { return new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, values: typedArray, }); } /** * Approximates an ellipsoid-tangent vector in 2D by projecting the end point into 2D. * Exposed for testing. * * @param {MapProjection} projection Map Projection for projecting coordinates to 2D. * @param {Cartographic} cartographic The cartographic origin point of the normal. * Used to check if the normal crosses the IDL during projection. * @param {Cartesian3} normal The normal in 3D. * @param {Cartesian3} projectedPosition The projected origin point of the normal in 2D. * @param {Cartesian3} result Result parameter on which to store the projected normal. * @private */ GroundPolylineGeometry._projectNormal = projectNormal; /** * Defines a heading angle, pitch angle, and range in a local frame. * Heading is the rotation from the local north direction where a positive angle is increasing eastward. * Pitch is the rotation from the local xy-plane. Positive pitch angles are above the plane. Negative pitch * angles are below the plane. Range is the distance from the center of the frame. * @alias HeadingPitchRange * @constructor * * @param {Number} [heading=0.0] The heading angle in radians. * @param {Number} [pitch=0.0] The pitch angle in radians. * @param {Number} [range=0.0] The distance from the center in meters. */ function HeadingPitchRange(heading, pitch, range) { /** * Heading is the rotation from the local north direction where a positive angle is increasing eastward. * @type {Number} * @default 0.0 */ this.heading = defaultValue(heading, 0.0); /** * Pitch is the rotation from the local xy-plane. Positive pitch angles * are above the plane. Negative pitch angles are below the plane. * @type {Number} * @default 0.0 */ this.pitch = defaultValue(pitch, 0.0); /** * Range is the distance from the center of the local frame. * @type {Number} * @default 0.0 */ this.range = defaultValue(range, 0.0); } /** * Duplicates a HeadingPitchRange instance. * * @param {HeadingPitchRange} hpr The HeadingPitchRange to duplicate. * @param {HeadingPitchRange} [result] The object onto which to store the result. * @returns {HeadingPitchRange} The modified result parameter or a new HeadingPitchRange instance if one was not provided. (Returns undefined if hpr is undefined) */ HeadingPitchRange.clone = function (hpr, result) { if (!defined(hpr)) { return undefined; } if (!defined(result)) { result = new HeadingPitchRange(); } result.heading = hpr.heading; result.pitch = hpr.pitch; result.range = hpr.range; return result; }; var factorial = CesiumMath.factorial; function calculateCoefficientTerm( x, zIndices, xTable, derivOrder, termOrder, reservedIndices ) { var result = 0; var reserved; var i; var j; if (derivOrder > 0) { for (i = 0; i < termOrder; i++) { reserved = false; for (j = 0; j < reservedIndices.length && !reserved; j++) { if (i === reservedIndices[j]) { reserved = true; } } if (!reserved) { reservedIndices.push(i); result += calculateCoefficientTerm( x, zIndices, xTable, derivOrder - 1, termOrder, reservedIndices ); reservedIndices.splice(reservedIndices.length - 1, 1); } } return result; } result = 1; for (i = 0; i < termOrder; i++) { reserved = false; for (j = 0; j < reservedIndices.length && !reserved; j++) { if (i === reservedIndices[j]) { reserved = true; } } if (!reserved) { result *= x - xTable[zIndices[i]]; } } return result; } /** * An {@link InterpolationAlgorithm} for performing Hermite interpolation. * * @namespace HermitePolynomialApproximation */ var HermitePolynomialApproximation = { type: "Hermite", }; /** * Given the desired degree, returns the number of data points required for interpolation. * * @param {Number} degree The desired degree of interpolation. * @param {Number} [inputOrder=0] The order of the inputs (0 means just the data, 1 means the data and its derivative, etc). * @returns {Number} The number of required data points needed for the desired degree of interpolation. * @exception {DeveloperError} degree must be 0 or greater. * @exception {DeveloperError} inputOrder must be 0 or greater. */ HermitePolynomialApproximation.getRequiredDataPoints = function ( degree, inputOrder ) { inputOrder = defaultValue(inputOrder, 0); //>>includeStart('debug', pragmas.debug); if (!defined(degree)) { throw new DeveloperError("degree is required."); } if (degree < 0) { throw new DeveloperError("degree must be 0 or greater."); } if (inputOrder < 0) { throw new DeveloperError("inputOrder must be 0 or greater."); } //>>includeEnd('debug'); return Math.max(Math.floor((degree + 1) / (inputOrder + 1)), 2); }; /** * Interpolates values using Hermite Polynomial Approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number[]} [result] An existing array into which to store the result. * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ HermitePolynomialApproximation.interpolateOrderZero = function ( x, xTable, yTable, yStride, result ) { if (!defined(result)) { result = new Array(yStride); } var i; var j; var d; var s; var len; var index; var length = xTable.length; var coefficients = new Array(yStride); for (i = 0; i < yStride; i++) { result[i] = 0; var l = new Array(length); coefficients[i] = l; for (j = 0; j < length; j++) { l[j] = []; } } var zIndicesLength = length, zIndices = new Array(zIndicesLength); for (i = 0; i < zIndicesLength; i++) { zIndices[i] = i; } var highestNonZeroCoef = length - 1; for (s = 0; s < yStride; s++) { for (j = 0; j < zIndicesLength; j++) { index = zIndices[j] * yStride + s; coefficients[s][0].push(yTable[index]); } for (i = 1; i < zIndicesLength; i++) { var nonZeroCoefficients = false; for (j = 0; j < zIndicesLength - i; j++) { var zj = xTable[zIndices[j]]; var zn = xTable[zIndices[j + i]]; var numerator; if (zn - zj <= 0) { index = zIndices[j] * yStride + yStride * i + s; numerator = yTable[index]; coefficients[s][i].push(numerator / factorial(i)); } else { numerator = coefficients[s][i - 1][j + 1] - coefficients[s][i - 1][j]; coefficients[s][i].push(numerator / (zn - zj)); } nonZeroCoefficients = nonZeroCoefficients || numerator !== 0; } if (!nonZeroCoefficients) { highestNonZeroCoef = i - 1; } } } for (d = 0, len = 0; d <= len; d++) { for (i = d; i <= highestNonZeroCoef; i++) { var tempTerm = calculateCoefficientTerm(x, zIndices, xTable, d, i, []); for (s = 0; s < yStride; s++) { var coeff = coefficients[s][i][0]; result[s + d * yStride] += coeff * tempTerm; } } } return result; }; var arrayScratch = []; /** * Interpolates values using Hermite Polynomial Approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number} inputOrder The number of derivatives supplied for input. * @param {Number} outputOrder The number of derivatives desired for output. * @param {Number[]} [result] An existing array into which to store the result. * * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ HermitePolynomialApproximation.interpolate = function ( x, xTable, yTable, yStride, inputOrder, outputOrder, result ) { var resultLength = yStride * (outputOrder + 1); if (!defined(result)) { result = new Array(resultLength); } for (var r = 0; r < resultLength; r++) { result[r] = 0; } var length = xTable.length; // The zIndices array holds copies of the addresses of the xTable values // in the range we're looking at. Even though this just holds information already // available in xTable this is a much more convenient format. var zIndices = new Array(length * (inputOrder + 1)); var i; for (i = 0; i < length; i++) { for (var j = 0; j < inputOrder + 1; j++) { zIndices[i * (inputOrder + 1) + j] = i; } } var zIndiceslength = zIndices.length; var coefficients = arrayScratch; var highestNonZeroCoef = fillCoefficientList( coefficients, zIndices, xTable, yTable, yStride, inputOrder ); var reservedIndices = []; var tmp = (zIndiceslength * (zIndiceslength + 1)) / 2; var loopStop = Math.min(highestNonZeroCoef, outputOrder); for (var d = 0; d <= loopStop; d++) { for (i = d; i <= highestNonZeroCoef; i++) { reservedIndices.length = 0; var tempTerm = calculateCoefficientTerm( x, zIndices, xTable, d, i, reservedIndices ); var dimTwo = Math.floor((i * (1 - i)) / 2) + zIndiceslength * i; for (var s = 0; s < yStride; s++) { var dimOne = Math.floor(s * tmp); var coef = coefficients[dimOne + dimTwo]; result[s + d * yStride] += coef * tempTerm; } } } return result; }; function fillCoefficientList( coefficients, zIndices, xTable, yTable, yStride, inputOrder ) { var j; var index; var highestNonZero = -1; var zIndiceslength = zIndices.length; var tmp = (zIndiceslength * (zIndiceslength + 1)) / 2; for (var s = 0; s < yStride; s++) { var dimOne = Math.floor(s * tmp); for (j = 0; j < zIndiceslength; j++) { index = zIndices[j] * yStride * (inputOrder + 1) + s; coefficients[dimOne + j] = yTable[index]; } for (var i = 1; i < zIndiceslength; i++) { var coefIndex = 0; var dimTwo = Math.floor((i * (1 - i)) / 2) + zIndiceslength * i; var nonZeroCoefficients = false; for (j = 0; j < zIndiceslength - i; j++) { var zj = xTable[zIndices[j]]; var zn = xTable[zIndices[j + i]]; var numerator; var coefficient; if (zn - zj <= 0) { index = zIndices[j] * yStride * (inputOrder + 1) + yStride * i + s; numerator = yTable[index]; coefficient = numerator / CesiumMath.factorial(i); coefficients[dimOne + dimTwo + coefIndex] = coefficient; coefIndex++; } else { var dimTwoMinusOne = Math.floor(((i - 1) * (2 - i)) / 2) + zIndiceslength * (i - 1); numerator = coefficients[dimOne + dimTwoMinusOne + j + 1] - coefficients[dimOne + dimTwoMinusOne + j]; coefficient = numerator / (zn - zj); coefficients[dimOne + dimTwo + coefIndex] = coefficient; coefIndex++; } nonZeroCoefficients = nonZeroCoefficients || numerator !== 0.0; } if (nonZeroCoefficients) { highestNonZero = Math.max(highestNonZero, i); } } } return highestNonZero; } /** * A structure containing the orientation data computed at a particular time. The data * represents the direction of the pole of rotation and the rotation about that pole. *

* These parameters correspond to the parameters in the Report from the IAU/IAG Working Group * except that they are expressed in radians. *

* * @namespace IauOrientationParameters * * @private */ function IauOrientationParameters( rightAscension, declination, rotation, rotationRate ) { /** * The right ascension of the north pole of the body with respect to * the International Celestial Reference Frame, in radians. * @type {Number} * * @private */ this.rightAscension = rightAscension; /** * The declination of the north pole of the body with respect to * the International Celestial Reference Frame, in radians. * @type {Number} * * @private */ this.declination = declination; /** * The rotation about the north pole used to align a set of axes with * the meridian defined by the IAU report, in radians. * @type {Number} * * @private */ this.rotation = rotation; /** * The instantaneous rotation rate about the north pole, in radians per second. * @type {Number} * * @private */ this.rotationRate = rotationRate; } /** * This is a collection of the orientation information available for central bodies. * The data comes from the Report of the IAU/IAG Working Group on Cartographic * Coordinates and Rotational Elements: 2000. * * @namespace Iau2000Orientation * * @private */ var Iau2000Orientation = {}; var TdtMinusTai$1 = 32.184; var J2000d$1 = 2451545.0; var c1 = -0.0529921; var c2 = -0.1059842; var c3 = 13.0120009; var c4 = 13.3407154; var c5 = 0.9856003; var c6 = 26.4057084; var c7 = 13.064993; var c8 = 0.3287146; var c9 = 1.7484877; var c10 = -0.1589763; var c11 = 0.0036096; var c12 = 0.1643573; var c13 = 12.9590088; var dateTT = new JulianDate(); /** * Compute the orientation parameters for the Moon. * * @param {JulianDate} [date=JulianDate.now()] The date to evaluate the parameters. * @param {IauOrientationParameters} [result] The object onto which to store the result. * @returns {IauOrientationParameters} The modified result parameter or a new instance representing the orientation of the Earth's Moon. * @private */ Iau2000Orientation.ComputeMoon = function (date, result) { if (!defined(date)) { date = JulianDate.now(); } dateTT = JulianDate.addSeconds(date, TdtMinusTai$1, dateTT); var d = JulianDate.totalDays(dateTT) - J2000d$1; var T = d / TimeConstants$1.DAYS_PER_JULIAN_CENTURY; var E1 = (125.045 + c1 * d) * CesiumMath.RADIANS_PER_DEGREE; var E2 = (250.089 + c2 * d) * CesiumMath.RADIANS_PER_DEGREE; var E3 = (260.008 + c3 * d) * CesiumMath.RADIANS_PER_DEGREE; var E4 = (176.625 + c4 * d) * CesiumMath.RADIANS_PER_DEGREE; var E5 = (357.529 + c5 * d) * CesiumMath.RADIANS_PER_DEGREE; var E6 = (311.589 + c6 * d) * CesiumMath.RADIANS_PER_DEGREE; var E7 = (134.963 + c7 * d) * CesiumMath.RADIANS_PER_DEGREE; var E8 = (276.617 + c8 * d) * CesiumMath.RADIANS_PER_DEGREE; var E9 = (34.226 + c9 * d) * CesiumMath.RADIANS_PER_DEGREE; var E10 = (15.134 + c10 * d) * CesiumMath.RADIANS_PER_DEGREE; var E11 = (119.743 + c11 * d) * CesiumMath.RADIANS_PER_DEGREE; var E12 = (239.961 + c12 * d) * CesiumMath.RADIANS_PER_DEGREE; var E13 = (25.053 + c13 * d) * CesiumMath.RADIANS_PER_DEGREE; var sinE1 = Math.sin(E1); var sinE2 = Math.sin(E2); var sinE3 = Math.sin(E3); var sinE4 = Math.sin(E4); var sinE5 = Math.sin(E5); var sinE6 = Math.sin(E6); var sinE7 = Math.sin(E7); var sinE8 = Math.sin(E8); var sinE9 = Math.sin(E9); var sinE10 = Math.sin(E10); var sinE11 = Math.sin(E11); var sinE12 = Math.sin(E12); var sinE13 = Math.sin(E13); var cosE1 = Math.cos(E1); var cosE2 = Math.cos(E2); var cosE3 = Math.cos(E3); var cosE4 = Math.cos(E4); var cosE5 = Math.cos(E5); var cosE6 = Math.cos(E6); var cosE7 = Math.cos(E7); var cosE8 = Math.cos(E8); var cosE9 = Math.cos(E9); var cosE10 = Math.cos(E10); var cosE11 = Math.cos(E11); var cosE12 = Math.cos(E12); var cosE13 = Math.cos(E13); var rightAscension = (269.9949 + 0.0031 * T - 3.8787 * sinE1 - 0.1204 * sinE2 + 0.07 * sinE3 - 0.0172 * sinE4 + 0.0072 * sinE6 - 0.0052 * sinE10 + 0.0043 * sinE13) * CesiumMath.RADIANS_PER_DEGREE; var declination = (66.5392 + 0.013 * T + 1.5419 * cosE1 + 0.0239 * cosE2 - 0.0278 * cosE3 + 0.0068 * cosE4 - 0.0029 * cosE6 + 0.0009 * cosE7 + 0.0008 * cosE10 - 0.0009 * cosE13) * CesiumMath.RADIANS_PER_DEGREE; var rotation = (38.3213 + 13.17635815 * d - 1.4e-12 * d * d + 3.561 * sinE1 + 0.1208 * sinE2 - 0.0642 * sinE3 + 0.0158 * sinE4 + 0.0252 * sinE5 - 0.0066 * sinE6 - 0.0047 * sinE7 - 0.0046 * sinE8 + 0.0028 * sinE9 + 0.0052 * sinE10 + 0.004 * sinE11 + 0.0019 * sinE12 - 0.0044 * sinE13) * CesiumMath.RADIANS_PER_DEGREE; var rotationRate = ((13.17635815 - 1.4e-12 * (2.0 * d) + 3.561 * cosE1 * c1 + 0.1208 * cosE2 * c2 - 0.0642 * cosE3 * c3 + 0.0158 * cosE4 * c4 + 0.0252 * cosE5 * c5 - 0.0066 * cosE6 * c6 - 0.0047 * cosE7 * c7 - 0.0046 * cosE8 * c8 + 0.0028 * cosE9 * c9 + 0.0052 * cosE10 * c10 + 0.004 * cosE11 * c11 + 0.0019 * cosE12 * c12 - 0.0044 * cosE13 * c13) / 86400.0) * CesiumMath.RADIANS_PER_DEGREE; if (!defined(result)) { result = new IauOrientationParameters(); } result.rightAscension = rightAscension; result.declination = declination; result.rotation = rotation; result.rotationRate = rotationRate; return result; }; /** * The Axes representing the orientation of a Globe as represented by the data * from the IAU/IAG Working Group reports on rotational elements. * @alias IauOrientationAxes * @constructor * * @param {IauOrientationAxes.ComputeFunction} [computeFunction] The function that computes the {@link IauOrientationParameters} given a {@link JulianDate}. * * @see Iau2000Orientation * * @private */ function IauOrientationAxes(computeFunction) { if (!defined(computeFunction) || typeof computeFunction !== "function") { computeFunction = Iau2000Orientation.ComputeMoon; } this._computeFunction = computeFunction; } var xAxisScratch = new Cartesian3(); var yAxisScratch = new Cartesian3(); var zAxisScratch = new Cartesian3(); function computeRotationMatrix(alpha, delta, result) { var xAxis = xAxisScratch; xAxis.x = Math.cos(alpha + CesiumMath.PI_OVER_TWO); xAxis.y = Math.sin(alpha + CesiumMath.PI_OVER_TWO); xAxis.z = 0.0; var cosDec = Math.cos(delta); var zAxis = zAxisScratch; zAxis.x = cosDec * Math.cos(alpha); zAxis.y = cosDec * Math.sin(alpha); zAxis.z = Math.sin(delta); var yAxis = Cartesian3.cross(zAxis, xAxis, yAxisScratch); if (!defined(result)) { result = new Matrix3(); } result[0] = xAxis.x; result[1] = yAxis.x; result[2] = zAxis.x; result[3] = xAxis.y; result[4] = yAxis.y; result[5] = zAxis.y; result[6] = xAxis.z; result[7] = yAxis.z; result[8] = zAxis.z; return result; } var rotMtxScratch = new Matrix3(); var quatScratch = new Quaternion(); /** * Computes a rotation from ICRF to a Globe's Fixed axes. * * @param {JulianDate} date The date to evaluate the matrix. * @param {Matrix3} result The object onto which to store the result. * @returns {Matrix3} The modified result parameter or a new instance of the rotation from ICRF to Fixed. */ IauOrientationAxes.prototype.evaluate = function (date, result) { if (!defined(date)) { date = JulianDate.now(); } var alphaDeltaW = this._computeFunction(date); var precMtx = computeRotationMatrix( alphaDeltaW.rightAscension, alphaDeltaW.declination, result ); var rot = CesiumMath.zeroToTwoPi(alphaDeltaW.rotation); var quat = Quaternion.fromAxisAngle(Cartesian3.UNIT_Z, rot, quatScratch); var rotMtx = Matrix3.fromQuaternion( Quaternion.conjugate(quat, quat), rotMtxScratch ); var cbi2cbf = Matrix3.multiply(rotMtx, precMtx, precMtx); return cbi2cbf; }; /** * The interface for interpolation algorithms. * * @interface InterpolationAlgorithm * * @see LagrangePolynomialApproximation * @see LinearApproximation * @see HermitePolynomialApproximation */ var InterpolationAlgorithm = {}; /** * Gets the name of this interpolation algorithm. * @type {String} */ InterpolationAlgorithm.type = undefined; /** * Given the desired degree, returns the number of data points required for interpolation. * @function * * @param {Number} degree The desired degree of interpolation. * @returns {Number} The number of required data points needed for the desired degree of interpolation. */ InterpolationAlgorithm.getRequiredDataPoints = DeveloperError.throwInstantiationError; /** * Performs zero order interpolation. * @function * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number[]} [result] An existing array into which to store the result. * * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ InterpolationAlgorithm.interpolateOrderZero = DeveloperError.throwInstantiationError; /** * Performs higher order interpolation. Not all interpolators need to support high-order interpolation, * if this function remains undefined on implementing objects, interpolateOrderZero will be used instead. * @function * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number} inputOrder The number of derivatives supplied for input. * @param {Number} outputOrder The number of derivatives desired for output. * @param {Number[]} [result] An existing array into which to store the result. * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ InterpolationAlgorithm.interpolate = DeveloperError.throwInstantiationError; var defaultTokenCredit; var defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4NTVhMjg4My1iNGI2LTRhNzgtOTQ0My1mYjA4MTNjMTJiYjQiLCJpZCI6MjU5LCJpYXQiOjE2MTQ1ODE4MDB9.FeIKU_q_xmSAPeRgrOYaJAWmHFGIvy6i-p_Zk9mweEw"; /** * Default settings for accessing the Cesium ion API. * * An ion access token is only required if you are using any ion related APIs. * A default access token is provided for evaluation purposes only. * Sign up for a free ion account and get your own access token at {@link https://cesium.com} * * @see IonResource * @see IonImageryProvider * @see IonGeocoderService * @see createWorldImagery * @see createWorldTerrain * @namespace Ion */ var Ion = {}; /** * Gets or sets the default Cesium ion access token. * * @type {String} */ Ion.defaultAccessToken = defaultAccessToken; /** * Gets or sets the default Cesium ion server. * * @type {String|Resource} * @default https://api.cesium.com */ Ion.defaultServer = new Resource({ url: "https://api.cesium.com/" }); Ion.getDefaultTokenCredit = function (providedKey) { if (providedKey !== defaultAccessToken) { return undefined; } if (!defined(defaultTokenCredit)) { var defaultTokenMessage = ' \ This application is using Cesium\'s default ion access token. Please assign Cesium.Ion.defaultAccessToken \ with an access token from your ion account before making any Cesium API calls. \ You can sign up for a free ion account at https://cesium.com.'; defaultTokenCredit = new Credit(defaultTokenMessage, true); } return defaultTokenCredit; }; /** * Provides geocoding via a {@link https://pelias.io/|Pelias} server. * @alias PeliasGeocoderService * @constructor * * @param {Resource|String} url The endpoint to the Pelias server. * * @example * // Configure a Viewer to use the Pelias server hosted by https://geocode.earth/ * var viewer = new Cesium.Viewer('cesiumContainer', { * geocoder: new Cesium.PeliasGeocoderService(new Cesium.Resource({ * url: 'https://api.geocode.earth/v1/', * queryParameters: { * api_key: '' * } * })) * }); */ function PeliasGeocoderService(url) { //>>includeStart('debug', pragmas.debug); Check.defined("url", url); //>>includeEnd('debug'); this._url = Resource.createIfNeeded(url); this._url.appendForwardSlash(); } Object.defineProperties(PeliasGeocoderService.prototype, { /** * The Resource used to access the Pelias endpoint. * @type {Resource} * @memberof PeliasGeocoderService.prototype * @readonly */ url: { get: function () { return this._url; }, }, }); /** * @function * * @param {String} query The query to be sent to the geocoder service * @param {GeocodeType} [type=GeocodeType.SEARCH] The type of geocode to perform. * @returns {Promise} */ PeliasGeocoderService.prototype.geocode = function (query, type) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("query", query); //>>includeEnd('debug'); var resource = this._url.getDerivedResource({ url: type === GeocodeType$1.AUTOCOMPLETE ? "autocomplete" : "search", queryParameters: { text: query, }, }); return resource.fetchJson().then(function (results) { return results.features.map(function (resultObject) { var destination; var bboxDegrees = resultObject.bbox; if (defined(bboxDegrees)) { destination = Rectangle.fromDegrees( bboxDegrees[0], bboxDegrees[1], bboxDegrees[2], bboxDegrees[3] ); } else { var lon = resultObject.geometry.coordinates[0]; var lat = resultObject.geometry.coordinates[1]; destination = Cartesian3.fromDegrees(lon, lat); } return { displayName: resultObject.properties.label, destination: destination, }; }); }); }; /** * Provides geocoding through Cesium ion. * @alias IonGeocoderService * @constructor * * @param {Object} options Object with the following properties: * @param {Scene} options.scene The scene * @param {String} [options.accessToken=Ion.defaultAccessToken] The access token to use. * @param {String|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server. * * @see Ion */ function IonGeocoderService(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.scene", options.scene); //>>includeEnd('debug'); var accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); var server = Resource.createIfNeeded( defaultValue(options.server, Ion.defaultServer) ); server.appendForwardSlash(); var defaultTokenCredit = Ion.getDefaultTokenCredit(accessToken); if (defined(defaultTokenCredit)) { options.scene.frameState.creditDisplay.addDefaultCredit( Credit.clone(defaultTokenCredit) ); } var searchEndpoint = server.getDerivedResource({ url: "v1/geocode", }); if (defined(accessToken)) { searchEndpoint.appendQueryParameters({ access_token: accessToken }); } this._accessToken = accessToken; this._server = server; this._pelias = new PeliasGeocoderService(searchEndpoint); } /** * @function * * @param {String} query The query to be sent to the geocoder service * @param {GeocodeType} [type=GeocodeType.SEARCH] The type of geocode to perform. * @returns {Promise} */ IonGeocoderService.prototype.geocode = function (query, geocodeType) { return this._pelias.geocode(query, geocodeType); }; /** * A {@link Resource} instance that encapsulates Cesium ion asset access. * This object is normally not instantiated directly, use {@link IonResource.fromAssetId}. * * @alias IonResource * @constructor * @augments Resource * * @param {Object} endpoint The result of the Cesium ion asset endpoint service. * @param {Resource} endpointResource The resource used to retreive the endpoint. * * @see Ion * @see IonImageryProvider * @see createWorldTerrain * @see https://cesium.com */ function IonResource(endpoint, endpointResource) { //>>includeStart('debug', pragmas.debug); Check.defined("endpoint", endpoint); Check.defined("endpointResource", endpointResource); //>>includeEnd('debug'); var options; var externalType = endpoint.externalType; var isExternal = defined(externalType); if (!isExternal) { options = { url: endpoint.url, retryAttempts: 1, retryCallback: retryCallback, }; } else if ( externalType === "3DTILES" || externalType === "STK_TERRAIN_SERVER" ) { // 3D Tiles and STK Terrain Server external assets can still be represented as an IonResource options = { url: endpoint.options.url }; } else { //External imagery assets have additional configuration that can't be represented as a Resource throw new RuntimeError( "Ion.createResource does not support external imagery assets; use IonImageryProvider instead." ); } Resource.call(this, options); // The asset endpoint data returned from ion. this._ionEndpoint = endpoint; this._ionEndpointDomain = isExternal ? undefined : new URI(endpoint.url).authority; // The endpoint resource to fetch when a new token is needed this._ionEndpointResource = endpointResource; // The primary IonResource from which an instance is derived this._ionRoot = undefined; // Shared promise for endpooint requests amd credits (only ever set on the root request) this._pendingPromise = undefined; this._credits = undefined; this._isExternal = isExternal; } if (defined(Object.create)) { IonResource.prototype = Object.create(Resource.prototype); IonResource.prototype.constructor = IonResource; } /** * Asynchronously creates an instance. * * @param {Number} assetId The Cesium ion asset id. * @param {Object} [options] An object with the following properties: * @param {String} [options.accessToken=Ion.defaultAccessToken] The access token to use. * @param {String|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server. * @returns {Promise.} A Promise to am instance representing the Cesium ion Asset. * * @example * //Load a Cesium3DTileset with asset ID of 124624234 * viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(124624234) })); * * @example * //Load a CZML file with asset ID of 10890 * Cesium.IonResource.fromAssetId(10890) * .then(function (resource) { * viewer.dataSources.add(Cesium.CzmlDataSource.load(resource)); * }); */ IonResource.fromAssetId = function (assetId, options) { var endpointResource = IonResource._createEndpointResource(assetId, options); return endpointResource.fetchJson().then(function (endpoint) { return new IonResource(endpoint, endpointResource); }); }; Object.defineProperties(IonResource.prototype, { /** * Gets the credits required for attribution of the asset. * * @memberof IonResource.prototype * @type {Credit[]} * @readonly */ credits: { get: function () { // Only we're not the root, return its credits; if (defined(this._ionRoot)) { return this._ionRoot.credits; } // We are the root if (defined(this._credits)) { return this._credits; } this._credits = IonResource.getCreditsFromEndpoint( this._ionEndpoint, this._ionEndpointResource ); return this._credits; }, }, }); /** @private */ IonResource.getCreditsFromEndpoint = function (endpoint, endpointResource) { var credits = endpoint.attributions.map(Credit.getIonCredit); var defaultTokenCredit = Ion.getDefaultTokenCredit( endpointResource.queryParameters.access_token ); if (defined(defaultTokenCredit)) { credits.push(Credit.clone(defaultTokenCredit)); } return credits; }; /** @inheritdoc */ IonResource.prototype.clone = function (result) { // We always want to use the root's information because it's the most up-to-date var ionRoot = defaultValue(this._ionRoot, this); if (!defined(result)) { result = new IonResource( ionRoot._ionEndpoint, ionRoot._ionEndpointResource ); } result = Resource.prototype.clone.call(this, result); result._ionRoot = ionRoot; result._isExternal = this._isExternal; return result; }; IonResource.prototype.fetchImage = function (options) { if (!this._isExternal) { var userOptions = options; options = { preferBlob: true, }; if (defined(userOptions)) { options.flipY = userOptions.flipY; options.preferImageBitmap = userOptions.preferImageBitmap; } } return Resource.prototype.fetchImage.call(this, options); }; IonResource.prototype._makeRequest = function (options) { // Don't send ion access token to non-ion servers. if ( this._isExternal || new URI(this.url).authority !== this._ionEndpointDomain ) { return Resource.prototype._makeRequest.call(this, options); } if (!defined(options.headers)) { options.headers = {}; } options.headers.Authorization = "Bearer " + this._ionEndpoint.accessToken; return Resource.prototype._makeRequest.call(this, options); }; /** * @private */ IonResource._createEndpointResource = function (assetId, options) { //>>includeStart('debug', pragmas.debug); Check.defined("assetId", assetId); //>>includeEnd('debug'); options = defaultValue(options, defaultValue.EMPTY_OBJECT); var server = defaultValue(options.server, Ion.defaultServer); var accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); server = Resource.createIfNeeded(server); var resourceOptions = { url: "v1/assets/" + assetId + "/endpoint", }; if (defined(accessToken)) { resourceOptions.queryParameters = { access_token: accessToken }; } return server.getDerivedResource(resourceOptions); }; function retryCallback(that, error) { var ionRoot = defaultValue(that._ionRoot, that); var endpointResource = ionRoot._ionEndpointResource; // Image is not available in worker threads, so this avoids // a ReferenceError var imageDefined = typeof Image !== "undefined"; // We only want to retry in the case of invalid credentials (401) or image // requests(since Image failures can not provide a status code) if ( !defined(error) || (error.statusCode !== 401 && !(imageDefined && error.target instanceof Image)) ) { return when.resolve(false); } // We use a shared pending promise for all derived assets, since they share // a common access_token. If we're already requesting a new token for this // asset, we wait on the same promise. if (!defined(ionRoot._pendingPromise)) { ionRoot._pendingPromise = endpointResource .fetchJson() .then(function (newEndpoint) { //Set the token for root resource so new derived resources automatically pick it up ionRoot._ionEndpoint = newEndpoint; return newEndpoint; }) .always(function (newEndpoint) { // Pass or fail, we're done with this promise, the next failure should use a new one. ionRoot._pendingPromise = undefined; return newEndpoint; }); } return ionRoot._pendingPromise.then(function (newEndpoint) { // Set the new token and endpoint for this resource that._ionEndpoint = newEndpoint; return true; }); } /** * An interval defined by a start and a stop time; optionally including those times as part of the interval. * Arbitrary data can optionally be associated with each instance for used with {@link TimeIntervalCollection}. * * @alias TimeInterval * @constructor * * @param {Object} [options] Object with the following properties: * @param {JulianDate} [options.start=new JulianDate()] The start time of the interval. * @param {JulianDate} [options.stop=new JulianDate()] The stop time of the interval. * @param {Boolean} [options.isStartIncluded=true] true if options.start is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if options.stop is included in the interval, false otherwise. * @param {Object} [options.data] Arbitrary data associated with this interval. * * @example * // Create an instance that spans August 1st, 1980 and is associated * // with a Cartesian position. * var timeInterval = new Cesium.TimeInterval({ * start : Cesium.JulianDate.fromIso8601('1980-08-01T00:00:00Z'), * stop : Cesium.JulianDate.fromIso8601('1980-08-02T00:00:00Z'), * isStartIncluded : true, * isStopIncluded : false, * data : Cesium.Cartesian3.fromDegrees(39.921037, -75.170082) * }); * * @example * // Create two instances from ISO 8601 intervals with associated numeric data * // then compute their intersection, summing the data they contain. * var left = Cesium.TimeInterval.fromIso8601({ * iso8601 : '2000/2010', * data : 2 * }); * * var right = Cesium.TimeInterval.fromIso8601({ * iso8601 : '1995/2005', * data : 3 * }); * * //The result of the below intersection will be an interval equivalent to * //var intersection = Cesium.TimeInterval.fromIso8601({ * // iso8601 : '2000/2005', * // data : 5 * //}); * var intersection = new Cesium.TimeInterval(); * Cesium.TimeInterval.intersect(left, right, intersection, function(leftData, rightData) { * return leftData + rightData; * }); * * @example * // Check if an interval contains a specific time. * var dateToCheck = Cesium.JulianDate.fromIso8601('1982-09-08T11:30:00Z'); * var containsDate = Cesium.TimeInterval.contains(timeInterval, dateToCheck); */ function TimeInterval(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * Gets or sets the start time of this interval. * @type {JulianDate} */ this.start = defined(options.start) ? JulianDate.clone(options.start) : new JulianDate(); /** * Gets or sets the stop time of this interval. * @type {JulianDate} */ this.stop = defined(options.stop) ? JulianDate.clone(options.stop) : new JulianDate(); /** * Gets or sets the data associated with this interval. * @type {*} */ this.data = options.data; /** * Gets or sets whether or not the start time is included in this interval. * @type {Boolean} * @default true */ this.isStartIncluded = defaultValue(options.isStartIncluded, true); /** * Gets or sets whether or not the stop time is included in this interval. * @type {Boolean} * @default true */ this.isStopIncluded = defaultValue(options.isStopIncluded, true); } Object.defineProperties(TimeInterval.prototype, { /** * Gets whether or not this interval is empty. * @memberof TimeInterval.prototype * @type {Boolean} * @readonly */ isEmpty: { get: function () { var stopComparedToStart = JulianDate.compare(this.stop, this.start); return ( stopComparedToStart < 0 || (stopComparedToStart === 0 && (!this.isStartIncluded || !this.isStopIncluded)) ); }, }, }); var scratchInterval = { start: undefined, stop: undefined, isStartIncluded: undefined, isStopIncluded: undefined, data: undefined, }; /** * Creates a new instance from a {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} interval. * * @throws DeveloperError if options.iso8601 does not match proper formatting. * * @param {Object} options Object with the following properties: * @param {String} options.iso8601 An ISO 8601 interval. * @param {Boolean} [options.isStartIncluded=true] true if options.start is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if options.stop is included in the interval, false otherwise. * @param {Object} [options.data] Arbitrary data associated with this interval. * @param {TimeInterval} [result] An existing instance to use for the result. * @returns {TimeInterval} The modified result parameter or a new instance if none was provided. */ TimeInterval.fromIso8601 = function (options, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.string("options.iso8601", options.iso8601); //>>includeEnd('debug'); var dates = options.iso8601.split("/"); if (dates.length !== 2) { throw new DeveloperError( "options.iso8601 is an invalid ISO 8601 interval." ); } var start = JulianDate.fromIso8601(dates[0]); var stop = JulianDate.fromIso8601(dates[1]); var isStartIncluded = defaultValue(options.isStartIncluded, true); var isStopIncluded = defaultValue(options.isStopIncluded, true); var data = options.data; if (!defined(result)) { scratchInterval.start = start; scratchInterval.stop = stop; scratchInterval.isStartIncluded = isStartIncluded; scratchInterval.isStopIncluded = isStopIncluded; scratchInterval.data = data; return new TimeInterval(scratchInterval); } result.start = start; result.stop = stop; result.isStartIncluded = isStartIncluded; result.isStopIncluded = isStopIncluded; result.data = data; return result; }; /** * Creates an ISO8601 representation of the provided interval. * * @param {TimeInterval} timeInterval The interval to be converted. * @param {Number} [precision] The number of fractional digits used to represent the seconds component. By default, the most precise representation is used. * @returns {String} The ISO8601 representation of the provided interval. */ TimeInterval.toIso8601 = function (timeInterval, precision) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("timeInterval", timeInterval); //>>includeEnd('debug'); return ( JulianDate.toIso8601(timeInterval.start, precision) + "/" + JulianDate.toIso8601(timeInterval.stop, precision) ); }; /** * Duplicates the provided instance. * * @param {TimeInterval} [timeInterval] The instance to clone. * @param {TimeInterval} [result] An existing instance to use for the result. * @returns {TimeInterval} The modified result parameter or a new instance if none was provided. */ TimeInterval.clone = function (timeInterval, result) { if (!defined(timeInterval)) { return undefined; } if (!defined(result)) { return new TimeInterval(timeInterval); } result.start = timeInterval.start; result.stop = timeInterval.stop; result.isStartIncluded = timeInterval.isStartIncluded; result.isStopIncluded = timeInterval.isStopIncluded; result.data = timeInterval.data; return result; }; /** * Compares two instances and returns true if they are equal, false otherwise. * * @param {TimeInterval} [left] The first instance. * @param {TimeInterval} [right] The second instance. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @returns {Boolean} true if the dates are equal; otherwise, false. */ TimeInterval.equals = function (left, right, dataComparer) { return ( left === right || (defined(left) && defined(right) && ((left.isEmpty && right.isEmpty) || (left.isStartIncluded === right.isStartIncluded && left.isStopIncluded === right.isStopIncluded && JulianDate.equals(left.start, right.start) && JulianDate.equals(left.stop, right.stop) && (left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data)))))) ); }; /** * Compares two instances and returns true if they are within epsilon seconds of * each other. That is, in order for the dates to be considered equal (and for * this function to return true), the absolute value of the difference between them, in * seconds, must be less than epsilon. * * @param {TimeInterval} [left] The first instance. * @param {TimeInterval} [right] The second instance. * @param {Number} [epsilon=0] The maximum number of seconds that should separate the two instances. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @returns {Boolean} true if the two dates are within epsilon seconds of each other; otherwise false. */ TimeInterval.equalsEpsilon = function (left, right, epsilon, dataComparer) { epsilon = defaultValue(epsilon, 0); return ( left === right || (defined(left) && defined(right) && ((left.isEmpty && right.isEmpty) || (left.isStartIncluded === right.isStartIncluded && left.isStopIncluded === right.isStopIncluded && JulianDate.equalsEpsilon(left.start, right.start, epsilon) && JulianDate.equalsEpsilon(left.stop, right.stop, epsilon) && (left.data === right.data || (defined(dataComparer) && dataComparer(left.data, right.data)))))) ); }; /** * Computes the intersection of two intervals, optionally merging their data. * * @param {TimeInterval} left The first interval. * @param {TimeInterval} [right] The second interval. * @param {TimeInterval} [result] An existing instance to use for the result. * @param {TimeInterval.MergeCallback} [mergeCallback] A function which merges the data of the two intervals. If omitted, the data from the left interval will be used. * @returns {TimeInterval} The modified result parameter. */ TimeInterval.intersect = function (left, right, result, mergeCallback) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("left", left); //>>includeEnd('debug'); if (!defined(right)) { return TimeInterval.clone(TimeInterval.EMPTY, result); } var leftStart = left.start; var leftStop = left.stop; var rightStart = right.start; var rightStop = right.stop; var intersectsStartRight = JulianDate.greaterThanOrEquals(rightStart, leftStart) && JulianDate.greaterThanOrEquals(leftStop, rightStart); var intersectsStartLeft = !intersectsStartRight && JulianDate.lessThanOrEquals(rightStart, leftStart) && JulianDate.lessThanOrEquals(leftStart, rightStop); if (!intersectsStartRight && !intersectsStartLeft) { return TimeInterval.clone(TimeInterval.EMPTY, result); } var leftIsStartIncluded = left.isStartIncluded; var leftIsStopIncluded = left.isStopIncluded; var rightIsStartIncluded = right.isStartIncluded; var rightIsStopIncluded = right.isStopIncluded; var leftLessThanRight = JulianDate.lessThan(leftStop, rightStop); if (!defined(result)) { result = new TimeInterval(); } result.start = intersectsStartRight ? rightStart : leftStart; result.isStartIncluded = (leftIsStartIncluded && rightIsStartIncluded) || (!JulianDate.equals(rightStart, leftStart) && ((intersectsStartRight && rightIsStartIncluded) || (intersectsStartLeft && leftIsStartIncluded))); result.stop = leftLessThanRight ? leftStop : rightStop; result.isStopIncluded = leftLessThanRight ? leftIsStopIncluded : (leftIsStopIncluded && rightIsStopIncluded) || (!JulianDate.equals(rightStop, leftStop) && rightIsStopIncluded); result.data = defined(mergeCallback) ? mergeCallback(left.data, right.data) : left.data; return result; }; /** * Checks if the specified date is inside the provided interval. * * @param {TimeInterval} timeInterval The interval. * @param {JulianDate} julianDate The date to check. * @returns {Boolean} true if the interval contains the specified date, false otherwise. */ TimeInterval.contains = function (timeInterval, julianDate) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("timeInterval", timeInterval); Check.typeOf.object("julianDate", julianDate); //>>includeEnd('debug'); if (timeInterval.isEmpty) { return false; } var startComparedToDate = JulianDate.compare(timeInterval.start, julianDate); if (startComparedToDate === 0) { return timeInterval.isStartIncluded; } var dateComparedToStop = JulianDate.compare(julianDate, timeInterval.stop); if (dateComparedToStop === 0) { return timeInterval.isStopIncluded; } return startComparedToDate < 0 && dateComparedToStop < 0; }; /** * Duplicates this instance. * * @param {TimeInterval} [result] An existing instance to use for the result. * @returns {TimeInterval} The modified result parameter or a new instance if none was provided. */ TimeInterval.prototype.clone = function (result) { return TimeInterval.clone(this, result); }; /** * Compares this instance against the provided instance componentwise and returns * true if they are equal, false otherwise. * * @param {TimeInterval} [right] The right hand side interval. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @returns {Boolean} true if they are equal, false otherwise. */ TimeInterval.prototype.equals = function (right, dataComparer) { return TimeInterval.equals(this, right, dataComparer); }; /** * Compares this instance against the provided instance componentwise and returns * true if they are within the provided epsilon, * false otherwise. * * @param {TimeInterval} [right] The right hand side interval. * @param {Number} [epsilon=0] The epsilon to use for equality testing. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @returns {Boolean} true if they are within the provided epsilon, false otherwise. */ TimeInterval.prototype.equalsEpsilon = function (right, epsilon, dataComparer) { return TimeInterval.equalsEpsilon(this, right, epsilon, dataComparer); }; /** * Creates a string representing this TimeInterval in ISO8601 format. * * @returns {String} A string representing this TimeInterval in ISO8601 format. */ TimeInterval.prototype.toString = function () { return TimeInterval.toIso8601(this); }; /** * An immutable empty interval. * * @type {TimeInterval} * @constant */ TimeInterval.EMPTY = Object.freeze( new TimeInterval({ start: new JulianDate(), stop: new JulianDate(), isStartIncluded: false, isStopIncluded: false, }) ); var MINIMUM_VALUE = Object.freeze( JulianDate.fromIso8601("0000-01-01T00:00:00Z") ); var MAXIMUM_VALUE = Object.freeze( JulianDate.fromIso8601("9999-12-31T24:00:00Z") ); var MAXIMUM_INTERVAL = Object.freeze( new TimeInterval({ start: MINIMUM_VALUE, stop: MAXIMUM_VALUE, }) ); /** * Constants related to ISO8601 support. * * @namespace * * @see {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601 on Wikipedia} * @see JulianDate * @see TimeInterval */ var Iso8601 = { /** * A {@link JulianDate} representing the earliest time representable by an ISO8601 date. * This is equivalent to the date string '0000-01-01T00:00:00Z' * * @type {JulianDate} * @constant */ MINIMUM_VALUE: MINIMUM_VALUE, /** * A {@link JulianDate} representing the latest time representable by an ISO8601 date. * This is equivalent to the date string '9999-12-31T24:00:00Z' * * @type {JulianDate} * @constant */ MAXIMUM_VALUE: MAXIMUM_VALUE, /** * A {@link TimeInterval} representing the largest interval representable by an ISO8601 interval. * This is equivalent to the interval string '0000-01-01T00:00:00Z/9999-12-31T24:00:00Z' * * @type {JulianDate} * @constant */ MAXIMUM_INTERVAL: MAXIMUM_INTERVAL, }; /** * This enumerated type is for representing keyboard modifiers. These are keys * that are held down in addition to other event types. * * @enum {Number} */ var KeyboardEventModifier = { /** * Represents the shift key being held down. * * @type {Number} * @constant */ SHIFT: 0, /** * Represents the control key being held down. * * @type {Number} * @constant */ CTRL: 1, /** * Represents the alt key being held down. * * @type {Number} * @constant */ ALT: 2, }; var KeyboardEventModifier$1 = Object.freeze(KeyboardEventModifier); /** * An {@link InterpolationAlgorithm} for performing Lagrange interpolation. * * @namespace LagrangePolynomialApproximation */ var LagrangePolynomialApproximation = { type: "Lagrange", }; /** * Given the desired degree, returns the number of data points required for interpolation. * * @param {Number} degree The desired degree of interpolation. * @returns {Number} The number of required data points needed for the desired degree of interpolation. */ LagrangePolynomialApproximation.getRequiredDataPoints = function (degree) { return Math.max(degree + 1.0, 2); }; /** * Interpolates values using Lagrange Polynomial Approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number[]} [result] An existing array into which to store the result. * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ LagrangePolynomialApproximation.interpolateOrderZero = function ( x, xTable, yTable, yStride, result ) { if (!defined(result)) { result = new Array(yStride); } var i; var j; var length = xTable.length; for (i = 0; i < yStride; i++) { result[i] = 0; } for (i = 0; i < length; i++) { var coefficient = 1; for (j = 0; j < length; j++) { if (j !== i) { var diffX = xTable[i] - xTable[j]; coefficient *= (x - xTable[j]) / diffX; } } for (j = 0; j < yStride; j++) { result[j] += coefficient * yTable[i * yStride + j]; } } return result; }; /** * An {@link InterpolationAlgorithm} for performing linear interpolation. * * @namespace LinearApproximation */ var LinearApproximation = { type: "Linear", }; /** * Given the desired degree, returns the number of data points required for interpolation. * Since linear interpolation can only generate a first degree polynomial, this function * always returns 2. * @param {Number} degree The desired degree of interpolation. * @returns {Number} This function always returns 2. * */ LinearApproximation.getRequiredDataPoints = function (degree) { return 2; }; /** * Interpolates values using linear approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number[]} [result] An existing array into which to store the result. * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ LinearApproximation.interpolateOrderZero = function ( x, xTable, yTable, yStride, result ) { //>>includeStart('debug', pragmas.debug); if (xTable.length !== 2) { throw new DeveloperError( "The xTable provided to the linear interpolator must have exactly two elements." ); } else if (yStride <= 0) { throw new DeveloperError( "There must be at least 1 dependent variable for each independent variable." ); } //>>includeEnd('debug'); if (!defined(result)) { result = new Array(yStride); } var i; var y0; var y1; var x0 = xTable[0]; var x1 = xTable[1]; //>>includeStart('debug', pragmas.debug); if (x0 === x1) { throw new DeveloperError( "Divide by zero error: xTable[0] and xTable[1] are equal" ); } //>>includeEnd('debug'); for (i = 0; i < yStride; i++) { y0 = yTable[i]; y1 = yTable[i + yStride]; result[i] = ((y1 - y0) * x + x1 * y0 - x0 * y1) / (x1 - x0); } return result; }; /** * A wrapper around arrays so that the internal length of the array can be manually managed. * * @alias ManagedArray * @constructor * @private * * @param {Number} [length=0] The initial length of the array. */ function ManagedArray(length) { length = defaultValue(length, 0); this._array = new Array(length); this._length = length; } Object.defineProperties(ManagedArray.prototype, { /** * Gets or sets the length of the array. * If the set length is greater than the length of the internal array, the internal array is resized. * * @memberof ManagedArray.prototype * @type Number */ length: { get: function () { return this._length; }, set: function (length) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("length", length, 0); //>>includeEnd('debug'); var array = this._array; var originalLength = this._length; if (length < originalLength) { // Remove trailing references for (var i = length; i < originalLength; ++i) { array[i] = undefined; } } else if (length > array.length) { array.length = length; } this._length = length; }, }, /** * Gets the internal array. * * @memberof ManagedArray.prototype * @type Array * @readonly */ values: { get: function () { return this._array; }, }, }); /** * Gets the element at an index. * * @param {Number} index The index to get. */ ManagedArray.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.lessThan("index", index, this._array.length); //>>includeEnd('debug'); return this._array[index]; }; /** * Sets the element at an index. Resizes the array if index is greater than the length of the array. * * @param {Number} index The index to set. * @param {*} element The element to set at index. */ ManagedArray.prototype.set = function (index, element) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("index", index); //>>includeEnd('debug'); if (index >= this._length) { this.length = index + 1; } this._array[index] = element; }; /** * Returns the last element in the array without modifying the array. * * @returns {*} The last element in the array. */ ManagedArray.prototype.peek = function () { return this._array[this._length - 1]; }; /** * Push an element into the array. * * @param {*} element The element to push. */ ManagedArray.prototype.push = function (element) { var index = this.length++; this._array[index] = element; }; /** * Pop an element from the array. * * @returns {*} The last element in the array. */ ManagedArray.prototype.pop = function () { if (this._length === 0) { return undefined; } var element = this._array[this._length - 1]; --this.length; return element; }; /** * Resize the internal array if length > _array.length. * * @param {Number} length The length. */ ManagedArray.prototype.reserve = function (length) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("length", length, 0); //>>includeEnd('debug'); if (length > this._array.length) { this._array.length = length; } }; /** * Resize the array. * * @param {Number} length The length. */ ManagedArray.prototype.resize = function (length) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("length", length, 0); //>>includeEnd('debug'); this.length = length; }; /** * Trim the internal array to the specified length. Defaults to the current length. * * @param {Number} [length] The length. */ ManagedArray.prototype.trim = function (length) { length = defaultValue(length, this._length); this._array.length = length; }; /** * Defines how geodetic ellipsoid coordinates ({@link Cartographic}) project to a * flat map like Cesium's 2D and Columbus View modes. * * @alias MapProjection * @constructor * @abstract * * @see GeographicProjection * @see WebMercatorProjection */ function MapProjection() { DeveloperError.throwInstantiationError(); } Object.defineProperties(MapProjection.prototype, { /** * Gets the {@link Ellipsoid}. * * @memberof MapProjection.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: DeveloperError.throwInstantiationError, }, }); /** * Projects {@link Cartographic} coordinates, in radians, to projection-specific map coordinates, in meters. * * @memberof MapProjection * @function * * @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. */ MapProjection.prototype.project = DeveloperError.throwInstantiationError; /** * Unprojects projection-specific map {@link Cartesian3} coordinates, in meters, to {@link Cartographic} * coordinates, in radians. * * @memberof MapProjection * @function * * @param {Cartesian3} cartesian The Cartesian position to unproject with height (z) in meters. * @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. */ MapProjection.prototype.unproject = DeveloperError.throwInstantiationError; /** * Represents a scalar value's lower and upper bound at a near distance and far distance in eye space. * @alias NearFarScalar * @constructor * * @param {Number} [near=0.0] The lower bound of the camera range. * @param {Number} [nearValue=0.0] The value at the lower bound of the camera range. * @param {Number} [far=1.0] The upper bound of the camera range. * @param {Number} [farValue=0.0] The value at the upper bound of the camera range. * * @see Packable */ function NearFarScalar(near, nearValue, far, farValue) { /** * The lower bound of the camera range. * @type {Number} * @default 0.0 */ this.near = defaultValue(near, 0.0); /** * The value at the lower bound of the camera range. * @type {Number} * @default 0.0 */ this.nearValue = defaultValue(nearValue, 0.0); /** * The upper bound of the camera range. * @type {Number} * @default 1.0 */ this.far = defaultValue(far, 1.0); /** * The value at the upper bound of the camera range. * @type {Number} * @default 0.0 */ this.farValue = defaultValue(farValue, 0.0); } /** * Duplicates a NearFarScalar instance. * * @param {NearFarScalar} nearFarScalar The NearFarScalar to duplicate. * @param {NearFarScalar} [result] The object onto which to store the result. * @returns {NearFarScalar} The modified result parameter or a new NearFarScalar instance if one was not provided. (Returns undefined if nearFarScalar is undefined) */ NearFarScalar.clone = function (nearFarScalar, result) { if (!defined(nearFarScalar)) { return undefined; } if (!defined(result)) { return new NearFarScalar( nearFarScalar.near, nearFarScalar.nearValue, nearFarScalar.far, nearFarScalar.farValue ); } result.near = nearFarScalar.near; result.nearValue = nearFarScalar.nearValue; result.far = nearFarScalar.far; result.farValue = nearFarScalar.farValue; return result; }; /** * The number of elements used to pack the object into an array. * @type {Number} */ NearFarScalar.packedLength = 4; /** * Stores the provided instance into the provided array. * * @param {NearFarScalar} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ NearFarScalar.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex++] = value.near; array[startingIndex++] = value.nearValue; array[startingIndex++] = value.far; array[startingIndex] = value.farValue; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {NearFarScalar} [result] The object into which to store the result. * @returns {NearFarScalar} The modified result parameter or a new NearFarScalar instance if one was not provided. */ NearFarScalar.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); if (!defined(result)) { result = new NearFarScalar(); } result.near = array[startingIndex++]; result.nearValue = array[startingIndex++]; result.far = array[startingIndex++]; result.farValue = array[startingIndex]; return result; }; /** * Compares the provided NearFarScalar and returns true if they are equal, * false otherwise. * * @param {NearFarScalar} [left] The first NearFarScalar. * @param {NearFarScalar} [right] The second NearFarScalar. * @returns {Boolean} true if left and right are equal; otherwise false. */ NearFarScalar.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.near === right.near && left.nearValue === right.nearValue && left.far === right.far && left.farValue === right.farValue) ); }; /** * Duplicates this instance. * * @param {NearFarScalar} [result] The object onto which to store the result. * @returns {NearFarScalar} The modified result parameter or a new NearFarScalar instance if one was not provided. */ NearFarScalar.prototype.clone = function (result) { return NearFarScalar.clone(this, result); }; /** * Compares this instance to the provided NearFarScalar and returns true if they are equal, * false otherwise. * * @param {NearFarScalar} [right] The right hand side NearFarScalar. * @returns {Boolean} true if left and right are equal; otherwise false. */ NearFarScalar.prototype.equals = function (right) { return NearFarScalar.equals(this, right); }; /** * This enumerated type is used in determining to what extent an object, the occludee, * is visible during horizon culling. An occluder may fully block an occludee, in which case * it has no visibility, may partially block an occludee from view, or may not block it at all, * leading to full visibility. * * @enum {Number} */ var Visibility = { /** * Represents that no part of an object is visible. * * @type {Number} * @constant */ NONE: -1, /** * Represents that part, but not all, of an object is visible * * @type {Number} * @constant */ PARTIAL: 0, /** * Represents that an object is visible in its entirety. * * @type {Number} * @constant */ FULL: 1, }; var Visibility$1 = Object.freeze(Visibility); /** * Creates an Occluder derived from an object's position and radius, as well as the camera position. * The occluder can be used to determine whether or not other objects are visible or hidden behind the * visible horizon defined by the occluder and camera position. * * @alias Occluder * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera. * * @constructor * * @example * // Construct an occluder one unit away from the origin with a radius of one. * var cameraPosition = Cesium.Cartesian3.ZERO; * var occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 1); * var occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition); */ function Occluder(occluderBoundingSphere, cameraPosition) { //>>includeStart('debug', pragmas.debug); if (!defined(occluderBoundingSphere)) { throw new DeveloperError("occluderBoundingSphere is required."); } if (!defined(cameraPosition)) { throw new DeveloperError("camera position is required."); } //>>includeEnd('debug'); this._occluderPosition = Cartesian3.clone(occluderBoundingSphere.center); this._occluderRadius = occluderBoundingSphere.radius; this._horizonDistance = 0.0; this._horizonPlaneNormal = undefined; this._horizonPlanePosition = undefined; this._cameraPosition = undefined; // cameraPosition fills in the above values this.cameraPosition = cameraPosition; } var scratchCartesian3$6 = new Cartesian3(); Object.defineProperties(Occluder.prototype, { /** * The position of the occluder. * @memberof Occluder.prototype * @type {Cartesian3} */ position: { get: function () { return this._occluderPosition; }, }, /** * The radius of the occluder. * @memberof Occluder.prototype * @type {Number} */ radius: { get: function () { return this._occluderRadius; }, }, /** * The position of the camera. * @memberof Occluder.prototype * @type {Cartesian3} */ cameraPosition: { set: function (cameraPosition) { //>>includeStart('debug', pragmas.debug); if (!defined(cameraPosition)) { throw new DeveloperError("cameraPosition is required."); } //>>includeEnd('debug'); cameraPosition = Cartesian3.clone(cameraPosition, this._cameraPosition); var cameraToOccluderVec = Cartesian3.subtract( this._occluderPosition, cameraPosition, scratchCartesian3$6 ); var invCameraToOccluderDistance = Cartesian3.magnitudeSquared( cameraToOccluderVec ); var occluderRadiusSqrd = this._occluderRadius * this._occluderRadius; var horizonDistance; var horizonPlaneNormal; var horizonPlanePosition; if (invCameraToOccluderDistance > occluderRadiusSqrd) { horizonDistance = Math.sqrt( invCameraToOccluderDistance - occluderRadiusSqrd ); invCameraToOccluderDistance = 1.0 / Math.sqrt(invCameraToOccluderDistance); horizonPlaneNormal = Cartesian3.multiplyByScalar( cameraToOccluderVec, invCameraToOccluderDistance, scratchCartesian3$6 ); var nearPlaneDistance = horizonDistance * horizonDistance * invCameraToOccluderDistance; horizonPlanePosition = Cartesian3.add( cameraPosition, Cartesian3.multiplyByScalar( horizonPlaneNormal, nearPlaneDistance, scratchCartesian3$6 ), scratchCartesian3$6 ); } else { horizonDistance = Number.MAX_VALUE; } this._horizonDistance = horizonDistance; this._horizonPlaneNormal = horizonPlaneNormal; this._horizonPlanePosition = horizonPlanePosition; this._cameraPosition = cameraPosition; }, }, }); /** * Creates an occluder from a bounding sphere and the camera position. * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} cameraPosition The coordinate of the viewer/camera. * @param {Occluder} [result] The object onto which to store the result. * @returns {Occluder} The occluder derived from an object's position and radius, as well as the camera position. */ Occluder.fromBoundingSphere = function ( occluderBoundingSphere, cameraPosition, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(occluderBoundingSphere)) { throw new DeveloperError("occluderBoundingSphere is required."); } if (!defined(cameraPosition)) { throw new DeveloperError("camera position is required."); } //>>includeEnd('debug'); if (!defined(result)) { return new Occluder(occluderBoundingSphere, cameraPosition); } Cartesian3.clone(occluderBoundingSphere.center, result._occluderPosition); result._occluderRadius = occluderBoundingSphere.radius; result.cameraPosition = cameraPosition; return result; }; var tempVecScratch = new Cartesian3(); /** * Determines whether or not a point, the occludee, is hidden from view by the occluder. * * @param {Cartesian3} occludee The point surrounding the occludee object. * @returns {Boolean} true if the occludee is visible; otherwise false. * * * @example * var cameraPosition = new Cesium.Cartesian3(0, 0, 0); * var littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25); * var occluder = new Cesium.Occluder(littleSphere, cameraPosition); * var point = new Cesium.Cartesian3(0, 0, -3); * occluder.isPointVisible(point); //returns true * * @see Occluder#computeVisibility */ Occluder.prototype.isPointVisible = function (occludee) { if (this._horizonDistance !== Number.MAX_VALUE) { var tempVec = Cartesian3.subtract( occludee, this._occluderPosition, tempVecScratch ); var temp = this._occluderRadius; temp = Cartesian3.magnitudeSquared(tempVec) - temp * temp; if (temp > 0.0) { temp = Math.sqrt(temp) + this._horizonDistance; tempVec = Cartesian3.subtract(occludee, this._cameraPosition, tempVec); return temp * temp > Cartesian3.magnitudeSquared(tempVec); } } return false; }; var occludeePositionScratch = new Cartesian3(); /** * Determines whether or not a sphere, the occludee, is hidden from view by the occluder. * * @param {BoundingSphere} occludee The bounding sphere surrounding the occludee object. * @returns {Boolean} true if the occludee is visible; otherwise false. * * * @example * var cameraPosition = new Cesium.Cartesian3(0, 0, 0); * var littleSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1), 0.25); * var occluder = new Cesium.Occluder(littleSphere, cameraPosition); * var bigSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -3), 1); * occluder.isBoundingSphereVisible(bigSphere); //returns true * * @see Occluder#computeVisibility */ Occluder.prototype.isBoundingSphereVisible = function (occludee) { var occludeePosition = Cartesian3.clone( occludee.center, occludeePositionScratch ); var occludeeRadius = occludee.radius; if (this._horizonDistance !== Number.MAX_VALUE) { var tempVec = Cartesian3.subtract( occludeePosition, this._occluderPosition, tempVecScratch ); var temp = this._occluderRadius - occludeeRadius; temp = Cartesian3.magnitudeSquared(tempVec) - temp * temp; if (occludeeRadius < this._occluderRadius) { if (temp > 0.0) { temp = Math.sqrt(temp) + this._horizonDistance; tempVec = Cartesian3.subtract( occludeePosition, this._cameraPosition, tempVec ); return ( temp * temp + occludeeRadius * occludeeRadius > Cartesian3.magnitudeSquared(tempVec) ); } return false; } // Prevent against the case where the occludee radius is larger than the occluder's; since this is // an uncommon case, the following code should rarely execute. if (temp > 0.0) { tempVec = Cartesian3.subtract( occludeePosition, this._cameraPosition, tempVec ); var tempVecMagnitudeSquared = Cartesian3.magnitudeSquared(tempVec); var occluderRadiusSquared = this._occluderRadius * this._occluderRadius; var occludeeRadiusSquared = occludeeRadius * occludeeRadius; if ( (this._horizonDistance * this._horizonDistance + occluderRadiusSquared) * occludeeRadiusSquared > tempVecMagnitudeSquared * occluderRadiusSquared ) { // The occludee is close enough that the occluder cannot possible occlude the occludee return true; } temp = Math.sqrt(temp) + this._horizonDistance; return temp * temp + occludeeRadiusSquared > tempVecMagnitudeSquared; } // The occludee completely encompasses the occluder return true; } return false; }; var tempScratch = new Cartesian3(); /** * Determine to what extent an occludee is visible (not visible, partially visible, or fully visible). * * @param {BoundingSphere} occludeeBS The bounding sphere of the occludee. * @returns {Visibility} Visibility.NONE if the occludee is not visible, * Visibility.PARTIAL if the occludee is partially visible, or * Visibility.FULL if the occludee is fully visible. * * * @example * var sphere1 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -1.5), 0.5); * var sphere2 = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -2.5), 0.5); * var cameraPosition = new Cesium.Cartesian3(0, 0, 0); * var occluder = new Cesium.Occluder(sphere1, cameraPosition); * occluder.computeVisibility(sphere2); //returns Visibility.NONE * * @see Occluder#isVisible */ Occluder.prototype.computeVisibility = function (occludeeBS) { //>>includeStart('debug', pragmas.debug); if (!defined(occludeeBS)) { throw new DeveloperError("occludeeBS is required."); } //>>includeEnd('debug'); // If the occludee radius is larger than the occluders, this will return that // the entire ocludee is visible, even though that may not be the case, though this should // not occur too often. var occludeePosition = Cartesian3.clone(occludeeBS.center); var occludeeRadius = occludeeBS.radius; if (occludeeRadius > this._occluderRadius) { return Visibility$1.FULL; } if (this._horizonDistance !== Number.MAX_VALUE) { // The camera is outside the occluder var tempVec = Cartesian3.subtract( occludeePosition, this._occluderPosition, tempScratch ); var temp = this._occluderRadius - occludeeRadius; var occluderToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec); temp = occluderToOccludeeDistSqrd - temp * temp; if (temp > 0.0) { // The occludee is not completely inside the occluder // Check to see if the occluder completely hides the occludee temp = Math.sqrt(temp) + this._horizonDistance; tempVec = Cartesian3.subtract( occludeePosition, this._cameraPosition, tempVec ); var cameraToOccludeeDistSqrd = Cartesian3.magnitudeSquared(tempVec); if ( temp * temp + occludeeRadius * occludeeRadius < cameraToOccludeeDistSqrd ) { return Visibility$1.NONE; } // Check to see whether the occluder is fully or partially visible // when the occludee does not intersect the occluder temp = this._occluderRadius + occludeeRadius; temp = occluderToOccludeeDistSqrd - temp * temp; if (temp > 0.0) { // The occludee does not intersect the occluder. temp = Math.sqrt(temp) + this._horizonDistance; return cameraToOccludeeDistSqrd < temp * temp + occludeeRadius * occludeeRadius ? Visibility$1.FULL : Visibility$1.PARTIAL; } //Check to see if the occluder is fully or partially visible when the occludee DOES //intersect the occluder tempVec = Cartesian3.subtract( occludeePosition, this._horizonPlanePosition, tempVec ); return Cartesian3.dot(tempVec, this._horizonPlaneNormal) > -occludeeRadius ? Visibility$1.PARTIAL : Visibility$1.FULL; } } return Visibility$1.NONE; }; var occludeePointScratch = new Cartesian3(); /** * Computes a point that can be used as the occludee position to the visibility functions. * Use a radius of zero for the occludee radius. Typically, a user computes a bounding sphere around * an object that is used for visibility; however it is also possible to compute a point that if * seen/not seen would also indicate if an object is visible/not visible. This function is better * called for objects that do not move relative to the occluder and is large, such as a chunk of * terrain. You are better off not calling this and using the object's bounding sphere for objects * such as a satellite or ground vehicle. * * @param {BoundingSphere} occluderBoundingSphere The bounding sphere surrounding the occluder. * @param {Cartesian3} occludeePosition The point where the occludee (bounding sphere of radius 0) is located. * @param {Cartesian3[]} positions List of altitude points on the horizon near the surface of the occluder. * @returns {Object} An object containing two attributes: occludeePoint and valid * which is a boolean value. * * @exception {DeveloperError} positions must contain at least one element. * @exception {DeveloperError} occludeePosition must have a value other than occluderBoundingSphere.center. * * @example * var cameraPosition = new Cesium.Cartesian3(0, 0, 0); * var occluderBoundingSphere = new Cesium.BoundingSphere(new Cesium.Cartesian3(0, 0, -8), 2); * var occluder = new Cesium.Occluder(occluderBoundingSphere, cameraPosition); * var positions = [new Cesium.Cartesian3(-0.25, 0, -5.3), new Cesium.Cartesian3(0.25, 0, -5.3)]; * var tileOccluderSphere = Cesium.BoundingSphere.fromPoints(positions); * var occludeePosition = tileOccluderSphere.center; * var occludeePt = Cesium.Occluder.computeOccludeePoint(occluderBoundingSphere, occludeePosition, positions); */ Occluder.computeOccludeePoint = function ( occluderBoundingSphere, occludeePosition, positions ) { //>>includeStart('debug', pragmas.debug); if (!defined(occluderBoundingSphere)) { throw new DeveloperError("occluderBoundingSphere is required."); } if (!defined(positions)) { throw new DeveloperError("positions is required."); } if (positions.length === 0) { throw new DeveloperError("positions must contain at least one element"); } //>>includeEnd('debug'); var occludeePos = Cartesian3.clone(occludeePosition); var occluderPosition = Cartesian3.clone(occluderBoundingSphere.center); var occluderRadius = occluderBoundingSphere.radius; var numPositions = positions.length; //>>includeStart('debug', pragmas.debug); if (Cartesian3.equals(occluderPosition, occludeePosition)) { throw new DeveloperError( "occludeePosition must be different than occluderBoundingSphere.center" ); } //>>includeEnd('debug'); // Compute a plane with a normal from the occluder to the occludee position. var occluderPlaneNormal = Cartesian3.normalize( Cartesian3.subtract(occludeePos, occluderPosition, occludeePointScratch), occludeePointScratch ); var occluderPlaneD = -Cartesian3.dot(occluderPlaneNormal, occluderPosition); //For each position, determine the horizon intersection. Choose the position and intersection //that results in the greatest angle with the occcluder plane. var aRotationVector = Occluder._anyRotationVector( occluderPosition, occluderPlaneNormal, occluderPlaneD ); var dot = Occluder._horizonToPlaneNormalDotProduct( occluderBoundingSphere, occluderPlaneNormal, occluderPlaneD, aRotationVector, positions[0] ); if (!dot) { //The position is inside the mimimum radius, which is invalid return undefined; } var tempDot; for (var i = 1; i < numPositions; ++i) { tempDot = Occluder._horizonToPlaneNormalDotProduct( occluderBoundingSphere, occluderPlaneNormal, occluderPlaneD, aRotationVector, positions[i] ); if (!tempDot) { //The position is inside the minimum radius, which is invalid return undefined; } if (tempDot < dot) { dot = tempDot; } } //Verify that the dot is not near 90 degress if (dot < 0.00174532836589830883577820272085) { return undefined; } var distance = occluderRadius / dot; return Cartesian3.add( occluderPosition, Cartesian3.multiplyByScalar( occluderPlaneNormal, distance, occludeePointScratch ), occludeePointScratch ); }; var computeOccludeePointFromRectangleScratch = []; /** * Computes a point that can be used as the occludee position to the visibility functions from a rectangle. * * @param {Rectangle} rectangle The rectangle used to create a bounding sphere. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the rectangle. * @returns {Object} An object containing two attributes: occludeePoint and valid * which is a boolean value. */ Occluder.computeOccludeePointFromRectangle = function (rectangle, ellipsoid) { //>>includeStart('debug', pragmas.debug); if (!defined(rectangle)) { throw new DeveloperError("rectangle is required."); } //>>includeEnd('debug'); ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var positions = Rectangle.subsample( rectangle, ellipsoid, 0.0, computeOccludeePointFromRectangleScratch ); var bs = BoundingSphere.fromPoints(positions); // TODO: get correct ellipsoid center var ellipsoidCenter = Cartesian3.ZERO; if (!Cartesian3.equals(ellipsoidCenter, bs.center)) { return Occluder.computeOccludeePoint( new BoundingSphere(ellipsoidCenter, ellipsoid.minimumRadius), bs.center, positions ); } return undefined; }; var tempVec0Scratch = new Cartesian3(); Occluder._anyRotationVector = function ( occluderPosition, occluderPlaneNormal, occluderPlaneD ) { var tempVec0 = Cartesian3.abs(occluderPlaneNormal, tempVec0Scratch); var majorAxis = tempVec0.x > tempVec0.y ? 0 : 1; if ( (majorAxis === 0 && tempVec0.z > tempVec0.x) || (majorAxis === 1 && tempVec0.z > tempVec0.y) ) { majorAxis = 2; } var tempVec = new Cartesian3(); var tempVec1; if (majorAxis === 0) { tempVec0.x = occluderPosition.x; tempVec0.y = occluderPosition.y + 1.0; tempVec0.z = occluderPosition.z + 1.0; tempVec1 = Cartesian3.UNIT_X; } else if (majorAxis === 1) { tempVec0.x = occluderPosition.x + 1.0; tempVec0.y = occluderPosition.y; tempVec0.z = occluderPosition.z + 1.0; tempVec1 = Cartesian3.UNIT_Y; } else { tempVec0.x = occluderPosition.x + 1.0; tempVec0.y = occluderPosition.y + 1.0; tempVec0.z = occluderPosition.z; tempVec1 = Cartesian3.UNIT_Z; } var u = (Cartesian3.dot(occluderPlaneNormal, tempVec0) + occluderPlaneD) / -Cartesian3.dot(occluderPlaneNormal, tempVec1); return Cartesian3.normalize( Cartesian3.subtract( Cartesian3.add( tempVec0, Cartesian3.multiplyByScalar(tempVec1, u, tempVec), tempVec0 ), occluderPosition, tempVec0 ), tempVec0 ); }; var posDirectionScratch = new Cartesian3(); Occluder._rotationVector = function ( occluderPosition, occluderPlaneNormal, occluderPlaneD, position, anyRotationVector ) { //Determine the angle between the occluder plane normal and the position direction var positionDirection = Cartesian3.subtract( position, occluderPosition, posDirectionScratch ); positionDirection = Cartesian3.normalize( positionDirection, positionDirection ); if ( Cartesian3.dot(occluderPlaneNormal, positionDirection) < 0.99999998476912904932780850903444 ) { var crossProduct = Cartesian3.cross( occluderPlaneNormal, positionDirection, positionDirection ); var length = Cartesian3.magnitude(crossProduct); if (length > CesiumMath.EPSILON13) { return Cartesian3.normalize(crossProduct, new Cartesian3()); } } //The occluder plane normal and the position direction are colinear. Use any //vector in the occluder plane as the rotation vector return anyRotationVector; }; var posScratch1 = new Cartesian3(); var occluerPosScratch = new Cartesian3(); var posScratch2 = new Cartesian3(); var horizonPlanePosScratch = new Cartesian3(); Occluder._horizonToPlaneNormalDotProduct = function ( occluderBS, occluderPlaneNormal, occluderPlaneD, anyRotationVector, position ) { var pos = Cartesian3.clone(position, posScratch1); var occluderPosition = Cartesian3.clone(occluderBS.center, occluerPosScratch); var occluderRadius = occluderBS.radius; //Verify that the position is outside the occluder var positionToOccluder = Cartesian3.subtract( occluderPosition, pos, posScratch2 ); var occluderToPositionDistanceSquared = Cartesian3.magnitudeSquared( positionToOccluder ); var occluderRadiusSquared = occluderRadius * occluderRadius; if (occluderToPositionDistanceSquared < occluderRadiusSquared) { return false; } //Horizon parameters var horizonDistanceSquared = occluderToPositionDistanceSquared - occluderRadiusSquared; var horizonDistance = Math.sqrt(horizonDistanceSquared); var occluderToPositionDistance = Math.sqrt(occluderToPositionDistanceSquared); var invOccluderToPositionDistance = 1.0 / occluderToPositionDistance; var cosTheta = horizonDistance * invOccluderToPositionDistance; var horizonPlaneDistance = cosTheta * horizonDistance; positionToOccluder = Cartesian3.normalize( positionToOccluder, positionToOccluder ); var horizonPlanePosition = Cartesian3.add( pos, Cartesian3.multiplyByScalar( positionToOccluder, horizonPlaneDistance, horizonPlanePosScratch ), horizonPlanePosScratch ); var horizonCrossDistance = Math.sqrt( horizonDistanceSquared - horizonPlaneDistance * horizonPlaneDistance ); //Rotate the position to occluder vector 90 degrees var tempVec = this._rotationVector( occluderPosition, occluderPlaneNormal, occluderPlaneD, pos, anyRotationVector ); var horizonCrossDirection = Cartesian3.fromElements( tempVec.x * tempVec.x * positionToOccluder.x + (tempVec.x * tempVec.y - tempVec.z) * positionToOccluder.y + (tempVec.x * tempVec.z + tempVec.y) * positionToOccluder.z, (tempVec.x * tempVec.y + tempVec.z) * positionToOccluder.x + tempVec.y * tempVec.y * positionToOccluder.y + (tempVec.y * tempVec.z - tempVec.x) * positionToOccluder.z, (tempVec.x * tempVec.z - tempVec.y) * positionToOccluder.x + (tempVec.y * tempVec.z + tempVec.x) * positionToOccluder.y + tempVec.z * tempVec.z * positionToOccluder.z, posScratch1 ); horizonCrossDirection = Cartesian3.normalize( horizonCrossDirection, horizonCrossDirection ); //Horizon positions var offset = Cartesian3.multiplyByScalar( horizonCrossDirection, horizonCrossDistance, posScratch1 ); tempVec = Cartesian3.normalize( Cartesian3.subtract( Cartesian3.add(horizonPlanePosition, offset, posScratch2), occluderPosition, posScratch2 ), posScratch2 ); var dot0 = Cartesian3.dot(occluderPlaneNormal, tempVec); tempVec = Cartesian3.normalize( Cartesian3.subtract( Cartesian3.subtract(horizonPlanePosition, offset, tempVec), occluderPosition, tempVec ), tempVec ); var dot1 = Cartesian3.dot(occluderPlaneNormal, tempVec); return dot0 < dot1 ? dot0 : dot1; }; /** * Value and type information for per-instance geometry attribute that determines the geometry instance offset * * @alias OffsetGeometryInstanceAttribute * @constructor * * @param {Number} [x=0] The x translation * @param {Number} [y=0] The y translation * @param {Number} [z=0] The z translation * * @private * * @see GeometryInstance * @see GeometryInstanceAttribute */ function OffsetGeometryInstanceAttribute(x, y, z) { x = defaultValue(x, 0); y = defaultValue(y, 0); z = defaultValue(z, 0); /** * The values for the attributes stored in a typed array. * * @type Float32Array */ this.value = new Float32Array([x, y, z]); } Object.defineProperties(OffsetGeometryInstanceAttribute.prototype, { /** * The datatype of each component in the attribute, e.g., individual elements in * {@link OffsetGeometryInstanceAttribute#value}. * * @memberof OffsetGeometryInstanceAttribute.prototype * * @type {ComponentDatatype} * @readonly * * @default {@link ComponentDatatype.FLOAT} */ componentDatatype: { get: function () { return ComponentDatatype$1.FLOAT; }, }, /** * The number of components in the attributes, i.e., {@link OffsetGeometryInstanceAttribute#value}. * * @memberof OffsetGeometryInstanceAttribute.prototype * * @type {Number} * @readonly * * @default 3 */ componentsPerAttribute: { get: function () { return 3; }, }, /** * When true and componentDatatype 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. * * @memberof OffsetGeometryInstanceAttribute.prototype * * @type {Boolean} * @readonly * * @default false */ normalize: { get: function () { return false; }, }, }); /** * Creates a new {@link OffsetGeometryInstanceAttribute} instance given the provided an enabled flag and {@link DistanceDisplayCondition}. * * @param {Cartesian3} offset The cartesian offset * @returns {OffsetGeometryInstanceAttribute} The new {@link OffsetGeometryInstanceAttribute} instance. */ OffsetGeometryInstanceAttribute.fromCartesian3 = function (offset) { //>>includeStart('debug', pragmas.debug); Check.defined("offset", offset); //>>includeEnd('debug'); return new OffsetGeometryInstanceAttribute(offset.x, offset.y, offset.z); }; /** * Converts a distance display condition to a typed array that can be used to assign a distance display condition attribute. * * @param {Cartesian3} offset The cartesian offset * @param {Float32Array} [result] The array to store the result in, if undefined a new instance will be created. * @returns {Float32Array} The modified result parameter or a new instance if result was undefined. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.modelMatrix = Cesium.OffsetGeometryInstanceAttribute.toValue(modelMatrix, attributes.modelMatrix); */ OffsetGeometryInstanceAttribute.toValue = function (offset, result) { //>>includeStart('debug', pragmas.debug); Check.defined("offset", offset); //>>includeEnd('debug'); if (!defined(result)) { result = new Float32Array([offset.x, offset.y, offset.z]); } result[0] = offset.x; result[1] = offset.y; result[2] = offset.z; return result; }; /** * Provides geocoding via a {@link https://opencagedata.com/|OpenCage} server. * @alias OpenCageGeocoderService * @constructor * * @param {Resource|String} url The endpoint to the OpenCage server. * @param {String} apiKey The OpenCage API Key. * @param {Object} [params] An object with the following properties (See https://opencagedata.com/api#forward-opt): * @param {Number} [params.abbrv] When set to 1 we attempt to abbreviate and shorten the formatted string we return. * @param {Number} [options.add_request] When set to 1 the various request parameters are added to the response for ease of debugging. * @param {String} [options.bounds] Provides the geocoder with a hint to the region that the query resides in. * @param {String} [options.countrycode] Restricts the results to the specified country or countries (as defined by the ISO 3166-1 Alpha 2 standard). * @param {String} [options.jsonp] Wraps the returned JSON with a function name. * @param {String} [options.language] An IETF format language code. * @param {Number} [options.limit] The maximum number of results we should return. * @param {Number} [options.min_confidence] An integer from 1-10. Only results with at least this confidence will be returned. * @param {Number} [options.no_annotations] When set to 1 results will not contain annotations. * @param {Number} [options.no_dedupe] When set to 1 results will not be deduplicated. * @param {Number} [options.no_record] When set to 1 the query contents are not logged. * @param {Number} [options.pretty] When set to 1 results are 'pretty' printed for easier reading. Useful for debugging. * @param {String} [options.proximity] Provides the geocoder with a hint to bias results in favour of those closer to the specified location (For example: 41.40139,2.12870). * * @example * // Configure a Viewer to use the OpenCage Geocoder * var viewer = new Cesium.Viewer('cesiumContainer', { * geocoder: new Cesium.OpenCageGeocoderService('https://api.opencagedata.com/geocode/v1/', '') * }); */ function OpenCageGeocoderService(url, apiKey, params) { //>>includeStart('debug', pragmas.debug); Check.defined("url", url); Check.defined("apiKey", apiKey); if (defined(params)) { Check.typeOf.object("params", params); } //>>includeEnd('debug'); url = Resource.createIfNeeded(url); url.appendForwardSlash(); url.setQueryParameters({ key: apiKey }); this._url = url; this._params = defaultValue(params, {}); } Object.defineProperties(OpenCageGeocoderService.prototype, { /** * The Resource used to access the OpenCage endpoint. * @type {Resource} * @memberof OpenCageGeocoderService.prototype * @readonly */ url: { get: function () { return this._url; }, }, /** * Optional params passed to OpenCage in order to customize geocoding * @type {Object} * @memberof OpenCageGeocoderService.prototype * @readonly */ params: { get: function () { return this._params; }, }, }); /** * @function * * @param {String} query The query to be sent to the geocoder service * @returns {Promise} */ OpenCageGeocoderService.prototype.geocode = function (query) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("query", query); //>>includeEnd('debug'); var resource = this._url.getDerivedResource({ url: "json", queryParameters: combine$2(this._params, { q: query }), }); return resource.fetchJson().then(function (response) { return response.results.map(function (resultObject) { var destination; var bounds = resultObject.bounds; if (defined(bounds)) { destination = Rectangle.fromDegrees( bounds.southwest.lng, bounds.southwest.lat, bounds.northeast.lng, bounds.northeast.lat ); } else { var lon = resultObject.geometry.lat; var lat = resultObject.geometry.lng; destination = Cartesian3.fromDegrees(lon, lat); } return { displayName: resultObject.formatted, destination: destination, }; }); }); }; /** * Static interface for types which can store their values as packed * elements in an array. These methods and properties are expected to be * defined on a constructor function. * * @interface Packable * * @see PackableForInterpolation */ var Packable = { /** * The number of elements used to pack the object into an array. * @type {Number} */ packedLength: undefined, /** * Stores the provided instance into the provided array. * @function * * @param {*} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. */ pack: DeveloperError.throwInstantiationError, /** * Retrieves an instance from a packed array. * @function * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Object} [result] The object into which to store the result. * @returns {Object} The modified result parameter or a new Object instance if one was not provided. */ unpack: DeveloperError.throwInstantiationError, }; /** * Static interface for {@link Packable} types which are interpolated in a * different representation than their packed value. These methods and * properties are expected to be defined on a constructor function. * * @namespace PackableForInterpolation * * @see Packable */ var PackableForInterpolation = { /** * The number of elements used to store the object into an array in its interpolatable form. * @type {Number} */ packedInterpolationLength: undefined, /** * Converts a packed array into a form suitable for interpolation. * @function * * @param {Number[]} packedArray The packed array. * @param {Number} [startingIndex=0] The index of the first element to be converted. * @param {Number} [lastIndex=packedArray.length] The index of the last element to be converted. * @param {Number[]} [result] The object into which to store the result. */ convertPackedArrayForInterpolation: DeveloperError.throwInstantiationError, /** * Retrieves an instance from a packed array converted with {@link PackableForInterpolation.convertPackedArrayForInterpolation}. * @function * * @param {Number[]} array The array previously packed for interpolation. * @param {Number[]} sourceArray The original packed array. * @param {Number} [startingIndex=0] The startingIndex used to convert the array. * @param {Number} [lastIndex=packedArray.length] The lastIndex used to convert the array. * @param {Object} [result] The object into which to store the result. * @returns {Object} The modified result parameter or a new Object instance if one was not provided. */ unpackInterpolationResult: DeveloperError.throwInstantiationError, }; /* This library rewrites the Canvas2D "measureText" function so that it returns a more complete metrics object. ** ----------------------------------------------------------------------------- CHANGELOG: 2012-01-21 - Whitespace handling added by Joe Turner (https://github.com/oampo) ** ----------------------------------------------------------------------------- */ /** @license fontmetrics.js - https://github.com/Pomax/fontmetrics.js Copyright (C) 2011 by Mike "Pomax" Kamermans Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **/ /* var NAME = "FontMetrics Library" var VERSION = "1-2012.0121.1300"; // if there is no getComputedStyle, this library won't work. if(!document.defaultView.getComputedStyle) { throw("ERROR: 'document.defaultView.getComputedStyle' not found. This library only works in browsers that can report computed CSS values."); } // store the old text metrics function on the Canvas2D prototype CanvasRenderingContext2D.prototype.measureTextWidth = CanvasRenderingContext2D.prototype.measureText; */ /** * shortcut function for getting computed CSS values */ var getCSSValue$1 = function(element, property) { return document.defaultView.getComputedStyle(element,null).getPropertyValue(property); }; /* // debug function var show = function(canvas, ctx, xstart, w, h, metrics) { document.body.appendChild(canvas); ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; ctx.beginPath(); ctx.moveTo(xstart,0); ctx.lineTo(xstart,h); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(xstart+metrics.bounds.maxx,0); ctx.lineTo(xstart+metrics.bounds.maxx,h); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0,h/2-metrics.ascent); ctx.lineTo(w,h/2-metrics.ascent); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0,h/2+metrics.descent); ctx.lineTo(w,h/2+metrics.descent); ctx.closePath(); ctx.stroke(); } */ /** * The new text metrics function */ var measureText = function(context2D, textstring, stroke, fill) { var metrics = context2D.measureText(textstring), fontFamily = getCSSValue$1(context2D.canvas,"font-family"), fontSize = getCSSValue$1(context2D.canvas,"font-size").replace("px",""), fontStyle = getCSSValue$1(context2D.canvas,"font-style"), fontWeight = getCSSValue$1(context2D.canvas,"font-weight"), isSpace = !(/\S/.test(textstring)); metrics.fontsize = fontSize; // for text lead values, we meaure a multiline text container. var leadDiv = document.createElement("div"); leadDiv.style.position = "absolute"; leadDiv.style.opacity = 0; leadDiv.style.font = fontStyle + " " + fontWeight + " " + fontSize + "px " + fontFamily; leadDiv.innerHTML = textstring + "
" + textstring; document.body.appendChild(leadDiv); // make some initial guess at the text leading (using the standard TeX ratio) metrics.leading = 1.2 * fontSize; // then we try to get the real value from the browser var leadDivHeight = getCSSValue$1(leadDiv,"height"); leadDivHeight = leadDivHeight.replace("px",""); if (leadDivHeight >= fontSize * 2) { metrics.leading = (leadDivHeight/2) | 0; } document.body.removeChild(leadDiv); // if we're not dealing with white space, we can compute metrics if (!isSpace) { // Have characters, so measure the text var canvas = document.createElement("canvas"); var padding = 100; canvas.width = metrics.width + padding; canvas.height = 3*fontSize; canvas.style.opacity = 1; canvas.style.fontFamily = fontFamily; canvas.style.fontSize = fontSize; canvas.style.fontStyle = fontStyle; canvas.style.fontWeight = fontWeight; var ctx = canvas.getContext("2d"); ctx.font = fontStyle + " " + fontWeight + " " + fontSize + "px " + fontFamily; var w = canvas.width, h = canvas.height, baseline = h/2; // Set all canvas pixeldata values to 255, with all the content // data being 0. This lets us scan for data[i] != 255. ctx.fillStyle = "white"; ctx.fillRect(-1, -1, w + 2, h + 2); if (stroke) { ctx.strokeStyle = "black"; ctx.lineWidth = context2D.lineWidth; ctx.strokeText(textstring, (padding / 2), baseline); } if (fill) { ctx.fillStyle = "black"; ctx.fillText(textstring, padding / 2, baseline); } var pixelData = ctx.getImageData(0, 0, w, h).data; // canvas pixel data is w*4 by h*4, because R, G, B and A are separate, // consecutive values in the array, rather than stored as 32 bit ints. var i = 0, w4 = w * 4, len = pixelData.length; // Finding the ascent uses a normal, forward scanline while (++i < len && pixelData[i] === 255) {} var ascent = (i/w4)|0; // Finding the descent uses a reverse scanline i = len - 1; while (--i > 0 && pixelData[i] === 255) {} var descent = (i/w4)|0; // find the min-x coordinate for(i = 0; i=len) { i = (i-len) + 4; }} var minx = ((i%w4)/4) | 0; // find the max-x coordinate var step = 1; for(i = len-3; i>=0 && pixelData[i] === 255; ) { i -= w4; if(i<0) { i = (len - 3) - (step++)*4; }} var maxx = ((i%w4)/4) + 1 | 0; // set font metrics metrics.ascent = (baseline - ascent); metrics.descent = (descent - baseline); metrics.bounds = { minx: minx - (padding/2), maxx: maxx - (padding/2), miny: 0, maxy: descent-ascent }; metrics.height = 1+(descent - ascent); } // if we ARE dealing with whitespace, most values will just be zero. else { // Only whitespace, so we can't measure the text metrics.ascent = 0; metrics.descent = 0; metrics.bounds = { minx: 0, maxx: metrics.width, // Best guess miny: 0, maxy: 0 }; metrics.height = 0; } return metrics; }; var imageSmoothingEnabledName; /** * Writes the given text into a new canvas. The canvas will be sized to fit the text. * If text is blank, returns undefined. * * @param {String} text The text to write. * @param {Object} [options] Object with the following properties: * @param {String} [options.font='10px sans-serif'] The CSS font to use. * @param {String} [options.textBaseline='bottom'] The baseline of the text. * @param {Boolean} [options.fill=true] Whether to fill the text. * @param {Boolean} [options.stroke=false] Whether to stroke the text. * @param {Color} [options.fillColor=Color.WHITE] The fill color. * @param {Color} [options.strokeColor=Color.BLACK] The stroke color. * @param {Number} [options.strokeWidth=1] The stroke width. * @param {Color} [options.backgroundColor=Color.TRANSPARENT] The background color of the canvas. * @param {Number} [options.padding=0] The pixel size of the padding to add around the text. * @returns {HTMLCanvasElement|undefined} A new canvas with the given text drawn into it. The dimensions object * from measureText will also be added to the returned canvas. If text is * blank, returns undefined. * @function writeTextToCanvas */ function writeTextToCanvas(text, options) { //>>includeStart('debug', pragmas.debug); if (!defined(text)) { throw new DeveloperError("text is required."); } //>>includeEnd('debug'); if (text === "") { return undefined; } options = defaultValue(options, defaultValue.EMPTY_OBJECT); var font = defaultValue(options.font, "10px sans-serif"); var stroke = defaultValue(options.stroke, false); var fill = defaultValue(options.fill, true); var strokeWidth = defaultValue(options.strokeWidth, 1); var backgroundColor = defaultValue( options.backgroundColor, Color.TRANSPARENT ); var padding = defaultValue(options.padding, 0); var doublePadding = padding * 2.0; var canvas = document.createElement("canvas"); canvas.width = 1; canvas.height = 1; canvas.style.font = font; var context2D = canvas.getContext("2d"); if (!defined(imageSmoothingEnabledName)) { if (defined(context2D.imageSmoothingEnabled)) { imageSmoothingEnabledName = "imageSmoothingEnabled"; } else if (defined(context2D.mozImageSmoothingEnabled)) { imageSmoothingEnabledName = "mozImageSmoothingEnabled"; } else if (defined(context2D.webkitImageSmoothingEnabled)) { imageSmoothingEnabledName = "webkitImageSmoothingEnabled"; } else if (defined(context2D.msImageSmoothingEnabled)) { imageSmoothingEnabledName = "msImageSmoothingEnabled"; } } context2D.font = font; context2D.lineJoin = "round"; context2D.lineWidth = strokeWidth; context2D[imageSmoothingEnabledName] = false; // textBaseline needs to be set before the measureText call. It won't work otherwise. // It's magic. context2D.textBaseline = defaultValue(options.textBaseline, "bottom"); // in order for measureText to calculate style, the canvas has to be // (temporarily) added to the DOM. canvas.style.visibility = "hidden"; document.body.appendChild(canvas); var dimensions = measureText(context2D, text, stroke, fill); canvas.dimensions = dimensions; document.body.removeChild(canvas); canvas.style.visibility = ""; //Some characters, such as the letter j, have a non-zero starting position. //This value is used for kerning later, but we need to take it into account //now in order to draw the text completely on the canvas var x = -dimensions.bounds.minx; //Expand the width to include the starting position. var width = Math.ceil(dimensions.width) + x + doublePadding; //While the height of the letter is correct, we need to adjust //where we start drawing it so that letters like j and y properly dip //below the line. var height = dimensions.height + doublePadding; var baseline = height - dimensions.ascent + padding; var y = height - baseline + doublePadding; canvas.width = width; canvas.height = height; // Properties must be explicitly set again after changing width and height context2D.font = font; context2D.lineJoin = "round"; context2D.lineWidth = strokeWidth; context2D[imageSmoothingEnabledName] = false; // Draw background if (backgroundColor !== Color.TRANSPARENT) { context2D.fillStyle = backgroundColor.toCssColorString(); context2D.fillRect(0, 0, canvas.width, canvas.height); } if (stroke) { var strokeColor = defaultValue(options.strokeColor, Color.BLACK); context2D.strokeStyle = strokeColor.toCssColorString(); context2D.strokeText(text, x + padding, y); } if (fill) { var fillColor = defaultValue(options.fillColor, Color.WHITE); context2D.fillStyle = fillColor.toCssColorString(); context2D.fillText(text, x + padding, y); } return canvas; } /** * A utility class for generating custom map pins as canvas elements. *

*
*
* Example pins generated using both the maki icon set, which ships with Cesium, and single character text. *
* * @alias PinBuilder * @constructor * * @demo {@link https://sandcastle.cesium.com/index.html?src=Map%20Pins.html|Cesium Sandcastle PinBuilder Demo} */ function PinBuilder() { this._cache = {}; } /** * Creates an empty pin of the specified color and size. * * @param {Color} color The color of the pin. * @param {Number} size The size of the pin, in pixels. * @returns {HTMLCanvasElement} The canvas element that represents the generated pin. */ PinBuilder.prototype.fromColor = function (color, size) { //>>includeStart('debug', pragmas.debug); if (!defined(color)) { throw new DeveloperError("color is required"); } if (!defined(size)) { throw new DeveloperError("size is required"); } //>>includeEnd('debug'); return createPin(undefined, undefined, color, size, this._cache); }; /** * Creates a pin with the specified icon, color, and size. * * @param {Resource|String} url The url of the image to be stamped onto the pin. * @param {Color} color The color of the pin. * @param {Number} size The size of the pin, in pixels. * @returns {HTMLCanvasElement|Promise.} The canvas element or a Promise to the canvas element that represents the generated pin. */ PinBuilder.prototype.fromUrl = function (url, color, size) { //>>includeStart('debug', pragmas.debug); if (!defined(url)) { throw new DeveloperError("url is required"); } if (!defined(color)) { throw new DeveloperError("color is required"); } if (!defined(size)) { throw new DeveloperError("size is required"); } //>>includeEnd('debug'); return createPin(url, undefined, color, size, this._cache); }; /** * Creates a pin with the specified {@link https://www.mapbox.com/maki/|maki} icon identifier, color, and size. * * @param {String} id The id of the maki icon to be stamped onto the pin. * @param {Color} color The color of the pin. * @param {Number} size The size of the pin, in pixels. * @returns {HTMLCanvasElement|Promise.} The canvas element or a Promise to the canvas element that represents the generated pin. */ PinBuilder.prototype.fromMakiIconId = function (id, color, size) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required"); } if (!defined(color)) { throw new DeveloperError("color is required"); } if (!defined(size)) { throw new DeveloperError("size is required"); } //>>includeEnd('debug'); return createPin( buildModuleUrl("Assets/Textures/maki/" + encodeURIComponent(id) + ".png"), undefined, color, size, this._cache ); }; /** * Creates a pin with the specified text, color, and size. The text will be sized to be as large as possible * while still being contained completely within the pin. * * @param {String} text The text to be stamped onto the pin. * @param {Color} color The color of the pin. * @param {Number} size The size of the pin, in pixels. * @returns {HTMLCanvasElement} The canvas element that represents the generated pin. */ PinBuilder.prototype.fromText = function (text, color, size) { //>>includeStart('debug', pragmas.debug); if (!defined(text)) { throw new DeveloperError("text is required"); } if (!defined(color)) { throw new DeveloperError("color is required"); } if (!defined(size)) { throw new DeveloperError("size is required"); } //>>includeEnd('debug'); return createPin(undefined, text, color, size, this._cache); }; var colorScratch$7 = new Color(); //This function (except for the 3 commented lines) was auto-generated from an online tool, //http://www.professorcloud.com/svg-to-canvas/, using Assets/Textures/pin.svg as input. //The reason we simply can't load and draw the SVG directly to the canvas is because //it taints the canvas in Internet Explorer (and possibly some other browsers); making //it impossible to create a WebGL texture from the result. function drawPin(context2D, color, size) { context2D.save(); context2D.scale(size / 24, size / 24); //Added to auto-generated code to scale up to desired size. context2D.fillStyle = color.toCssColorString(); //Modified from auto-generated code. context2D.strokeStyle = color.brighten(0.6, colorScratch$7).toCssColorString(); //Modified from auto-generated code. context2D.lineWidth = 0.846; context2D.beginPath(); context2D.moveTo(6.72, 0.422); context2D.lineTo(17.28, 0.422); context2D.bezierCurveTo(18.553, 0.422, 19.577, 1.758, 19.577, 3.415); context2D.lineTo(19.577, 10.973); context2D.bezierCurveTo(19.577, 12.63, 18.553, 13.966, 17.282, 13.966); context2D.lineTo(14.386, 14.008); context2D.lineTo(11.826, 23.578); context2D.lineTo(9.614, 14.008); context2D.lineTo(6.719, 13.965); context2D.bezierCurveTo(5.446, 13.983, 4.422, 12.629, 4.422, 10.972); context2D.lineTo(4.422, 3.416); context2D.bezierCurveTo(4.423, 1.76, 5.447, 0.423, 6.718, 0.423); context2D.closePath(); context2D.fill(); context2D.stroke(); context2D.restore(); } //This function takes an image or canvas and uses it as a template //to "stamp" the pin with a white image outlined in black. The color //values of the input image are ignored completely and only the alpha //values are used. function drawIcon(context2D, image, size) { //Size is the largest image that looks good inside of pin box. var imageSize = size / 2.5; var sizeX = imageSize; var sizeY = imageSize; if (image.width > image.height) { sizeY = imageSize * (image.height / image.width); } else if (image.width < image.height) { sizeX = imageSize * (image.width / image.height); } //x and y are the center of the pin box var x = Math.round((size - sizeX) / 2); var y = Math.round((7 / 24) * size - sizeY / 2); context2D.globalCompositeOperation = "destination-out"; context2D.drawImage(image, x - 1, y, sizeX, sizeY); context2D.drawImage(image, x, y - 1, sizeX, sizeY); context2D.drawImage(image, x + 1, y, sizeX, sizeY); context2D.drawImage(image, x, y + 1, sizeX, sizeY); context2D.globalCompositeOperation = "destination-over"; context2D.fillStyle = Color.BLACK.toCssColorString(); context2D.fillRect(x - 1, y - 1, sizeX + 2, sizeY + 2); context2D.globalCompositeOperation = "destination-out"; context2D.drawImage(image, x, y, sizeX, sizeY); context2D.globalCompositeOperation = "destination-over"; context2D.fillStyle = Color.WHITE.toCssColorString(); context2D.fillRect(x - 1, y - 2, sizeX + 2, sizeY + 2); } var stringifyScratch = new Array(4); function createPin(url, label, color, size, cache) { //Use the parameters as a unique ID for caching. stringifyScratch[0] = url; stringifyScratch[1] = label; stringifyScratch[2] = color; stringifyScratch[3] = size; var id = JSON.stringify(stringifyScratch); var item = cache[id]; if (defined(item)) { return item; } var canvas = document.createElement("canvas"); canvas.width = size; canvas.height = size; var context2D = canvas.getContext("2d"); drawPin(context2D, color, size); if (defined(url)) { var resource = Resource.createIfNeeded(url); //If we have an image url, load it and then stamp the pin. var promise = resource.fetchImage().then(function (image) { drawIcon(context2D, image, size); cache[id] = canvas; return canvas; }); cache[id] = promise; return promise; } else if (defined(label)) { //If we have a label, write it to a canvas and then stamp the pin. var image = writeTextToCanvas(label, { font: "bold " + size + "px sans-serif", }); drawIcon(context2D, image, size); } cache[id] = canvas; return canvas; } /** * The data type of a pixel. * * @enum {Number} * @see PostProcessStage */ var PixelDatatype = { UNSIGNED_BYTE: WebGLConstants$1.UNSIGNED_BYTE, UNSIGNED_SHORT: WebGLConstants$1.UNSIGNED_SHORT, UNSIGNED_INT: WebGLConstants$1.UNSIGNED_INT, FLOAT: WebGLConstants$1.FLOAT, HALF_FLOAT: WebGLConstants$1.HALF_FLOAT_OES, UNSIGNED_INT_24_8: WebGLConstants$1.UNSIGNED_INT_24_8, UNSIGNED_SHORT_4_4_4_4: WebGLConstants$1.UNSIGNED_SHORT_4_4_4_4, UNSIGNED_SHORT_5_5_5_1: WebGLConstants$1.UNSIGNED_SHORT_5_5_5_1, UNSIGNED_SHORT_5_6_5: WebGLConstants$1.UNSIGNED_SHORT_5_6_5, }; /** @private */ PixelDatatype.toWebGLConstant = function (pixelDatatype, context) { switch (pixelDatatype) { case PixelDatatype.UNSIGNED_BYTE: return WebGLConstants$1.UNSIGNED_BYTE; case PixelDatatype.UNSIGNED_SHORT: return WebGLConstants$1.UNSIGNED_SHORT; case PixelDatatype.UNSIGNED_INT: return WebGLConstants$1.UNSIGNED_INT; case PixelDatatype.FLOAT: return WebGLConstants$1.FLOAT; case PixelDatatype.HALF_FLOAT: return context.webgl2 ? WebGLConstants$1.HALF_FLOAT : WebGLConstants$1.HALF_FLOAT_OES; case PixelDatatype.UNSIGNED_INT_24_8: return WebGLConstants$1.UNSIGNED_INT_24_8; case PixelDatatype.UNSIGNED_SHORT_4_4_4_4: return WebGLConstants$1.UNSIGNED_SHORT_4_4_4_4; case PixelDatatype.UNSIGNED_SHORT_5_5_5_1: return WebGLConstants$1.UNSIGNED_SHORT_5_5_5_1; case PixelDatatype.UNSIGNED_SHORT_5_6_5: return PixelDatatype.UNSIGNED_SHORT_5_6_5; } }; /** @private */ PixelDatatype.isPacked = function (pixelDatatype) { return ( pixelDatatype === PixelDatatype.UNSIGNED_INT_24_8 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_4_4_4_4 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_5_5_1 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_6_5 ); }; /** @private */ PixelDatatype.sizeInBytes = function (pixelDatatype) { switch (pixelDatatype) { case PixelDatatype.UNSIGNED_BYTE: return 1; case PixelDatatype.UNSIGNED_SHORT: case PixelDatatype.UNSIGNED_SHORT_4_4_4_4: case PixelDatatype.UNSIGNED_SHORT_5_5_5_1: case PixelDatatype.UNSIGNED_SHORT_5_6_5: case PixelDatatype.HALF_FLOAT: return 2; case PixelDatatype.UNSIGNED_INT: case PixelDatatype.FLOAT: case PixelDatatype.UNSIGNED_INT_24_8: return 4; } }; /** @private */ PixelDatatype.validate = function (pixelDatatype) { return ( pixelDatatype === PixelDatatype.UNSIGNED_BYTE || pixelDatatype === PixelDatatype.UNSIGNED_SHORT || pixelDatatype === PixelDatatype.UNSIGNED_INT || pixelDatatype === PixelDatatype.FLOAT || pixelDatatype === PixelDatatype.HALF_FLOAT || pixelDatatype === PixelDatatype.UNSIGNED_INT_24_8 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_4_4_4_4 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_5_5_1 || pixelDatatype === PixelDatatype.UNSIGNED_SHORT_5_6_5 ); }; var PixelDatatype$1 = Object.freeze(PixelDatatype); /** * The format of a pixel, i.e., the number of components it has and what they represent. * * @enum {Number} */ var PixelFormat = { /** * A pixel format containing a depth value. * * @type {Number} * @constant */ DEPTH_COMPONENT: WebGLConstants$1.DEPTH_COMPONENT, /** * A pixel format containing a depth and stencil value, most often used with {@link PixelDatatype.UNSIGNED_INT_24_8}. * * @type {Number} * @constant */ DEPTH_STENCIL: WebGLConstants$1.DEPTH_STENCIL, /** * A pixel format containing an alpha channel. * * @type {Number} * @constant */ ALPHA: WebGLConstants$1.ALPHA, /** * A pixel format containing red, green, and blue channels. * * @type {Number} * @constant */ RGB: WebGLConstants$1.RGB, /** * A pixel format containing red, green, blue, and alpha channels. * * @type {Number} * @constant */ RGBA: WebGLConstants$1.RGBA, /** * A pixel format containing a luminance (intensity) channel. * * @type {Number} * @constant */ LUMINANCE: WebGLConstants$1.LUMINANCE, /** * A pixel format containing luminance (intensity) and alpha channels. * * @type {Number} * @constant */ LUMINANCE_ALPHA: WebGLConstants$1.LUMINANCE_ALPHA, /** * A pixel format containing red, green, and blue channels that is DXT1 compressed. * * @type {Number} * @constant */ RGB_DXT1: WebGLConstants$1.COMPRESSED_RGB_S3TC_DXT1_EXT, /** * A pixel format containing red, green, blue, and alpha channels that is DXT1 compressed. * * @type {Number} * @constant */ RGBA_DXT1: WebGLConstants$1.COMPRESSED_RGBA_S3TC_DXT1_EXT, /** * A pixel format containing red, green, blue, and alpha channels that is DXT3 compressed. * * @type {Number} * @constant */ RGBA_DXT3: WebGLConstants$1.COMPRESSED_RGBA_S3TC_DXT3_EXT, /** * A pixel format containing red, green, blue, and alpha channels that is DXT5 compressed. * * @type {Number} * @constant */ RGBA_DXT5: WebGLConstants$1.COMPRESSED_RGBA_S3TC_DXT5_EXT, /** * A pixel format containing red, green, and blue channels that is PVR 4bpp compressed. * * @type {Number} * @constant */ RGB_PVRTC_4BPPV1: WebGLConstants$1.COMPRESSED_RGB_PVRTC_4BPPV1_IMG, /** * A pixel format containing red, green, and blue channels that is PVR 2bpp compressed. * * @type {Number} * @constant */ RGB_PVRTC_2BPPV1: WebGLConstants$1.COMPRESSED_RGB_PVRTC_2BPPV1_IMG, /** * A pixel format containing red, green, blue, and alpha channels that is PVR 4bpp compressed. * * @type {Number} * @constant */ RGBA_PVRTC_4BPPV1: WebGLConstants$1.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, /** * A pixel format containing red, green, blue, and alpha channels that is PVR 2bpp compressed. * * @type {Number} * @constant */ RGBA_PVRTC_2BPPV1: WebGLConstants$1.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, /** * A pixel format containing red, green, and blue channels that is ETC1 compressed. * * @type {Number} * @constant */ RGB_ETC1: WebGLConstants$1.COMPRESSED_RGB_ETC1_WEBGL, }; /** * @private */ PixelFormat.componentsLength = function (pixelFormat) { switch (pixelFormat) { case PixelFormat.RGB: return 3; case PixelFormat.RGBA: return 4; case PixelFormat.LUMINANCE_ALPHA: return 2; case PixelFormat.ALPHA: case PixelFormat.LUMINANCE: return 1; default: return 1; } }; /** * @private */ PixelFormat.validate = function (pixelFormat) { return ( pixelFormat === PixelFormat.DEPTH_COMPONENT || pixelFormat === PixelFormat.DEPTH_STENCIL || pixelFormat === PixelFormat.ALPHA || pixelFormat === PixelFormat.RGB || pixelFormat === PixelFormat.RGBA || pixelFormat === PixelFormat.LUMINANCE || pixelFormat === PixelFormat.LUMINANCE_ALPHA || pixelFormat === PixelFormat.RGB_DXT1 || pixelFormat === PixelFormat.RGBA_DXT1 || pixelFormat === PixelFormat.RGBA_DXT3 || pixelFormat === PixelFormat.RGBA_DXT5 || pixelFormat === PixelFormat.RGB_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGB_PVRTC_2BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_2BPPV1 || pixelFormat === PixelFormat.RGB_ETC1 ); }; /** * @private */ PixelFormat.isColorFormat = function (pixelFormat) { return ( pixelFormat === PixelFormat.ALPHA || pixelFormat === PixelFormat.RGB || pixelFormat === PixelFormat.RGBA || pixelFormat === PixelFormat.LUMINANCE || pixelFormat === PixelFormat.LUMINANCE_ALPHA ); }; /** * @private */ PixelFormat.isDepthFormat = function (pixelFormat) { return ( pixelFormat === PixelFormat.DEPTH_COMPONENT || pixelFormat === PixelFormat.DEPTH_STENCIL ); }; /** * @private */ PixelFormat.isCompressedFormat = function (pixelFormat) { return ( pixelFormat === PixelFormat.RGB_DXT1 || pixelFormat === PixelFormat.RGBA_DXT1 || pixelFormat === PixelFormat.RGBA_DXT3 || pixelFormat === PixelFormat.RGBA_DXT5 || pixelFormat === PixelFormat.RGB_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGB_PVRTC_2BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_2BPPV1 || pixelFormat === PixelFormat.RGB_ETC1 ); }; /** * @private */ PixelFormat.isDXTFormat = function (pixelFormat) { return ( pixelFormat === PixelFormat.RGB_DXT1 || pixelFormat === PixelFormat.RGBA_DXT1 || pixelFormat === PixelFormat.RGBA_DXT3 || pixelFormat === PixelFormat.RGBA_DXT5 ); }; /** * @private */ PixelFormat.isPVRTCFormat = function (pixelFormat) { return ( pixelFormat === PixelFormat.RGB_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGB_PVRTC_2BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_4BPPV1 || pixelFormat === PixelFormat.RGBA_PVRTC_2BPPV1 ); }; /** * @private */ PixelFormat.isETC1Format = function (pixelFormat) { return pixelFormat === PixelFormat.RGB_ETC1; }; /** * @private */ PixelFormat.compressedTextureSizeInBytes = function ( pixelFormat, width, height ) { switch (pixelFormat) { case PixelFormat.RGB_DXT1: case PixelFormat.RGBA_DXT1: case PixelFormat.RGB_ETC1: return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; case PixelFormat.RGBA_DXT3: case PixelFormat.RGBA_DXT5: return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; case PixelFormat.RGB_PVRTC_4BPPV1: case PixelFormat.RGBA_PVRTC_4BPPV1: return Math.floor((Math.max(width, 8) * Math.max(height, 8) * 4 + 7) / 8); case PixelFormat.RGB_PVRTC_2BPPV1: case PixelFormat.RGBA_PVRTC_2BPPV1: return Math.floor( (Math.max(width, 16) * Math.max(height, 8) * 2 + 7) / 8 ); default: return 0; } }; /** * @private */ PixelFormat.textureSizeInBytes = function ( pixelFormat, pixelDatatype, width, height ) { var componentsLength = PixelFormat.componentsLength(pixelFormat); if (PixelDatatype$1.isPacked(pixelDatatype)) { componentsLength = 1; } return ( componentsLength * PixelDatatype$1.sizeInBytes(pixelDatatype) * width * height ); }; /** * @private */ PixelFormat.alignmentInBytes = function (pixelFormat, pixelDatatype, width) { var mod = PixelFormat.textureSizeInBytes(pixelFormat, pixelDatatype, width, 1) % 4; return mod === 0 ? 4 : mod === 2 ? 2 : 1; }; /** * @private */ PixelFormat.createTypedArray = function ( pixelFormat, pixelDatatype, width, height ) { var constructor; var sizeInBytes = PixelDatatype$1.sizeInBytes(pixelDatatype); if (sizeInBytes === Uint8Array.BYTES_PER_ELEMENT) { constructor = Uint8Array; } else if (sizeInBytes === Uint16Array.BYTES_PER_ELEMENT) { constructor = Uint16Array; } else if ( sizeInBytes === Float32Array.BYTES_PER_ELEMENT && pixelDatatype === PixelDatatype$1.FLOAT ) { constructor = Float32Array; } else { constructor = Uint32Array; } var size = PixelFormat.componentsLength(pixelFormat) * width * height; return new constructor(size); }; /** * @private */ PixelFormat.flipY = function ( bufferView, pixelFormat, pixelDatatype, width, height ) { if (height === 1) { return bufferView; } var flipped = PixelFormat.createTypedArray( pixelFormat, pixelDatatype, width, height ); var numberOfComponents = PixelFormat.componentsLength(pixelFormat); var textureWidth = width * numberOfComponents; for (var i = 0; i < height; ++i) { var row = i * width * numberOfComponents; var flippedRow = (height - i - 1) * width * numberOfComponents; for (var j = 0; j < textureWidth; ++j) { flipped[flippedRow + j] = bufferView[row + j]; } } return flipped; }; /** * @private */ PixelFormat.toInternalFormat = function (pixelFormat, pixelDatatype, context) { // WebGL 1 require internalFormat to be the same as PixelFormat if (!context.webgl2) { return pixelFormat; } // Convert pixelFormat to correct internalFormat for WebGL 2 if (pixelFormat === PixelFormat.DEPTH_STENCIL) { return WebGLConstants$1.DEPTH24_STENCIL8; } if (pixelFormat === PixelFormat.DEPTH_COMPONENT) { if (pixelDatatype === PixelDatatype$1.UNSIGNED_SHORT) { return WebGLConstants$1.DEPTH_COMPONENT16; } else if (pixelDatatype === PixelDatatype$1.UNSIGNED_INT) { return WebGLConstants$1.DEPTH_COMPONENT24; } } if (pixelDatatype === PixelDatatype$1.FLOAT) { switch (pixelFormat) { case PixelFormat.RGBA: return WebGLConstants$1.RGBA32F; case PixelFormat.RGB: return WebGLConstants$1.RGB32F; case PixelFormat.RG: return WebGLConstants$1.RG32F; case PixelFormat.R: return WebGLConstants$1.R32F; } } if (pixelDatatype === PixelDatatype$1.HALF_FLOAT) { switch (pixelFormat) { case PixelFormat.RGBA: return WebGLConstants$1.RGBA16F; case PixelFormat.RGB: return WebGLConstants$1.RGB16F; case PixelFormat.RG: return WebGLConstants$1.RG16F; case PixelFormat.R: return WebGLConstants$1.R16F; } } return pixelFormat; }; var PixelFormat$1 = Object.freeze(PixelFormat); /** * Describes geometry representing a plane centered at the origin, with a unit width and length. * * @alias PlaneGeometry * @constructor * * @param {Object} [options] Object with the following properties: * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @example * var planeGeometry = new Cesium.PlaneGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY * }); */ function PlaneGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); this._vertexFormat = vertexFormat; this._workerName = "createPlaneGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ PlaneGeometry.packedLength = VertexFormat.packedLength; /** * Stores the provided instance into the provided array. * * @param {PlaneGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PlaneGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); VertexFormat.pack(value._vertexFormat, array, startingIndex); return array; }; var scratchVertexFormat$5 = new VertexFormat(); var scratchOptions$9 = { vertexFormat: scratchVertexFormat$5, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PlaneGeometry} [result] The object into which to store the result. * @returns {PlaneGeometry} The modified result parameter or a new PlaneGeometry instance if one was not provided. */ PlaneGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$5 ); if (!defined(result)) { return new PlaneGeometry(scratchOptions$9); } result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); return result; }; var min$1 = new Cartesian3(-0.5, -0.5, 0.0); var max$1 = new Cartesian3(0.5, 0.5, 0.0); /** * Computes the geometric representation of a plane, including its vertices, indices, and a bounding sphere. * * @param {PlaneGeometry} planeGeometry A description of the plane. * @returns {Geometry|undefined} The computed vertices and indices. */ PlaneGeometry.createGeometry = function (planeGeometry) { var vertexFormat = planeGeometry._vertexFormat; var attributes = new GeometryAttributes(); var indices; var positions; if (vertexFormat.position) { // 4 corner points. Duplicated 3 times each for each incident edge/face. positions = new Float64Array(4 * 3); // +z face positions[0] = min$1.x; positions[1] = min$1.y; positions[2] = 0.0; positions[3] = max$1.x; positions[4] = min$1.y; positions[5] = 0.0; positions[6] = max$1.x; positions[7] = max$1.y; positions[8] = 0.0; positions[9] = min$1.x; positions[10] = max$1.y; positions[11] = 0.0; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); if (vertexFormat.normal) { var normals = new Float32Array(4 * 3); // +z face normals[0] = 0.0; normals[1] = 0.0; normals[2] = 1.0; normals[3] = 0.0; normals[4] = 0.0; normals[5] = 1.0; normals[6] = 0.0; normals[7] = 0.0; normals[8] = 1.0; normals[9] = 0.0; normals[10] = 0.0; normals[11] = 1.0; attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.st) { var texCoords = new Float32Array(4 * 2); // +z face texCoords[0] = 0.0; texCoords[1] = 0.0; texCoords[2] = 1.0; texCoords[3] = 0.0; texCoords[4] = 1.0; texCoords[5] = 1.0; texCoords[6] = 0.0; texCoords[7] = 1.0; attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: texCoords, }); } if (vertexFormat.tangent) { var tangents = new Float32Array(4 * 3); // +z face tangents[0] = 1.0; tangents[1] = 0.0; tangents[2] = 0.0; tangents[3] = 1.0; tangents[4] = 0.0; tangents[5] = 0.0; tangents[6] = 1.0; tangents[7] = 0.0; tangents[8] = 0.0; tangents[9] = 1.0; tangents[10] = 0.0; tangents[11] = 0.0; attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { var bitangents = new Float32Array(4 * 3); // +z face bitangents[0] = 0.0; bitangents[1] = 1.0; bitangents[2] = 0.0; bitangents[3] = 0.0; bitangents[4] = 1.0; bitangents[5] = 0.0; bitangents[6] = 0.0; bitangents[7] = 1.0; bitangents[8] = 0.0; bitangents[9] = 0.0; bitangents[10] = 1.0; bitangents[11] = 0.0; attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } // 2 triangles indices = new Uint16Array(2 * 3); // +z face indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: new BoundingSphere(Cartesian3.ZERO, Math.sqrt(2.0)), }); }; /** * Describes geometry representing the outline of a plane centered at the origin, with a unit width and length. * * @alias PlaneOutlineGeometry * @constructor * */ function PlaneOutlineGeometry() { this._workerName = "createPlaneOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ PlaneOutlineGeometry.packedLength = 0; /** * Stores the provided instance into the provided array. * * @param {PlaneOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * * @returns {Number[]} The array that was packed into */ PlaneOutlineGeometry.pack = function (value, array) { //>>includeStart('debug', pragmas.debug); Check.defined("value", value); Check.defined("array", array); //>>includeEnd('debug'); return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PlaneOutlineGeometry} [result] The object into which to store the result. * @returns {PlaneOutlineGeometry} The modified result parameter or a new PlaneOutlineGeometry instance if one was not provided. */ PlaneOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); if (!defined(result)) { return new PlaneOutlineGeometry(); } return result; }; var min = new Cartesian3(-0.5, -0.5, 0.0); var max = new Cartesian3(0.5, 0.5, 0.0); /** * Computes the geometric representation of an outline of a plane, including its vertices, indices, and a bounding sphere. * * @returns {Geometry|undefined} The computed vertices and indices. */ PlaneOutlineGeometry.createGeometry = function () { var attributes = new GeometryAttributes(); var indices = new Uint16Array(4 * 2); var positions = new Float64Array(4 * 3); positions[0] = min.x; positions[1] = min.y; positions[2] = min.z; positions[3] = max.x; positions[4] = min.y; positions[5] = min.z; positions[6] = max.x; positions[7] = max.y; positions[8] = min.z; positions[9] = min.x; positions[10] = max.y; positions[11] = min.z; attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); indices[0] = 0; indices[1] = 1; indices[2] = 1; indices[3] = 2; indices[4] = 2; indices[5] = 3; indices[6] = 3; indices[7] = 0; return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: new BoundingSphere(Cartesian3.ZERO, Math.sqrt(2.0)), }); }; var scratchCarto1 = new Cartographic(); var scratchCarto2 = new Cartographic(); function adjustPosHeightsForNormal(position, p1, p2, ellipsoid) { var carto1 = ellipsoid.cartesianToCartographic(position, scratchCarto1); var height = carto1.height; var p1Carto = ellipsoid.cartesianToCartographic(p1, scratchCarto2); p1Carto.height = height; ellipsoid.cartographicToCartesian(p1Carto, p1); var p2Carto = ellipsoid.cartesianToCartographic(p2, scratchCarto2); p2Carto.height = height - 100; ellipsoid.cartographicToCartesian(p2Carto, p2); } var scratchBoundingRectangle = new BoundingRectangle(); var scratchPosition$9 = new Cartesian3(); var scratchNormal$2 = new Cartesian3(); var scratchTangent$1 = new Cartesian3(); var scratchBitangent$1 = new Cartesian3(); var p1Scratch = new Cartesian3(); var p2Scratch = new Cartesian3(); var scratchPerPosNormal = new Cartesian3(); var scratchPerPosTangent = new Cartesian3(); var scratchPerPosBitangent = new Cartesian3(); var appendTextureCoordinatesOrigin = new Cartesian2(); var appendTextureCoordinatesCartesian2 = new Cartesian2(); var appendTextureCoordinatesCartesian3 = new Cartesian3(); var appendTextureCoordinatesQuaternion = new Quaternion(); var appendTextureCoordinatesMatrix3 = new Matrix3(); var tangentMatrixScratch = new Matrix3(); function computeAttributes$2(options) { var vertexFormat = options.vertexFormat; var geometry = options.geometry; var shadowVolume = options.shadowVolume; var flatPositions = geometry.attributes.position.values; var length = flatPositions.length; var wall = options.wall; var top = options.top || wall; var bottom = options.bottom || wall; if ( vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent || shadowVolume ) { // PERFORMANCE_IDEA: Compute before subdivision, then just interpolate during subdivision. // PERFORMANCE_IDEA: Compute with createGeometryFromPositions() for fast path when there's no holes. var boundingRectangle = options.boundingRectangle; var tangentPlane = options.tangentPlane; var ellipsoid = options.ellipsoid; var stRotation = options.stRotation; var perPositionHeight = options.perPositionHeight; var origin = appendTextureCoordinatesOrigin; origin.x = boundingRectangle.x; origin.y = boundingRectangle.y; var textureCoordinates = vertexFormat.st ? new Float32Array(2 * (length / 3)) : undefined; var normals; if (vertexFormat.normal) { if (perPositionHeight && top && !wall) { normals = geometry.attributes.normal.values; } else { normals = new Float32Array(length); } } var tangents = vertexFormat.tangent ? new Float32Array(length) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(length) : undefined; var extrudeNormals = shadowVolume ? new Float32Array(length) : undefined; var textureCoordIndex = 0; var attrIndex = 0; var normal = scratchNormal$2; var tangent = scratchTangent$1; var bitangent = scratchBitangent$1; var recomputeNormal = true; var textureMatrix = appendTextureCoordinatesMatrix3; var tangentRotationMatrix = tangentMatrixScratch; if (stRotation !== 0.0) { var rotation = Quaternion.fromAxisAngle( tangentPlane._plane.normal, stRotation, appendTextureCoordinatesQuaternion ); textureMatrix = Matrix3.fromQuaternion(rotation, textureMatrix); rotation = Quaternion.fromAxisAngle( tangentPlane._plane.normal, -stRotation, appendTextureCoordinatesQuaternion ); tangentRotationMatrix = Matrix3.fromQuaternion( rotation, tangentRotationMatrix ); } else { textureMatrix = Matrix3.clone(Matrix3.IDENTITY, textureMatrix); tangentRotationMatrix = Matrix3.clone( Matrix3.IDENTITY, tangentRotationMatrix ); } var bottomOffset = 0; var bottomOffset2 = 0; if (top && bottom) { bottomOffset = length / 2; bottomOffset2 = length / 3; length /= 2; } for (var i = 0; i < length; i += 3) { var position = Cartesian3.fromArray( flatPositions, i, appendTextureCoordinatesCartesian3 ); if (vertexFormat.st) { var p = Matrix3.multiplyByVector( textureMatrix, position, scratchPosition$9 ); p = ellipsoid.scaleToGeodeticSurface(p, p); var st = tangentPlane.projectPointOntoPlane( p, appendTextureCoordinatesCartesian2 ); Cartesian2.subtract(st, origin, st); var stx = CesiumMath.clamp(st.x / boundingRectangle.width, 0, 1); var sty = CesiumMath.clamp(st.y / boundingRectangle.height, 0, 1); if (bottom) { textureCoordinates[textureCoordIndex + bottomOffset2] = stx; textureCoordinates[textureCoordIndex + 1 + bottomOffset2] = sty; } if (top) { textureCoordinates[textureCoordIndex] = stx; textureCoordinates[textureCoordIndex + 1] = sty; } textureCoordIndex += 2; } if ( vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent || shadowVolume ) { var attrIndex1 = attrIndex + 1; var attrIndex2 = attrIndex + 2; if (wall) { if (i + 3 < length) { var p1 = Cartesian3.fromArray(flatPositions, i + 3, p1Scratch); if (recomputeNormal) { var p2 = Cartesian3.fromArray( flatPositions, i + length, p2Scratch ); if (perPositionHeight) { adjustPosHeightsForNormal(position, p1, p2, ellipsoid); } Cartesian3.subtract(p1, position, p1); Cartesian3.subtract(p2, position, p2); normal = Cartesian3.normalize( Cartesian3.cross(p2, p1, normal), normal ); recomputeNormal = false; } if (Cartesian3.equalsEpsilon(p1, position, CesiumMath.EPSILON10)) { // if we've reached a corner recomputeNormal = true; } } if (vertexFormat.tangent || vertexFormat.bitangent) { bitangent = ellipsoid.geodeticSurfaceNormal(position, bitangent); if (vertexFormat.tangent) { tangent = Cartesian3.normalize( Cartesian3.cross(bitangent, normal, tangent), tangent ); } } } else { normal = ellipsoid.geodeticSurfaceNormal(position, normal); if (vertexFormat.tangent || vertexFormat.bitangent) { if (perPositionHeight) { scratchPerPosNormal = Cartesian3.fromArray( normals, attrIndex, scratchPerPosNormal ); scratchPerPosTangent = Cartesian3.cross( Cartesian3.UNIT_Z, scratchPerPosNormal, scratchPerPosTangent ); scratchPerPosTangent = Cartesian3.normalize( Matrix3.multiplyByVector( tangentRotationMatrix, scratchPerPosTangent, scratchPerPosTangent ), scratchPerPosTangent ); if (vertexFormat.bitangent) { scratchPerPosBitangent = Cartesian3.normalize( Cartesian3.cross( scratchPerPosNormal, scratchPerPosTangent, scratchPerPosBitangent ), scratchPerPosBitangent ); } } tangent = Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent); tangent = Cartesian3.normalize( Matrix3.multiplyByVector(tangentRotationMatrix, tangent, tangent), tangent ); if (vertexFormat.bitangent) { bitangent = Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); } } } if (vertexFormat.normal) { if (options.wall) { normals[attrIndex + bottomOffset] = normal.x; normals[attrIndex1 + bottomOffset] = normal.y; normals[attrIndex2 + bottomOffset] = normal.z; } else if (bottom) { normals[attrIndex + bottomOffset] = -normal.x; normals[attrIndex1 + bottomOffset] = -normal.y; normals[attrIndex2 + bottomOffset] = -normal.z; } if ((top && !perPositionHeight) || wall) { normals[attrIndex] = normal.x; normals[attrIndex1] = normal.y; normals[attrIndex2] = normal.z; } } if (shadowVolume) { if (wall) { normal = ellipsoid.geodeticSurfaceNormal(position, normal); } extrudeNormals[attrIndex + bottomOffset] = -normal.x; extrudeNormals[attrIndex1 + bottomOffset] = -normal.y; extrudeNormals[attrIndex2 + bottomOffset] = -normal.z; } if (vertexFormat.tangent) { if (options.wall) { tangents[attrIndex + bottomOffset] = tangent.x; tangents[attrIndex1 + bottomOffset] = tangent.y; tangents[attrIndex2 + bottomOffset] = tangent.z; } else if (bottom) { tangents[attrIndex + bottomOffset] = -tangent.x; tangents[attrIndex1 + bottomOffset] = -tangent.y; tangents[attrIndex2 + bottomOffset] = -tangent.z; } if (top) { if (perPositionHeight) { tangents[attrIndex] = scratchPerPosTangent.x; tangents[attrIndex1] = scratchPerPosTangent.y; tangents[attrIndex2] = scratchPerPosTangent.z; } else { tangents[attrIndex] = tangent.x; tangents[attrIndex1] = tangent.y; tangents[attrIndex2] = tangent.z; } } } if (vertexFormat.bitangent) { if (bottom) { bitangents[attrIndex + bottomOffset] = bitangent.x; bitangents[attrIndex1 + bottomOffset] = bitangent.y; bitangents[attrIndex2 + bottomOffset] = bitangent.z; } if (top) { if (perPositionHeight) { bitangents[attrIndex] = scratchPerPosBitangent.x; bitangents[attrIndex1] = scratchPerPosBitangent.y; bitangents[attrIndex2] = scratchPerPosBitangent.z; } else { bitangents[attrIndex] = bitangent.x; bitangents[attrIndex1] = bitangent.y; bitangents[attrIndex2] = bitangent.z; } } } attrIndex += 3; } } if (vertexFormat.st) { geometry.attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } if (vertexFormat.normal) { geometry.attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { geometry.attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { geometry.attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (shadowVolume) { geometry.attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: extrudeNormals, }); } } if (options.extrude && defined(options.offsetAttribute)) { var size = flatPositions.length / 3; var offsetAttribute = new Uint8Array(size); if (options.offsetAttribute === GeometryOffsetAttribute$1.TOP) { if ((top && bottom) || wall) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else if (top) { offsetAttribute = arrayFill(offsetAttribute, 1); } } else { var offsetValue = options.offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, }); } return geometry; } var startCartographicScratch = new Cartographic(); var endCartographicScratch = new Cartographic(); var idlCross = { westOverIDL: 0.0, eastOverIDL: 0.0, }; var ellipsoidGeodesic = new EllipsoidGeodesic(); function computeRectangle$1(positions, ellipsoid, arcType, granularity, result) { result = defaultValue(result, new Rectangle()); if (!defined(positions) || positions.length < 3) { result.west = 0.0; result.north = 0.0; result.south = 0.0; result.east = 0.0; return result; } if (arcType === ArcType$1.RHUMB) { return Rectangle.fromCartesianArray(positions, ellipsoid, result); } if (!ellipsoidGeodesic.ellipsoid.equals(ellipsoid)) { ellipsoidGeodesic = new EllipsoidGeodesic(undefined, undefined, ellipsoid); } result.west = Number.POSITIVE_INFINITY; result.east = Number.NEGATIVE_INFINITY; result.south = Number.POSITIVE_INFINITY; result.north = Number.NEGATIVE_INFINITY; idlCross.westOverIDL = Number.POSITIVE_INFINITY; idlCross.eastOverIDL = Number.NEGATIVE_INFINITY; var inverseChordLength = 1.0 / CesiumMath.chordLength(granularity, ellipsoid.maximumRadius); var positionsLength = positions.length; var endCartographic = ellipsoid.cartesianToCartographic( positions[0], endCartographicScratch ); var startCartographic = startCartographicScratch; var swap; for (var i = 1; i < positionsLength; i++) { swap = startCartographic; startCartographic = endCartographic; endCartographic = ellipsoid.cartesianToCartographic(positions[i], swap); ellipsoidGeodesic.setEndPoints(startCartographic, endCartographic); interpolateAndGrowRectangle( ellipsoidGeodesic, inverseChordLength, result, idlCross ); } swap = startCartographic; startCartographic = endCartographic; endCartographic = ellipsoid.cartesianToCartographic(positions[0], swap); ellipsoidGeodesic.setEndPoints(startCartographic, endCartographic); interpolateAndGrowRectangle( ellipsoidGeodesic, inverseChordLength, result, idlCross ); if (result.east - result.west > idlCross.eastOverIDL - idlCross.westOverIDL) { result.west = idlCross.westOverIDL; result.east = idlCross.eastOverIDL; if (result.east > CesiumMath.PI) { result.east = result.east - CesiumMath.TWO_PI; } if (result.west > CesiumMath.PI) { result.west = result.west - CesiumMath.TWO_PI; } } return result; } var interpolatedCartographicScratch = new Cartographic(); function interpolateAndGrowRectangle( ellipsoidGeodesic, inverseChordLength, result, idlCross ) { var segmentLength = ellipsoidGeodesic.surfaceDistance; var numPoints = Math.ceil(segmentLength * inverseChordLength); var subsegmentDistance = numPoints > 0 ? segmentLength / (numPoints - 1) : Number.POSITIVE_INFINITY; var interpolationDistance = 0.0; for (var i = 0; i < numPoints; i++) { var interpolatedCartographic = ellipsoidGeodesic.interpolateUsingSurfaceDistance( interpolationDistance, interpolatedCartographicScratch ); interpolationDistance += subsegmentDistance; var longitude = interpolatedCartographic.longitude; var latitude = interpolatedCartographic.latitude; result.west = Math.min(result.west, longitude); result.east = Math.max(result.east, longitude); result.south = Math.min(result.south, latitude); result.north = Math.max(result.north, latitude); var lonAdjusted = longitude >= 0 ? longitude : longitude + CesiumMath.TWO_PI; idlCross.westOverIDL = Math.min(idlCross.westOverIDL, lonAdjusted); idlCross.eastOverIDL = Math.max(idlCross.eastOverIDL, lonAdjusted); } } var createGeometryFromPositionsExtrudedPositions = []; function createGeometryFromPositionsExtruded$1( ellipsoid, polygon, granularity, hierarchy, perPositionHeight, closeTop, closeBottom, vertexFormat, arcType ) { var geos = { walls: [], }; var i; if (closeTop || closeBottom) { var topGeo = PolygonGeometryLibrary.createGeometryFromPositions( ellipsoid, polygon, granularity, perPositionHeight, vertexFormat, arcType ); var edgePoints = topGeo.attributes.position.values; var indices = topGeo.indices; var numPositions; var newIndices; if (closeTop && closeBottom) { var topBottomPositions = edgePoints.concat(edgePoints); numPositions = topBottomPositions.length / 3; newIndices = IndexDatatype$1.createTypedArray( numPositions, indices.length * 2 ); newIndices.set(indices); var ilength = indices.length; var length = numPositions / 2; for (i = 0; i < ilength; i += 3) { var i0 = newIndices[i] + length; var i1 = newIndices[i + 1] + length; var i2 = newIndices[i + 2] + length; newIndices[i + ilength] = i2; newIndices[i + 1 + ilength] = i1; newIndices[i + 2 + ilength] = i0; } topGeo.attributes.position.values = topBottomPositions; if (perPositionHeight && vertexFormat.normal) { var normals = topGeo.attributes.normal.values; topGeo.attributes.normal.values = new Float32Array( topBottomPositions.length ); topGeo.attributes.normal.values.set(normals); } topGeo.indices = newIndices; } else if (closeBottom) { numPositions = edgePoints.length / 3; newIndices = IndexDatatype$1.createTypedArray(numPositions, indices.length); for (i = 0; i < indices.length; i += 3) { newIndices[i] = indices[i + 2]; newIndices[i + 1] = indices[i + 1]; newIndices[i + 2] = indices[i]; } topGeo.indices = newIndices; } geos.topAndBottom = new GeometryInstance({ geometry: topGeo, }); } var outerRing = hierarchy.outerRing; var tangentPlane = EllipsoidTangentPlane.fromPoints(outerRing, ellipsoid); var positions2D = tangentPlane.projectPointsOntoPlane( outerRing, createGeometryFromPositionsExtrudedPositions ); var windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); if (windingOrder === WindingOrder$1.CLOCKWISE) { outerRing = outerRing.slice().reverse(); } var wallGeo = PolygonGeometryLibrary.computeWallGeometry( outerRing, ellipsoid, granularity, perPositionHeight, arcType ); geos.walls.push( new GeometryInstance({ geometry: wallGeo, }) ); var holes = hierarchy.holes; for (i = 0; i < holes.length; i++) { var hole = holes[i]; tangentPlane = EllipsoidTangentPlane.fromPoints(hole, ellipsoid); positions2D = tangentPlane.projectPointsOntoPlane( hole, createGeometryFromPositionsExtrudedPositions ); windingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); if (windingOrder === WindingOrder$1.COUNTER_CLOCKWISE) { hole = hole.slice().reverse(); } wallGeo = PolygonGeometryLibrary.computeWallGeometry( hole, ellipsoid, granularity, perPositionHeight, arcType ); geos.walls.push( new GeometryInstance({ geometry: wallGeo, }) ); } return geos; } /** * A description of a polygon on the ellipsoid. The polygon is defined by a polygon hierarchy. Polygon geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @alias PolygonGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes. * @param {Number} [options.height=0.0] The distance in meters between the polygon and the ellipsoid surface. * @param {Number} [options.extrudedHeight] The distance in meters between the polygon's extruded face and the ellipsoid surface. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {Boolean} [options.perPositionHeight=false] Use the height of options.positions for each position instead of using options.height to determine the height. * @param {Boolean} [options.closeTop=true] When false, leaves off the top of an extruded polygon open. * @param {Boolean} [options.closeBottom=true] When false, leaves off the bottom of an extruded polygon open. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polygon edges must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * * @see PolygonGeometry#createGeometry * @see PolygonGeometry#fromPositions * * @demo {@link https://sandcastle.cesium.com/index.html?src=Polygon.html|Cesium Sandcastle Polygon Demo} * * @example * // 1. create a polygon from points * var polygon = new Cesium.PolygonGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * ) * }); * var geometry = Cesium.PolygonGeometry.createGeometry(polygon); * * // 2. create a nested polygon with holes * var polygonWithHole = new Cesium.PolygonGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -109.0, 30.0, * -95.0, 30.0, * -95.0, 40.0, * -109.0, 40.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -107.0, 31.0, * -107.0, 39.0, * -97.0, 39.0, * -97.0, 31.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -105.0, 33.0, * -99.0, 33.0, * -99.0, 37.0, * -105.0, 37.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -103.0, 34.0, * -101.0, 34.0, * -101.0, 36.0, * -103.0, 36.0 * ]) * )] * )] * )] * ) * }); * var geometry = Cesium.PolygonGeometry.createGeometry(polygonWithHole); * * // 3. create extruded polygon * var extrudedPolygon = new Cesium.PolygonGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * ), * extrudedHeight: 300000 * }); * var geometry = Cesium.PolygonGeometry.createGeometry(extrudedPolygon); */ function PolygonGeometry(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.polygonHierarchy", options.polygonHierarchy); if ( defined(options.perPositionHeight) && options.perPositionHeight && defined(options.height) ) { throw new DeveloperError( "Cannot use both options.perPositionHeight and options.height" ); } if ( defined(options.arcType) && options.arcType !== ArcType$1.GEODESIC && options.arcType !== ArcType$1.RHUMB ) { throw new DeveloperError( "Invalid arcType. Valid options are ArcType.GEODESIC and ArcType.RHUMB." ); } //>>includeEnd('debug'); var polygonHierarchy = options.polygonHierarchy; var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var stRotation = defaultValue(options.stRotation, 0.0); var perPositionHeight = defaultValue(options.perPositionHeight, false); var perPositionHeightExtrude = perPositionHeight && defined(options.extrudedHeight); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); if (!perPositionHeightExtrude) { var h = Math.max(height, extrudedHeight); extrudedHeight = Math.min(height, extrudedHeight); height = h; } this._vertexFormat = VertexFormat.clone(vertexFormat); this._ellipsoid = Ellipsoid.clone(ellipsoid); this._granularity = granularity; this._stRotation = stRotation; this._height = height; this._extrudedHeight = extrudedHeight; this._closeTop = defaultValue(options.closeTop, true); this._closeBottom = defaultValue(options.closeBottom, true); this._polygonHierarchy = polygonHierarchy; this._perPositionHeight = perPositionHeight; this._perPositionHeightExtrude = perPositionHeightExtrude; this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = "createPolygonGeometry"; this._offsetAttribute = options.offsetAttribute; this._arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); this._rectangle = undefined; this._textureCoordinateRotationPoints = undefined; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + VertexFormat.packedLength + 12; } /** * A description of a polygon from an array of positions. Polygon geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that defined the corner points of the polygon. * @param {Number} [options.height=0.0] The height of the polygon. * @param {Number} [options.extrudedHeight] The height of the polygon extrusion. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {Boolean} [options.perPositionHeight=false] Use the height of options.positions for each position instead of using options.height to determine the height. * @param {Boolean} [options.closeTop=true] When false, leaves off the top of an extruded polygon open. * @param {Boolean} [options.closeBottom=true] When false, leaves off the bottom of an extruded polygon open. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polygon edges must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * @returns {PolygonGeometry} * * * @example * // create a polygon from points * var polygon = Cesium.PolygonGeometry.fromPositions({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * }); * var geometry = Cesium.PolygonGeometry.createGeometry(polygon); * * @see PolygonGeometry#createGeometry */ PolygonGeometry.fromPositions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", options.positions); //>>includeEnd('debug'); var newOptions = { polygonHierarchy: { positions: options.positions, }, height: options.height, extrudedHeight: options.extrudedHeight, vertexFormat: options.vertexFormat, stRotation: options.stRotation, ellipsoid: options.ellipsoid, granularity: options.granularity, perPositionHeight: options.perPositionHeight, closeTop: options.closeTop, closeBottom: options.closeBottom, offsetAttribute: options.offsetAttribute, arcType: options.arcType, }; return new PolygonGeometry(newOptions); }; /** * Stores the provided instance into the provided array. * * @param {PolygonGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PolygonGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); startingIndex = PolygonGeometryLibrary.packPolygonHierarchy( value._polygonHierarchy, array, startingIndex ); Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._height; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._granularity; array[startingIndex++] = value._stRotation; array[startingIndex++] = value._perPositionHeightExtrude ? 1.0 : 0.0; array[startingIndex++] = value._perPositionHeight ? 1.0 : 0.0; array[startingIndex++] = value._closeTop ? 1.0 : 0.0; array[startingIndex++] = value._closeBottom ? 1.0 : 0.0; array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; array[startingIndex++] = defaultValue(value._offsetAttribute, -1); array[startingIndex++] = value._arcType; array[startingIndex] = value.packedLength; return array; }; var scratchEllipsoid$9 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat$4 = new VertexFormat(); //Only used to avoid inability to default construct. var dummyOptions$1 = { polygonHierarchy: {}, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolygonGeometry} [result] The object into which to store the result. */ PolygonGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var polygonHierarchy = PolygonGeometryLibrary.unpackPolygonHierarchy( array, startingIndex ); startingIndex = polygonHierarchy.startingIndex; delete polygonHierarchy.startingIndex; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$9); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$4 ); startingIndex += VertexFormat.packedLength; var height = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var granularity = array[startingIndex++]; var stRotation = array[startingIndex++]; var perPositionHeightExtrude = array[startingIndex++] === 1.0; var perPositionHeight = array[startingIndex++] === 1.0; var closeTop = array[startingIndex++] === 1.0; var closeBottom = array[startingIndex++] === 1.0; var shadowVolume = array[startingIndex++] === 1.0; var offsetAttribute = array[startingIndex++]; var arcType = array[startingIndex++]; var packedLength = array[startingIndex]; if (!defined(result)) { result = new PolygonGeometry(dummyOptions$1); } result._polygonHierarchy = polygonHierarchy; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._height = height; result._extrudedHeight = extrudedHeight; result._granularity = granularity; result._stRotation = stRotation; result._perPositionHeightExtrude = perPositionHeightExtrude; result._perPositionHeight = perPositionHeight; result._closeTop = closeTop; result._closeBottom = closeBottom; result._shadowVolume = shadowVolume; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; result._arcType = arcType; result.packedLength = packedLength; return result; }; /** * Returns the bounding rectangle given the provided options * * @param {Object} options Object with the following properties: * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions sampled. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polygon edges must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @param {Rectangle} [result] An object in which to store the result. * * @returns {Rectangle} The result rectangle */ PolygonGeometry.computeRectangle = function (options, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.polygonHierarchy", options.polygonHierarchy); //>>includeEnd('debug'); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); //>>includeStart('debug', pragmas.debug); if (arcType !== ArcType$1.GEODESIC && arcType !== ArcType$1.RHUMB) { throw new DeveloperError( "Invalid arcType. Valid options are ArcType.GEODESIC and ArcType.RHUMB." ); } //>>includeEnd('debug'); var polygonHierarchy = options.polygonHierarchy; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); return computeRectangle$1( polygonHierarchy.positions, ellipsoid, arcType, granularity, result ); }; /** * Computes the geometric representation of a polygon, including its vertices, indices, and a bounding sphere. * * @param {PolygonGeometry} polygonGeometry A description of the polygon. * @returns {Geometry|undefined} The computed vertices and indices. */ PolygonGeometry.createGeometry = function (polygonGeometry) { var vertexFormat = polygonGeometry._vertexFormat; var ellipsoid = polygonGeometry._ellipsoid; var granularity = polygonGeometry._granularity; var stRotation = polygonGeometry._stRotation; var polygonHierarchy = polygonGeometry._polygonHierarchy; var perPositionHeight = polygonGeometry._perPositionHeight; var closeTop = polygonGeometry._closeTop; var closeBottom = polygonGeometry._closeBottom; var arcType = polygonGeometry._arcType; var outerPositions = polygonHierarchy.positions; if (outerPositions.length < 3) { return; } var tangentPlane = EllipsoidTangentPlane.fromPoints( outerPositions, ellipsoid ); var results = PolygonGeometryLibrary.polygonsFromHierarchy( polygonHierarchy, tangentPlane.projectPointsOntoPlane.bind(tangentPlane), !perPositionHeight, ellipsoid ); var hierarchy = results.hierarchy; var polygons = results.polygons; if (hierarchy.length === 0) { return; } outerPositions = hierarchy[0].outerRing; var boundingRectangle = PolygonGeometryLibrary.computeBoundingRectangle( tangentPlane.plane.normal, tangentPlane.projectPointOntoPlane.bind(tangentPlane), outerPositions, stRotation, scratchBoundingRectangle ); var geometries = []; var height = polygonGeometry._height; var extrudedHeight = polygonGeometry._extrudedHeight; var extrude = polygonGeometry._perPositionHeightExtrude || !CesiumMath.equalsEpsilon(height, extrudedHeight, 0, CesiumMath.EPSILON2); var options = { perPositionHeight: perPositionHeight, vertexFormat: vertexFormat, geometry: undefined, tangentPlane: tangentPlane, boundingRectangle: boundingRectangle, ellipsoid: ellipsoid, stRotation: stRotation, bottom: false, top: true, wall: false, extrude: false, arcType: arcType, }; var i; if (extrude) { options.extrude = true; options.top = closeTop; options.bottom = closeBottom; options.shadowVolume = polygonGeometry._shadowVolume; options.offsetAttribute = polygonGeometry._offsetAttribute; for (i = 0; i < polygons.length; i++) { var splitGeometry = createGeometryFromPositionsExtruded$1( ellipsoid, polygons[i], granularity, hierarchy[i], perPositionHeight, closeTop, closeBottom, vertexFormat, arcType ); var topAndBottom; if (closeTop && closeBottom) { topAndBottom = splitGeometry.topAndBottom; options.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded( topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight ); } else if (closeTop) { topAndBottom = splitGeometry.topAndBottom; topAndBottom.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( topAndBottom.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight ); options.geometry = topAndBottom.geometry; } else if (closeBottom) { topAndBottom = splitGeometry.topAndBottom; topAndBottom.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( topAndBottom.geometry.attributes.position.values, extrudedHeight, ellipsoid, true ); options.geometry = topAndBottom.geometry; } if (closeTop || closeBottom) { options.wall = false; topAndBottom.geometry = computeAttributes$2(options); geometries.push(topAndBottom); } var walls = splitGeometry.walls; options.wall = true; for (var k = 0; k < walls.length; k++) { var wall = walls[k]; options.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded( wall.geometry, height, extrudedHeight, ellipsoid, perPositionHeight ); wall.geometry = computeAttributes$2(options); geometries.push(wall); } } } else { for (i = 0; i < polygons.length; i++) { var geometryInstance = new GeometryInstance({ geometry: PolygonGeometryLibrary.createGeometryFromPositions( ellipsoid, polygons[i], granularity, perPositionHeight, vertexFormat, arcType ), }); geometryInstance.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( geometryInstance.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight ); options.geometry = geometryInstance.geometry; geometryInstance.geometry = computeAttributes$2(options); if (defined(polygonGeometry._offsetAttribute)) { var length = geometryInstance.geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute( { componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, } ); } geometries.push(geometryInstance); } } var geometry = GeometryPipeline.combineInstances(geometries)[0]; geometry.attributes.position.values = new Float64Array( geometry.attributes.position.values ); geometry.indices = IndexDatatype$1.createTypedArray( geometry.attributes.position.values.length / 3, geometry.indices ); var attributes = geometry.attributes; var boundingSphere = BoundingSphere.fromVertices(attributes.position.values); if (!vertexFormat.position) { delete attributes.position; } return new Geometry({ attributes: attributes, indices: geometry.indices, primitiveType: geometry.primitiveType, boundingSphere: boundingSphere, offsetAttribute: polygonGeometry._offsetAttribute, }); }; /** * @private */ PolygonGeometry.createShadowVolume = function ( polygonGeometry, minHeightFunc, maxHeightFunc ) { var granularity = polygonGeometry._granularity; var ellipsoid = polygonGeometry._ellipsoid; var minHeight = minHeightFunc(granularity, ellipsoid); var maxHeight = maxHeightFunc(granularity, ellipsoid); return new PolygonGeometry({ polygonHierarchy: polygonGeometry._polygonHierarchy, ellipsoid: ellipsoid, stRotation: polygonGeometry._stRotation, granularity: granularity, perPositionHeight: false, extrudedHeight: minHeight, height: maxHeight, vertexFormat: VertexFormat.POSITION_ONLY, shadowVolume: true, arcType: polygonGeometry._arcType, }); }; function textureCoordinateRotationPoints$1(polygonGeometry) { var stRotation = -polygonGeometry._stRotation; if (stRotation === 0.0) { return [0, 0, 0, 1, 1, 0]; } var ellipsoid = polygonGeometry._ellipsoid; var positions = polygonGeometry._polygonHierarchy.positions; var boundingRectangle = polygonGeometry.rectangle; return Geometry._textureCoordinateRotationPoints( positions, stRotation, ellipsoid, boundingRectangle ); } Object.defineProperties(PolygonGeometry.prototype, { /** * @private */ rectangle: { get: function () { if (!defined(this._rectangle)) { var positions = this._polygonHierarchy.positions; this._rectangle = computeRectangle$1( positions, this._ellipsoid, this._arcType, this._granularity ); } return this._rectangle; }, }, /** * For remapping texture coordinates when rendering PolygonGeometries as GroundPrimitives. * @private */ textureCoordinateRotationPoints: { get: function () { if (!defined(this._textureCoordinateRotationPoints)) { this._textureCoordinateRotationPoints = textureCoordinateRotationPoints$1( this ); } return this._textureCoordinateRotationPoints; }, }, }); /** * An hierarchy of linear rings which define a polygon and its holes. * The holes themselves may also have holes which nest inner polygons. * @alias PolygonHierarchy * @constructor * * @param {Cartesian3[]} [positions] A linear ring defining the outer boundary of the polygon or hole. * @param {PolygonHierarchy[]} [holes] An array of polygon hierarchies defining holes in the polygon. */ function PolygonHierarchy(positions, holes) { /** * A linear ring defining the outer boundary of the polygon or hole. * @type {Cartesian3[]} */ this.positions = defined(positions) ? positions : []; /** * An array of polygon hierarchies defining holes in the polygon. * @type {PolygonHierarchy[]} */ this.holes = defined(holes) ? holes : []; } var createGeometryFromPositionsPositions = []; var createGeometryFromPositionsSubdivided = []; function createGeometryFromPositions( ellipsoid, positions, minDistance, perPositionHeight, arcType ) { var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); var positions2D = tangentPlane.projectPointsOntoPlane( positions, createGeometryFromPositionsPositions ); var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); if (originalWindingOrder === WindingOrder$1.CLOCKWISE) { positions2D.reverse(); positions = positions.slice().reverse(); } var subdividedPositions; var i; var length = positions.length; var index = 0; if (!perPositionHeight) { var numVertices = 0; if (arcType === ArcType$1.GEODESIC) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideLineCount( positions[i], positions[(i + 1) % length], minDistance ); } } else if (arcType === ArcType$1.RHUMB) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount( ellipsoid, positions[i], positions[(i + 1) % length], minDistance ); } } subdividedPositions = new Float64Array(numVertices * 3); for (i = 0; i < length; i++) { var tempPositions; if (arcType === ArcType$1.GEODESIC) { tempPositions = PolygonGeometryLibrary.subdivideLine( positions[i], positions[(i + 1) % length], minDistance, createGeometryFromPositionsSubdivided ); } else if (arcType === ArcType$1.RHUMB) { tempPositions = PolygonGeometryLibrary.subdivideRhumbLine( ellipsoid, positions[i], positions[(i + 1) % length], minDistance, createGeometryFromPositionsSubdivided ); } var tempPositionsLength = tempPositions.length; for (var j = 0; j < tempPositionsLength; ++j) { subdividedPositions[index++] = tempPositions[j]; } } } else { subdividedPositions = new Float64Array(length * 2 * 3); for (i = 0; i < length; i++) { var p0 = positions[i]; var p1 = positions[(i + 1) % length]; subdividedPositions[index++] = p0.x; subdividedPositions[index++] = p0.y; subdividedPositions[index++] = p0.z; subdividedPositions[index++] = p1.x; subdividedPositions[index++] = p1.y; subdividedPositions[index++] = p1.z; } } length = subdividedPositions.length / 3; var indicesSize = length * 2; var indices = IndexDatatype$1.createTypedArray(length, indicesSize); index = 0; for (i = 0; i < length - 1; i++) { indices[index++] = i; indices[index++] = i + 1; } indices[index++] = length - 1; indices[index++] = 0; return new GeometryInstance({ geometry: new Geometry({ attributes: new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: subdividedPositions, }), }), indices: indices, primitiveType: PrimitiveType$1.LINES, }), }); } function createGeometryFromPositionsExtruded( ellipsoid, positions, minDistance, perPositionHeight, arcType ) { var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); var positions2D = tangentPlane.projectPointsOntoPlane( positions, createGeometryFromPositionsPositions ); var originalWindingOrder = PolygonPipeline.computeWindingOrder2D(positions2D); if (originalWindingOrder === WindingOrder$1.CLOCKWISE) { positions2D.reverse(); positions = positions.slice().reverse(); } var subdividedPositions; var i; var length = positions.length; var corners = new Array(length); var index = 0; if (!perPositionHeight) { var numVertices = 0; if (arcType === ArcType$1.GEODESIC) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideLineCount( positions[i], positions[(i + 1) % length], minDistance ); } } else if (arcType === ArcType$1.RHUMB) { for (i = 0; i < length; i++) { numVertices += PolygonGeometryLibrary.subdivideRhumbLineCount( ellipsoid, positions[i], positions[(i + 1) % length], minDistance ); } } subdividedPositions = new Float64Array(numVertices * 3 * 2); for (i = 0; i < length; ++i) { corners[i] = index / 3; var tempPositions; if (arcType === ArcType$1.GEODESIC) { tempPositions = PolygonGeometryLibrary.subdivideLine( positions[i], positions[(i + 1) % length], minDistance, createGeometryFromPositionsSubdivided ); } else if (arcType === ArcType$1.RHUMB) { tempPositions = PolygonGeometryLibrary.subdivideRhumbLine( ellipsoid, positions[i], positions[(i + 1) % length], minDistance, createGeometryFromPositionsSubdivided ); } var tempPositionsLength = tempPositions.length; for (var j = 0; j < tempPositionsLength; ++j) { subdividedPositions[index++] = tempPositions[j]; } } } else { subdividedPositions = new Float64Array(length * 2 * 3 * 2); for (i = 0; i < length; ++i) { corners[i] = index / 3; var p0 = positions[i]; var p1 = positions[(i + 1) % length]; subdividedPositions[index++] = p0.x; subdividedPositions[index++] = p0.y; subdividedPositions[index++] = p0.z; subdividedPositions[index++] = p1.x; subdividedPositions[index++] = p1.y; subdividedPositions[index++] = p1.z; } } length = subdividedPositions.length / (3 * 2); var cornersLength = corners.length; var indicesSize = (length * 2 + cornersLength) * 2; var indices = IndexDatatype$1.createTypedArray( length + cornersLength, indicesSize ); index = 0; for (i = 0; i < length; ++i) { indices[index++] = i; indices[index++] = (i + 1) % length; indices[index++] = i + length; indices[index++] = ((i + 1) % length) + length; } for (i = 0; i < cornersLength; i++) { var corner = corners[i]; indices[index++] = corner; indices[index++] = corner + length; } return new GeometryInstance({ geometry: new Geometry({ attributes: new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: subdividedPositions, }), }), indices: indices, primitiveType: PrimitiveType$1.LINES, }), }); } /** * A description of the outline of a polygon on the ellipsoid. The polygon is defined by a polygon hierarchy. * * @alias PolygonOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {PolygonHierarchy} options.polygonHierarchy A polygon hierarchy that can include holes. * @param {Number} [options.height=0.0] The distance in meters between the polygon and the ellipsoid surface. * @param {Number} [options.extrudedHeight] The distance in meters between the polygon's extruded face and the ellipsoid surface. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {Boolean} [options.perPositionHeight=false] Use the height of options.positions for each position instead of using options.height to determine the height. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of path the outline must follow. Valid options are {@link ArcType.GEODESIC} and {@link ArcType.RHUMB}. * * @see PolygonOutlineGeometry#createGeometry * @see PolygonOutlineGeometry#fromPositions * * @example * // 1. create a polygon outline from points * var polygon = new Cesium.PolygonOutlineGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * ) * }); * var geometry = Cesium.PolygonOutlineGeometry.createGeometry(polygon); * * // 2. create a nested polygon with holes outline * var polygonWithHole = new Cesium.PolygonOutlineGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -109.0, 30.0, * -95.0, 30.0, * -95.0, 40.0, * -109.0, 40.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -107.0, 31.0, * -107.0, 39.0, * -97.0, 39.0, * -97.0, 31.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -105.0, 33.0, * -99.0, 33.0, * -99.0, 37.0, * -105.0, 37.0 * ]), * [new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -103.0, 34.0, * -101.0, 34.0, * -101.0, 36.0, * -103.0, 36.0 * ]) * )] * )] * )] * ) * }); * var geometry = Cesium.PolygonOutlineGeometry.createGeometry(polygonWithHole); * * // 3. create extruded polygon outline * var extrudedPolygon = new Cesium.PolygonOutlineGeometry({ * polygonHierarchy : new Cesium.PolygonHierarchy( * Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * ), * extrudedHeight: 300000 * }); * var geometry = Cesium.PolygonOutlineGeometry.createGeometry(extrudedPolygon); */ function PolygonOutlineGeometry(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.polygonHierarchy", options.polygonHierarchy); if (options.perPositionHeight && defined(options.height)) { throw new DeveloperError( "Cannot use both options.perPositionHeight and options.height" ); } if ( defined(options.arcType) && options.arcType !== ArcType$1.GEODESIC && options.arcType !== ArcType$1.RHUMB ) { throw new DeveloperError( "Invalid arcType. Valid options are ArcType.GEODESIC and ArcType.RHUMB." ); } //>>includeEnd('debug'); var polygonHierarchy = options.polygonHierarchy; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var perPositionHeight = defaultValue(options.perPositionHeight, false); var perPositionHeightExtrude = perPositionHeight && defined(options.extrudedHeight); var arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); if (!perPositionHeightExtrude) { var h = Math.max(height, extrudedHeight); extrudedHeight = Math.min(height, extrudedHeight); height = h; } this._ellipsoid = Ellipsoid.clone(ellipsoid); this._granularity = granularity; this._height = height; this._extrudedHeight = extrudedHeight; this._arcType = arcType; this._polygonHierarchy = polygonHierarchy; this._perPositionHeight = perPositionHeight; this._perPositionHeightExtrude = perPositionHeightExtrude; this._offsetAttribute = options.offsetAttribute; this._workerName = "createPolygonOutlineGeometry"; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + 8; } /** * Stores the provided instance into the provided array. * * @param {PolygonOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PolygonOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); startingIndex = PolygonGeometryLibrary.packPolygonHierarchy( value._polygonHierarchy, array, startingIndex ); Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._height; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._granularity; array[startingIndex++] = value._perPositionHeightExtrude ? 1.0 : 0.0; array[startingIndex++] = value._perPositionHeight ? 1.0 : 0.0; array[startingIndex++] = value._arcType; array[startingIndex++] = defaultValue(value._offsetAttribute, -1); array[startingIndex] = value.packedLength; return array; }; var scratchEllipsoid$8 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var dummyOptions = { polygonHierarchy: {}, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolygonOutlineGeometry} [result] The object into which to store the result. * @returns {PolygonOutlineGeometry} The modified result parameter or a new PolygonOutlineGeometry instance if one was not provided. */ PolygonOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var polygonHierarchy = PolygonGeometryLibrary.unpackPolygonHierarchy( array, startingIndex ); startingIndex = polygonHierarchy.startingIndex; delete polygonHierarchy.startingIndex; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$8); startingIndex += Ellipsoid.packedLength; var height = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var granularity = array[startingIndex++]; var perPositionHeightExtrude = array[startingIndex++] === 1.0; var perPositionHeight = array[startingIndex++] === 1.0; var arcType = array[startingIndex++]; var offsetAttribute = array[startingIndex++]; var packedLength = array[startingIndex]; if (!defined(result)) { result = new PolygonOutlineGeometry(dummyOptions); } result._polygonHierarchy = polygonHierarchy; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._height = height; result._extrudedHeight = extrudedHeight; result._granularity = granularity; result._perPositionHeight = perPositionHeight; result._perPositionHeightExtrude = perPositionHeightExtrude; result._arcType = arcType; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; result.packedLength = packedLength; return result; }; /** * A description of a polygon outline from an array of positions. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of positions that defined the corner points of the polygon. * @param {Number} [options.height=0.0] The height of the polygon. * @param {Number} [options.extrudedHeight] The height of the polygon extrusion. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {Boolean} [options.perPositionHeight=false] Use the height of options.positions for each position instead of using options.height to determine the height. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of path the outline must follow. Valid options are {@link LinkType.GEODESIC} and {@link ArcType.RHUMB}. * @returns {PolygonOutlineGeometry} * * * @example * // create a polygon from points * var polygon = Cesium.PolygonOutlineGeometry.fromPositions({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0, * -75.0, 30.0, * -70.0, 30.0, * -68.0, 40.0 * ]) * }); * var geometry = Cesium.PolygonOutlineGeometry.createGeometry(polygon); * * @see PolygonOutlineGeometry#createGeometry */ PolygonOutlineGeometry.fromPositions = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.positions", options.positions); //>>includeEnd('debug'); var newOptions = { polygonHierarchy: { positions: options.positions, }, height: options.height, extrudedHeight: options.extrudedHeight, ellipsoid: options.ellipsoid, granularity: options.granularity, perPositionHeight: options.perPositionHeight, arcType: options.arcType, offsetAttribute: options.offsetAttribute, }; return new PolygonOutlineGeometry(newOptions); }; /** * Computes the geometric representation of a polygon outline, including its vertices, indices, and a bounding sphere. * * @param {PolygonOutlineGeometry} polygonGeometry A description of the polygon outline. * @returns {Geometry|undefined} The computed vertices and indices. */ PolygonOutlineGeometry.createGeometry = function (polygonGeometry) { var ellipsoid = polygonGeometry._ellipsoid; var granularity = polygonGeometry._granularity; var polygonHierarchy = polygonGeometry._polygonHierarchy; var perPositionHeight = polygonGeometry._perPositionHeight; var arcType = polygonGeometry._arcType; var polygons = PolygonGeometryLibrary.polygonOutlinesFromHierarchy( polygonHierarchy, !perPositionHeight, ellipsoid ); if (polygons.length === 0) { return undefined; } var geometryInstance; var geometries = []; var minDistance = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); var height = polygonGeometry._height; var extrudedHeight = polygonGeometry._extrudedHeight; var extrude = polygonGeometry._perPositionHeightExtrude || !CesiumMath.equalsEpsilon(height, extrudedHeight, 0, CesiumMath.EPSILON2); var offsetValue; var i; if (extrude) { for (i = 0; i < polygons.length; i++) { geometryInstance = createGeometryFromPositionsExtruded( ellipsoid, polygons[i], minDistance, perPositionHeight, arcType ); geometryInstance.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded( geometryInstance.geometry, height, extrudedHeight, ellipsoid, perPositionHeight ); if (defined(polygonGeometry._offsetAttribute)) { var size = geometryInstance.geometry.attributes.position.values.length / 3; var offsetAttribute = new Uint8Array(size); if (polygonGeometry._offsetAttribute === GeometryOffsetAttribute$1.TOP) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else { offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute( { componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, } ); } geometries.push(geometryInstance); } } else { for (i = 0; i < polygons.length; i++) { geometryInstance = createGeometryFromPositions( ellipsoid, polygons[i], minDistance, perPositionHeight, arcType ); geometryInstance.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( geometryInstance.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight ); if (defined(polygonGeometry._offsetAttribute)) { var length = geometryInstance.geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute( { componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, } ); } geometries.push(geometryInstance); } } var geometry = GeometryPipeline.combineInstances(geometries)[0]; var boundingSphere = BoundingSphere.fromVertices( geometry.attributes.position.values ); return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: geometry.primitiveType, boundingSphere: boundingSphere, offsetAttribute: polygonGeometry._offsetAttribute, }); }; var scratchInterpolateColorsArray = []; function interpolateColors$1(p0, p1, color0, color1, numPoints) { var colors = scratchInterpolateColorsArray; colors.length = numPoints; var i; var r0 = color0.red; var g0 = color0.green; var b0 = color0.blue; var a0 = color0.alpha; var r1 = color1.red; var g1 = color1.green; var b1 = color1.blue; var a1 = color1.alpha; if (Color.equals(color0, color1)) { for (i = 0; i < numPoints; i++) { colors[i] = Color.clone(color0); } return colors; } var redPerVertex = (r1 - r0) / numPoints; var greenPerVertex = (g1 - g0) / numPoints; var bluePerVertex = (b1 - b0) / numPoints; var alphaPerVertex = (a1 - a0) / numPoints; for (i = 0; i < numPoints; i++) { colors[i] = new Color( r0 + i * redPerVertex, g0 + i * greenPerVertex, b0 + i * bluePerVertex, a0 + i * alphaPerVertex ); } return colors; } /** * A description of a polyline modeled as a line strip; the first two positions define a line segment, * and each additional position defines a line segment from the previous position. The polyline is capable of * displaying with a material. * * @alias PolylineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the positions in the polyline as a line strip. * @param {Number} [options.width=1.0] The width in pixels. * @param {Color[]} [options.colors] An Array of {@link Color} defining the per vertex or per segment colors. * @param {Boolean} [options.colorsPerVertex=false] A boolean that determines whether the colors will be flat across each segment of the line or interpolated across the vertices. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polyline segments must follow. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude if options.arcType is not ArcType.NONE. Determines the number of positions in the buffer. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * * @exception {DeveloperError} At least two positions are required. * @exception {DeveloperError} width must be greater than or equal to one. * @exception {DeveloperError} colors has an invalid length. * * @see PolylineGeometry#createGeometry * * @demo {@link https://sandcastle.cesium.com/index.html?src=Polyline.html|Cesium Sandcastle Polyline Demo} * * @example * // A polyline with two connected line segments * var polyline = new Cesium.PolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 5.0, 0.0, * 5.0, 5.0 * ]), * width : 10.0 * }); * var geometry = Cesium.PolylineGeometry.createGeometry(polyline); */ function PolylineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; var colors = options.colors; var width = defaultValue(options.width, 1.0); var colorsPerVertex = defaultValue(options.colorsPerVertex, false); //>>includeStart('debug', pragmas.debug); if (!defined(positions) || positions.length < 2) { throw new DeveloperError("At least two positions are required."); } if (typeof width !== "number") { throw new DeveloperError("width must be a number"); } if ( defined(colors) && ((colorsPerVertex && colors.length < positions.length) || (!colorsPerVertex && colors.length < positions.length - 1)) ) { throw new DeveloperError("colors has an invalid length."); } //>>includeEnd('debug'); this._positions = positions; this._colors = colors; this._width = width; this._colorsPerVertex = colorsPerVertex; this._vertexFormat = VertexFormat.clone( defaultValue(options.vertexFormat, VertexFormat.DEFAULT) ); this._arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._workerName = "createPolylineGeometry"; var numComponents = 1 + positions.length * Cartesian3.packedLength; numComponents += defined(colors) ? 1 + colors.length * Color.packedLength : 1; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + VertexFormat.packedLength + 4; } /** * Stores the provided instance into the provided array. * * @param {PolylineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PolylineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var colors = value._colors; length = defined(colors) ? colors.length : 0.0; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Color.packedLength) { Color.pack(colors[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._width; array[startingIndex++] = value._colorsPerVertex ? 1.0 : 0.0; array[startingIndex++] = value._arcType; array[startingIndex] = value._granularity; return array; }; var scratchEllipsoid$7 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat$3 = new VertexFormat(); var scratchOptions$8 = { positions: undefined, colors: undefined, ellipsoid: scratchEllipsoid$7, vertexFormat: scratchVertexFormat$3, width: undefined, colorsPerVertex: undefined, arcType: undefined, granularity: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolylineGeometry} [result] The object into which to store the result. * @returns {PolylineGeometry} The modified result parameter or a new PolylineGeometry instance if one was not provided. */ PolylineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var colors = length > 0 ? new Array(length) : undefined; for (i = 0; i < length; ++i, startingIndex += Color.packedLength) { colors[i] = Color.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$7); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$3 ); startingIndex += VertexFormat.packedLength; var width = array[startingIndex++]; var colorsPerVertex = array[startingIndex++] === 1.0; var arcType = array[startingIndex++]; var granularity = array[startingIndex]; if (!defined(result)) { scratchOptions$8.positions = positions; scratchOptions$8.colors = colors; scratchOptions$8.width = width; scratchOptions$8.colorsPerVertex = colorsPerVertex; scratchOptions$8.arcType = arcType; scratchOptions$8.granularity = granularity; return new PolylineGeometry(scratchOptions$8); } result._positions = positions; result._colors = colors; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._width = width; result._colorsPerVertex = colorsPerVertex; result._arcType = arcType; result._granularity = granularity; return result; }; var scratchCartesian3$5 = new Cartesian3(); var scratchPosition$8 = new Cartesian3(); var scratchPrevPosition = new Cartesian3(); var scratchNextPosition = new Cartesian3(); /** * Computes the geometric representation of a polyline, including its vertices, indices, and a bounding sphere. * * @param {PolylineGeometry} polylineGeometry A description of the polyline. * @returns {Geometry|undefined} The computed vertices and indices. */ PolylineGeometry.createGeometry = function (polylineGeometry) { var width = polylineGeometry._width; var vertexFormat = polylineGeometry._vertexFormat; var colors = polylineGeometry._colors; var colorsPerVertex = polylineGeometry._colorsPerVertex; var arcType = polylineGeometry._arcType; var granularity = polylineGeometry._granularity; var ellipsoid = polylineGeometry._ellipsoid; var i; var j; var k; var positions = arrayRemoveDuplicates( polylineGeometry._positions, Cartesian3.equalsEpsilon ); var positionsLength = positions.length; // A width of a pixel or less is not a valid geometry, but in order to support external data // that may have errors we treat this as an empty geometry. if (positionsLength < 2 || width <= 0.0) { return undefined; } if (arcType === ArcType$1.GEODESIC || arcType === ArcType$1.RHUMB) { var subdivisionSize; var numberOfPointsFunction; if (arcType === ArcType$1.GEODESIC) { subdivisionSize = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); numberOfPointsFunction = PolylinePipeline.numberOfPoints; } else { subdivisionSize = granularity; numberOfPointsFunction = PolylinePipeline.numberOfPointsRhumbLine; } var heights = PolylinePipeline.extractHeights(positions, ellipsoid); if (defined(colors)) { var colorLength = 1; for (i = 0; i < positionsLength - 1; ++i) { colorLength += numberOfPointsFunction( positions[i], positions[i + 1], subdivisionSize ); } var newColors = new Array(colorLength); var newColorIndex = 0; for (i = 0; i < positionsLength - 1; ++i) { var p0 = positions[i]; var p1 = positions[i + 1]; var c0 = colors[i]; var numColors = numberOfPointsFunction(p0, p1, subdivisionSize); if (colorsPerVertex && i < colorLength) { var c1 = colors[i + 1]; var interpolatedColors = interpolateColors$1(p0, p1, c0, c1, numColors); var interpolatedColorsLength = interpolatedColors.length; for (j = 0; j < interpolatedColorsLength; ++j) { newColors[newColorIndex++] = interpolatedColors[j]; } } else { for (j = 0; j < numColors; ++j) { newColors[newColorIndex++] = Color.clone(c0); } } } newColors[newColorIndex] = Color.clone(colors[colors.length - 1]); colors = newColors; scratchInterpolateColorsArray.length = 0; } if (arcType === ArcType$1.GEODESIC) { positions = PolylinePipeline.generateCartesianArc({ positions: positions, minDistance: subdivisionSize, ellipsoid: ellipsoid, height: heights, }); } else { positions = PolylinePipeline.generateCartesianRhumbArc({ positions: positions, granularity: subdivisionSize, ellipsoid: ellipsoid, height: heights, }); } } positionsLength = positions.length; var size = positionsLength * 4.0 - 4.0; var finalPositions = new Float64Array(size * 3); var prevPositions = new Float64Array(size * 3); var nextPositions = new Float64Array(size * 3); var expandAndWidth = new Float32Array(size * 2); var st = vertexFormat.st ? new Float32Array(size * 2) : undefined; var finalColors = defined(colors) ? new Uint8Array(size * 4) : undefined; var positionIndex = 0; var expandAndWidthIndex = 0; var stIndex = 0; var colorIndex = 0; var position; for (j = 0; j < positionsLength; ++j) { if (j === 0) { position = scratchCartesian3$5; Cartesian3.subtract(positions[0], positions[1], position); Cartesian3.add(positions[0], position, position); } else { position = positions[j - 1]; } Cartesian3.clone(position, scratchPrevPosition); Cartesian3.clone(positions[j], scratchPosition$8); if (j === positionsLength - 1) { position = scratchCartesian3$5; Cartesian3.subtract( positions[positionsLength - 1], positions[positionsLength - 2], position ); Cartesian3.add(positions[positionsLength - 1], position, position); } else { position = positions[j + 1]; } Cartesian3.clone(position, scratchNextPosition); var color0, color1; if (defined(finalColors)) { if (j !== 0 && !colorsPerVertex) { color0 = colors[j - 1]; } else { color0 = colors[j]; } if (j !== positionsLength - 1) { color1 = colors[j]; } } var startK = j === 0 ? 2 : 0; var endK = j === positionsLength - 1 ? 2 : 4; for (k = startK; k < endK; ++k) { Cartesian3.pack(scratchPosition$8, finalPositions, positionIndex); Cartesian3.pack(scratchPrevPosition, prevPositions, positionIndex); Cartesian3.pack(scratchNextPosition, nextPositions, positionIndex); positionIndex += 3; var direction = k - 2 < 0 ? -1.0 : 1.0; expandAndWidth[expandAndWidthIndex++] = 2 * (k % 2) - 1; // expand direction expandAndWidth[expandAndWidthIndex++] = direction * width; if (vertexFormat.st) { st[stIndex++] = j / (positionsLength - 1); st[stIndex++] = Math.max(expandAndWidth[expandAndWidthIndex - 2], 0.0); } if (defined(finalColors)) { var color = k < 2 ? color0 : color1; finalColors[colorIndex++] = Color.floatToByte(color.red); finalColors[colorIndex++] = Color.floatToByte(color.green); finalColors[colorIndex++] = Color.floatToByte(color.blue); finalColors[colorIndex++] = Color.floatToByte(color.alpha); } } } var attributes = new GeometryAttributes(); attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: finalPositions, }); attributes.prevPosition = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: prevPositions, }); attributes.nextPosition = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: nextPositions, }); attributes.expandAndWidth = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: expandAndWidth, }); if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: st, }); } if (defined(finalColors)) { attributes.color = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 4, values: finalColors, normalize: true, }); } var indices = IndexDatatype$1.createTypedArray(size, positionsLength * 6 - 6); var index = 0; var indicesIndex = 0; var length = positionsLength - 1.0; for (j = 0; j < length; ++j) { indices[indicesIndex++] = index; indices[indicesIndex++] = index + 2; indices[indicesIndex++] = index + 1; indices[indicesIndex++] = index + 1; indices[indicesIndex++] = index + 2; indices[indicesIndex++] = index + 3; index += 4; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: BoundingSphere.fromPoints(positions), geometryType: GeometryType$1.POLYLINES, }); }; var warnings = {}; /** * Logs a one time message to the console. Use this function instead of * console.log directly since this does not log duplicate messages * unless it is called from multiple workers. * * @function oneTimeWarning * * @param {String} identifier The unique identifier for this warning. * @param {String} [message=identifier] The message to log to the console. * * @example * for(var i=0;i>includeStart('debug', pragmas.debug); if (!defined(identifier)) { throw new DeveloperError("identifier is required."); } //>>includeEnd('debug'); if (!defined(warnings[identifier])) { warnings[identifier] = true; console.warn(defaultValue(message, identifier)); } } oneTimeWarning.geometryOutlines = "Entity geometry outlines are unsupported on terrain. Outlines will be disabled. To enable outlines, disable geometry terrain clamping by explicitly setting height to 0."; oneTimeWarning.geometryZIndex = "Entity geometry with zIndex are unsupported when height or extrudedHeight are defined. zIndex will be ignored"; oneTimeWarning.geometryHeightReference = "Entity corridor, ellipse, polygon or rectangle with heightReference must also have a defined height. heightReference will be ignored"; oneTimeWarning.geometryExtrudedHeightReference = "Entity corridor, ellipse, polygon or rectangle with extrudedHeightReference must also have a defined extrudedHeight. extrudedHeightReference will be ignored"; function computeAttributes$1( combinedPositions, shape, boundingRectangle, vertexFormat ) { var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: combinedPositions, }); } var shapeLength = shape.length; var vertexCount = combinedPositions.length / 3; var length = (vertexCount - shapeLength * 2) / (shapeLength * 2); var firstEndIndices = PolygonPipeline.triangulate(shape); var indicesCount = (length - 1) * shapeLength * 6 + firstEndIndices.length * 2; var indices = IndexDatatype$1.createTypedArray(vertexCount, indicesCount); var i, j; var ll, ul, ur, lr; var offset = shapeLength * 2; var index = 0; for (i = 0; i < length - 1; i++) { for (j = 0; j < shapeLength - 1; j++) { ll = j * 2 + i * shapeLength * 2; lr = ll + offset; ul = ll + 1; ur = ul + offset; indices[index++] = ul; indices[index++] = ll; indices[index++] = ur; indices[index++] = ur; indices[index++] = ll; indices[index++] = lr; } ll = shapeLength * 2 - 2 + i * shapeLength * 2; ul = ll + 1; ur = ul + offset; lr = ll + offset; indices[index++] = ul; indices[index++] = ll; indices[index++] = ur; indices[index++] = ur; indices[index++] = ll; indices[index++] = lr; } if (vertexFormat.st || vertexFormat.tangent || vertexFormat.bitangent) { // st required for tangent/bitangent calculation var st = new Float32Array(vertexCount * 2); var lengthSt = 1 / (length - 1); var heightSt = 1 / boundingRectangle.height; var heightOffset = boundingRectangle.height / 2; var s, t; var stindex = 0; for (i = 0; i < length; i++) { s = i * lengthSt; t = heightSt * (shape[0].y + heightOffset); st[stindex++] = s; st[stindex++] = t; for (j = 1; j < shapeLength; j++) { t = heightSt * (shape[j].y + heightOffset); st[stindex++] = s; st[stindex++] = t; st[stindex++] = s; st[stindex++] = t; } t = heightSt * (shape[0].y + heightOffset); st[stindex++] = s; st[stindex++] = t; } for (j = 0; j < shapeLength; j++) { s = 0; t = heightSt * (shape[j].y + heightOffset); st[stindex++] = s; st[stindex++] = t; } for (j = 0; j < shapeLength; j++) { s = (length - 1) * lengthSt; t = heightSt * (shape[j].y + heightOffset); st[stindex++] = s; st[stindex++] = t; } attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: new Float32Array(st), }); } var endOffset = vertexCount - shapeLength * 2; for (i = 0; i < firstEndIndices.length; i += 3) { var v0 = firstEndIndices[i] + endOffset; var v1 = firstEndIndices[i + 1] + endOffset; var v2 = firstEndIndices[i + 2] + endOffset; indices[index++] = v0; indices[index++] = v1; indices[index++] = v2; indices[index++] = v2 + shapeLength; indices[index++] = v1 + shapeLength; indices[index++] = v0 + shapeLength; } var geometry = new Geometry({ attributes: attributes, indices: indices, boundingSphere: BoundingSphere.fromVertices(combinedPositions), primitiveType: PrimitiveType$1.TRIANGLES, }); if (vertexFormat.normal) { geometry = GeometryPipeline.computeNormal(geometry); } if (vertexFormat.tangent || vertexFormat.bitangent) { try { geometry = GeometryPipeline.computeTangentAndBitangent(geometry); } catch (e) { oneTimeWarning( "polyline-volume-tangent-bitangent", "Unable to compute tangents and bitangents for polyline volume geometry" ); //TODO https://github.com/CesiumGS/cesium/issues/3609 } if (!vertexFormat.tangent) { geometry.attributes.tangent = undefined; } if (!vertexFormat.bitangent) { geometry.attributes.bitangent = undefined; } if (!vertexFormat.st) { geometry.attributes.st = undefined; } } return geometry; } /** * A description of a polyline with a volume (a 2D shape extruded along a polyline). * * @alias PolylineVolumeGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.polylinePositions An array of {@link Cartesian3} positions that define the center of the polyline volume. * @param {Cartesian2[]} options.shapePositions An array of {@link Cartesian2} positions that define the shape to be extruded along the polyline * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners. * * @see PolylineVolumeGeometry#createGeometry * * @demo {@link https://sandcastle.cesium.com/index.html?src=Polyline%20Volume.html|Cesium Sandcastle Polyline Volume Demo} * * @example * function computeCircle(radius) { * var positions = []; * for (var i = 0; i < 360; i++) { * var radians = Cesium.Math.toRadians(i); * positions.push(new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians))); * } * return positions; * } * * var volume = new Cesium.PolylineVolumeGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_ONLY, * polylinePositions : Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0 * ]), * shapePositions : computeCircle(100000.0) * }); */ function PolylineVolumeGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.polylinePositions; var shape = options.shapePositions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.polylinePositions is required."); } if (!defined(shape)) { throw new DeveloperError("options.shapePositions is required."); } //>>includeEnd('debug'); this._positions = positions; this._shape = shape; this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._cornerType = defaultValue(options.cornerType, CornerType$1.ROUNDED); this._vertexFormat = VertexFormat.clone( defaultValue(options.vertexFormat, VertexFormat.DEFAULT) ); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._workerName = "createPolylineVolumeGeometry"; var numComponents = 1 + positions.length * Cartesian3.packedLength; numComponents += 1 + shape.length * Cartesian2.packedLength; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + VertexFormat.packedLength + 2; } /** * Stores the provided instance into the provided array. * * @param {PolylineVolumeGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PolylineVolumeGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var shape = value._shape; length = shape.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) { Cartesian2.pack(shape[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._cornerType; array[startingIndex] = value._granularity; return array; }; var scratchEllipsoid$6 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat$2 = new VertexFormat(); var scratchOptions$7 = { polylinePositions: undefined, shapePositions: undefined, ellipsoid: scratchEllipsoid$6, vertexFormat: scratchVertexFormat$2, cornerType: undefined, granularity: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolylineVolumeGeometry} [result] The object into which to store the result. * @returns {PolylineVolumeGeometry} The modified result parameter or a new PolylineVolumeGeometry instance if one was not provided. */ PolylineVolumeGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var shape = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) { shape[i] = Cartesian2.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$6); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$2 ); startingIndex += VertexFormat.packedLength; var cornerType = array[startingIndex++]; var granularity = array[startingIndex]; if (!defined(result)) { scratchOptions$7.polylinePositions = positions; scratchOptions$7.shapePositions = shape; scratchOptions$7.cornerType = cornerType; scratchOptions$7.granularity = granularity; return new PolylineVolumeGeometry(scratchOptions$7); } result._positions = positions; result._shape = shape; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._cornerType = cornerType; result._granularity = granularity; return result; }; var brScratch$1 = new BoundingRectangle(); /** * Computes the geometric representation of a polyline with a volume, including its vertices, indices, and a bounding sphere. * * @param {PolylineVolumeGeometry} polylineVolumeGeometry A description of the polyline volume. * @returns {Geometry|undefined} The computed vertices and indices. */ PolylineVolumeGeometry.createGeometry = function (polylineVolumeGeometry) { var positions = polylineVolumeGeometry._positions; var cleanPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); var shape2D = polylineVolumeGeometry._shape; shape2D = PolylineVolumeGeometryLibrary.removeDuplicatesFromShape(shape2D); if (cleanPositions.length < 2 || shape2D.length < 3) { return undefined; } if ( PolygonPipeline.computeWindingOrder2D(shape2D) === WindingOrder$1.CLOCKWISE ) { shape2D.reverse(); } var boundingRectangle = BoundingRectangle.fromPoints(shape2D, brScratch$1); var computedPositions = PolylineVolumeGeometryLibrary.computePositions( cleanPositions, shape2D, boundingRectangle, polylineVolumeGeometry, true ); return computeAttributes$1( computedPositions, shape2D, boundingRectangle, polylineVolumeGeometry._vertexFormat ); }; function computeAttributes(positions, shape) { var attributes = new GeometryAttributes(); attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); var shapeLength = shape.length; var vertexCount = attributes.position.values.length / 3; var positionLength = positions.length / 3; var shapeCount = positionLength / shapeLength; var indices = IndexDatatype$1.createTypedArray( vertexCount, 2 * shapeLength * (shapeCount + 1) ); var i, j; var index = 0; i = 0; var offset = i * shapeLength; for (j = 0; j < shapeLength - 1; j++) { indices[index++] = j + offset; indices[index++] = j + offset + 1; } indices[index++] = shapeLength - 1 + offset; indices[index++] = offset; i = shapeCount - 1; offset = i * shapeLength; for (j = 0; j < shapeLength - 1; j++) { indices[index++] = j + offset; indices[index++] = j + offset + 1; } indices[index++] = shapeLength - 1 + offset; indices[index++] = offset; for (i = 0; i < shapeCount - 1; i++) { var firstOffset = shapeLength * i; var secondOffset = firstOffset + shapeLength; for (j = 0; j < shapeLength; j++) { indices[index++] = j + firstOffset; indices[index++] = j + secondOffset; } } var geometry = new Geometry({ attributes: attributes, indices: IndexDatatype$1.createTypedArray(vertexCount, indices), boundingSphere: BoundingSphere.fromVertices(positions), primitiveType: PrimitiveType$1.LINES, }); return geometry; } /** * A description of a polyline with a volume (a 2D shape extruded along a polyline). * * @alias PolylineVolumeOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.polylinePositions An array of positions that define the center of the polyline volume. * @param {Cartesian2[]} options.shapePositions An array of positions that define the shape to be extruded along the polyline * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * @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 {CornerType} [options.cornerType=CornerType.ROUNDED] Determines the style of the corners. * * @see PolylineVolumeOutlineGeometry#createGeometry * * @example * function computeCircle(radius) { * var positions = []; * for (var i = 0; i < 360; i++) { * var radians = Cesium.Math.toRadians(i); * positions.push(new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians))); * } * return positions; * } * * var volumeOutline = new Cesium.PolylineVolumeOutlineGeometry({ * polylinePositions : Cesium.Cartesian3.fromDegreesArray([ * -72.0, 40.0, * -70.0, 35.0 * ]), * shapePositions : computeCircle(100000.0) * }); */ function PolylineVolumeOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.polylinePositions; var shape = options.shapePositions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.polylinePositions is required."); } if (!defined(shape)) { throw new DeveloperError("options.shapePositions is required."); } //>>includeEnd('debug'); this._positions = positions; this._shape = shape; this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._cornerType = defaultValue(options.cornerType, CornerType$1.ROUNDED); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._workerName = "createPolylineVolumeOutlineGeometry"; var numComponents = 1 + positions.length * Cartesian3.packedLength; numComponents += 1 + shape.length * Cartesian2.packedLength; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + 2; } /** * Stores the provided instance into the provided array. * * @param {PolylineVolumeOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ PolylineVolumeOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var shape = value._shape; length = shape.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) { Cartesian2.pack(shape[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._cornerType; array[startingIndex] = value._granularity; return array; }; var scratchEllipsoid$5 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchOptions$6 = { polylinePositions: undefined, shapePositions: undefined, ellipsoid: scratchEllipsoid$5, height: undefined, cornerType: undefined, granularity: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {PolylineVolumeOutlineGeometry} [result] The object into which to store the result. * @returns {PolylineVolumeOutlineGeometry} The modified result parameter or a new PolylineVolumeOutlineGeometry instance if one was not provided. */ PolylineVolumeOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var shape = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian2.packedLength) { shape[i] = Cartesian2.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$5); startingIndex += Ellipsoid.packedLength; var cornerType = array[startingIndex++]; var granularity = array[startingIndex]; if (!defined(result)) { scratchOptions$6.polylinePositions = positions; scratchOptions$6.shapePositions = shape; scratchOptions$6.cornerType = cornerType; scratchOptions$6.granularity = granularity; return new PolylineVolumeOutlineGeometry(scratchOptions$6); } result._positions = positions; result._shape = shape; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._cornerType = cornerType; result._granularity = granularity; return result; }; var brScratch = new BoundingRectangle(); /** * Computes the geometric representation of the outline of a polyline with a volume, including its vertices, indices, and a bounding sphere. * * @param {PolylineVolumeOutlineGeometry} polylineVolumeOutlineGeometry A description of the polyline volume outline. * @returns {Geometry|undefined} The computed vertices and indices. */ PolylineVolumeOutlineGeometry.createGeometry = function ( polylineVolumeOutlineGeometry ) { var positions = polylineVolumeOutlineGeometry._positions; var cleanPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); var shape2D = polylineVolumeOutlineGeometry._shape; shape2D = PolylineVolumeGeometryLibrary.removeDuplicatesFromShape(shape2D); if (cleanPositions.length < 2 || shape2D.length < 3) { return undefined; } if ( PolygonPipeline.computeWindingOrder2D(shape2D) === WindingOrder$1.CLOCKWISE ) { shape2D.reverse(); } var boundingRectangle = BoundingRectangle.fromPoints(shape2D, brScratch); var computedPositions = PolylineVolumeGeometryLibrary.computePositions( cleanPositions, shape2D, boundingRectangle, polylineVolumeOutlineGeometry, false ); return computeAttributes(computedPositions, shape2D); }; /** * Base class for proxying requested made by {@link Resource}. * * @alias Proxy * @constructor * * @see DefaultProxy */ function Proxy() { DeveloperError.throwInstantiationError(); } /** * Get the final URL to use to request a given resource. * * @param {String} resource The resource to request. * @returns {String} proxied resource * @function */ Proxy.prototype.getURL = DeveloperError.throwInstantiationError; function createEvaluateFunction(spline) { var points = spline.points; var times = spline.times; // use slerp interpolation return function (time, result) { if (!defined(result)) { result = new Quaternion(); } var i = (spline._lastTimeIndex = spline.findTimeInterval( time, spline._lastTimeIndex )); var u = (time - times[i]) / (times[i + 1] - times[i]); var q0 = points[i]; var q1 = points[i + 1]; return Quaternion.fastSlerp(q0, q1, u, result); }; } /** * A spline that uses spherical linear (slerp) interpolation to create a quaternion curve. * The generated curve is in the class C1. * * @alias QuaternionSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. * The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Quaternion[]} options.points The array of {@link Quaternion} control points. * * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * @see HermiteSpline * @see CatmullRomSpline * @see LinearSpline * @see WeightSpline */ function QuaternionSpline(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var points = options.points; var times = options.times; //>>includeStart('debug', pragmas.debug); if (!defined(points) || !defined(times)) { throw new DeveloperError("points and times are required."); } if (points.length < 2) { throw new DeveloperError( "points.length must be greater than or equal to 2." ); } if (times.length !== points.length) { throw new DeveloperError("times.length must be equal to points.length."); } //>>includeEnd('debug'); this._times = times; this._points = points; this._evaluateFunction = createEvaluateFunction(this); this._lastTimeIndex = 0; } Object.defineProperties(QuaternionSpline.prototype, { /** * An array of times for the control points. * * @memberof QuaternionSpline.prototype * * @type {Number[]} * @readonly */ times: { get: function () { return this._times; }, }, /** * An array of {@link Quaternion} control points. * * @memberof QuaternionSpline.prototype * * @type {Quaternion[]} * @readonly */ points: { get: function () { return this._points; }, }, }); /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ QuaternionSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */ QuaternionSpline.prototype.wrapTime = Spline.prototype.wrapTime; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ QuaternionSpline.prototype.clampTime = Spline.prototype.clampTime; /** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Quaternion} [result] The object onto which to store the result. * @returns {Quaternion} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ QuaternionSpline.prototype.evaluate = function (time, result) { return this._evaluateFunction(time, result); }; function quickselect$1(arr, k, left, right, compare) { quickselectStep$1(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare$1); } function quickselectStep$1(arr, k, left, right, compare) { while (right > left) { if (right - left > 600) { var n = right - left + 1; var m = k - left + 1; var z = Math.log(n); var s = 0.5 * Math.exp(2 * z / 3); var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); quickselectStep$1(arr, k, newLeft, newRight, compare); } var t = arr[k]; var i = left; var j = right; swap$2(arr, left, k); if (compare(arr[right], t) > 0) { swap$2(arr, left, right); } while (i < j) { swap$2(arr, i, j); i++; j--; while (compare(arr[i], t) < 0) { i++; } while (compare(arr[j], t) > 0) { j--; } } if (compare(arr[left], t) === 0) { swap$2(arr, left, j); } else { j++; swap$2(arr, j, right); } if (j <= k) { left = j + 1; } if (k <= j) { right = j - 1; } } } function swap$2(arr, i, j) { var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } function defaultCompare$1(a, b) { return a < b ? -1 : a > b ? 1 : 0; } function RBush(maxEntries) { if ( maxEntries === void 0 ) maxEntries = 9; // max entries in a node is 9 by default; min node fill is 40% for best performance this._maxEntries = Math.max(4, maxEntries); this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); this.clear(); } RBush.prototype.all = function all () { return this._all(this.data, []); }; RBush.prototype.search = function search (bbox) { var node = this.data; var result = []; if (!intersects(bbox, node)) { return result; } var toBBox = this.toBBox; var nodesToSearch = []; while (node) { for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; var childBBox = node.leaf ? toBBox(child) : child; if (intersects(bbox, childBBox)) { if (node.leaf) { result.push(child); } else if (contains$1(bbox, childBBox)) { this._all(child, result); } else { nodesToSearch.push(child); } } } node = nodesToSearch.pop(); } return result; }; RBush.prototype.collides = function collides (bbox) { var node = this.data; if (!intersects(bbox, node)) { return false; } var nodesToSearch = []; while (node) { for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; var childBBox = node.leaf ? this.toBBox(child) : child; if (intersects(bbox, childBBox)) { if (node.leaf || contains$1(bbox, childBBox)) { return true; } nodesToSearch.push(child); } } node = nodesToSearch.pop(); } return false; }; RBush.prototype.load = function load (data) { if (!(data && data.length)) { return this; } if (data.length < this._minEntries) { for (var i = 0; i < data.length; i++) { this.insert(data[i]); } return this; } // recursively build the tree with the given data from scratch using OMT algorithm var node = this._build(data.slice(), 0, data.length - 1, 0); if (!this.data.children.length) { // save as is if tree is empty this.data = node; } else if (this.data.height === node.height) { // split root if trees have the same height this._splitRoot(this.data, node); } else { if (this.data.height < node.height) { // swap trees if inserted one is bigger var tmpNode = this.data; this.data = node; node = tmpNode; } // insert the small tree into the large tree at appropriate level this._insert(node, this.data.height - node.height - 1, true); } return this; }; RBush.prototype.insert = function insert (item) { if (item) { this._insert(item, this.data.height - 1); } return this; }; RBush.prototype.clear = function clear () { this.data = createNode([]); return this; }; RBush.prototype.remove = function remove (item, equalsFn) { if (!item) { return this; } var node = this.data; var bbox = this.toBBox(item); var path = []; var indexes = []; var i, parent, goingUp; // depth-first iterative tree traversal while (node || path.length) { if (!node) { // go up node = path.pop(); parent = path[path.length - 1]; i = indexes.pop(); goingUp = true; } if (node.leaf) { // check current node var index = findItem(item, node.children, equalsFn); if (index !== -1) { // item found, remove the item and condense tree upwards node.children.splice(index, 1); path.push(node); this._condense(path); return this; } } if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down path.push(node); indexes.push(i); i = 0; parent = node; node = node.children[0]; } else if (parent) { // go right i++; node = parent.children[i]; goingUp = false; } else { node = null; } // nothing found } return this; }; RBush.prototype.toBBox = function toBBox (item) { return item; }; RBush.prototype.compareMinX = function compareMinX (a, b) { return a.minX - b.minX; }; RBush.prototype.compareMinY = function compareMinY (a, b) { return a.minY - b.minY; }; RBush.prototype.toJSON = function toJSON () { return this.data; }; RBush.prototype.fromJSON = function fromJSON (data) { this.data = data; return this; }; RBush.prototype._all = function _all (node, result) { var nodesToSearch = []; while (node) { if (node.leaf) { result.push.apply(result, node.children); } else { nodesToSearch.push.apply(nodesToSearch, node.children); } node = nodesToSearch.pop(); } return result; }; RBush.prototype._build = function _build (items, left, right, height) { var N = right - left + 1; var M = this._maxEntries; var node; if (N <= M) { // reached leaf level; return leaf node = createNode(items.slice(left, right + 1)); calcBBox(node, this.toBBox); return node; } if (!height) { // target height of the bulk-loaded tree height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization M = Math.ceil(N / Math.pow(M, height - 1)); } node = createNode([]); node.leaf = false; node.height = height; // split the items into M mostly square tiles var N2 = Math.ceil(N / M); var N1 = N2 * Math.ceil(Math.sqrt(M)); multiSelect(items, left, right, N1, this.compareMinX); for (var i = left; i <= right; i += N1) { var right2 = Math.min(i + N1 - 1, right); multiSelect(items, i, right2, N2, this.compareMinY); for (var j = i; j <= right2; j += N2) { var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively node.children.push(this._build(items, j, right3, height - 1)); } } calcBBox(node, this.toBBox); return node; }; RBush.prototype._chooseSubtree = function _chooseSubtree (bbox, node, level, path) { while (true) { path.push(node); if (node.leaf || path.length - 1 === level) { break; } var minArea = Infinity; var minEnlargement = Infinity; var targetNode = (void 0); for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; var area = bboxArea(child); var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement if (enlargement < minEnlargement) { minEnlargement = enlargement; minArea = area < minArea ? area : minArea; targetNode = child; } else if (enlargement === minEnlargement) { // otherwise choose one with the smallest area if (area < minArea) { minArea = area; targetNode = child; } } } node = targetNode || node.children[0]; } return node; }; RBush.prototype._insert = function _insert (item, level, isNode) { var bbox = isNode ? item : this.toBBox(item); var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node node.children.push(item); extend(node, bbox); // split on node overflow; propagate upwards if necessary while (level >= 0) { if (insertPath[level].children.length > this._maxEntries) { this._split(insertPath, level); level--; } else { break; } } // adjust bboxes along the insertion path this._adjustParentBBoxes(bbox, insertPath, level); }; // split overflowed node into two RBush.prototype._split = function _split (insertPath, level) { var node = insertPath[level]; var M = node.children.length; var m = this._minEntries; this._chooseSplitAxis(node, m, M); var splitIndex = this._chooseSplitIndex(node, m, M); var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); newNode.height = node.height; newNode.leaf = node.leaf; calcBBox(node, this.toBBox); calcBBox(newNode, this.toBBox); if (level) { insertPath[level - 1].children.push(newNode); } else { this._splitRoot(node, newNode); } }; RBush.prototype._splitRoot = function _splitRoot (node, newNode) { // split root node this.data = createNode([node, newNode]); this.data.height = node.height + 1; this.data.leaf = false; calcBBox(this.data, this.toBBox); }; RBush.prototype._chooseSplitIndex = function _chooseSplitIndex (node, m, M) { var index; var minOverlap = Infinity; var minArea = Infinity; for (var i = m; i <= M - m; i++) { var bbox1 = distBBox(node, 0, i, this.toBBox); var bbox2 = distBBox(node, i, M, this.toBBox); var overlap = intersectionArea(bbox1, bbox2); var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap if (overlap < minOverlap) { minOverlap = overlap; index = i; minArea = area < minArea ? area : minArea; } else if (overlap === minOverlap) { // otherwise choose distribution with minimum area if (area < minArea) { minArea = area; index = i; } } } return index || M - m; }; // sorts node children by the best axis for split RBush.prototype._chooseSplitAxis = function _chooseSplitAxis (node, m, M) { var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; var xMargin = this._allDistMargin(node, m, M, compareMinX); var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX, // otherwise it's already sorted by minY if (xMargin < yMargin) { node.children.sort(compareMinX); } }; // total margin of all possible split distributions where each node is at least m full RBush.prototype._allDistMargin = function _allDistMargin (node, m, M, compare) { node.children.sort(compare); var toBBox = this.toBBox; var leftBBox = distBBox(node, 0, m, toBBox); var rightBBox = distBBox(node, M - m, M, toBBox); var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); for (var i = m; i < M - m; i++) { var child = node.children[i]; extend(leftBBox, node.leaf ? toBBox(child) : child); margin += bboxMargin(leftBBox); } for (var i$1 = M - m - 1; i$1 >= m; i$1--) { var child$1 = node.children[i$1]; extend(rightBBox, node.leaf ? toBBox(child$1) : child$1); margin += bboxMargin(rightBBox); } return margin; }; RBush.prototype._adjustParentBBoxes = function _adjustParentBBoxes (bbox, path, level) { // adjust bboxes along the given tree path for (var i = level; i >= 0; i--) { extend(path[i], bbox); } }; RBush.prototype._condense = function _condense (path) { // go through the path, removing empty nodes and updating bboxes for (var i = path.length - 1, siblings = (void 0); i >= 0; i--) { if (path[i].children.length === 0) { if (i > 0) { siblings = path[i - 1].children; siblings.splice(siblings.indexOf(path[i]), 1); } else { this.clear(); } } else { calcBBox(path[i], this.toBBox); } } }; function findItem(item, items, equalsFn) { if (!equalsFn) { return items.indexOf(item); } for (var i = 0; i < items.length; i++) { if (equalsFn(item, items[i])) { return i; } } return -1; } // calculate node's bbox from bboxes of its children function calcBBox(node, toBBox) { distBBox(node, 0, node.children.length, toBBox, node); } // min bounding rectangle of node children from k to p-1 function distBBox(node, k, p, toBBox, destNode) { if (!destNode) { destNode = createNode(null); } destNode.minX = Infinity; destNode.minY = Infinity; destNode.maxX = -Infinity; destNode.maxY = -Infinity; for (var i = k; i < p; i++) { var child = node.children[i]; extend(destNode, node.leaf ? toBBox(child) : child); } return destNode; } function extend(a, b) { a.minX = Math.min(a.minX, b.minX); a.minY = Math.min(a.minY, b.minY); a.maxX = Math.max(a.maxX, b.maxX); a.maxY = Math.max(a.maxY, b.maxY); return a; } function compareNodeMinX(a, b) { return a.minX - b.minX; } function compareNodeMinY(a, b) { return a.minY - b.minY; } function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } function enlargedArea(a, b) { return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); } function intersectionArea(a, b) { var minX = Math.max(a.minX, b.minX); var minY = Math.max(a.minY, b.minY); var maxX = Math.min(a.maxX, b.maxX); var maxY = Math.min(a.maxY, b.maxY); return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); } function contains$1(a, b) { return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY; } function intersects(a, b) { return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY; } function createNode(children) { return { children: children, height: 1, leaf: true, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other; // combines selection algorithm with binary divide & conquer approach function multiSelect(arr, left, right, n, compare) { var stack = [left, right]; while (stack.length) { right = stack.pop(); left = stack.pop(); if (right - left <= n) { continue; } var mid = left + Math.ceil((right - left) / n / 2) * n; quickselect$1(arr, mid, left, right, compare); stack.push(left, mid, mid, right); } } /** * Wrapper around rbush for use with Rectangle types. * @private */ function RectangleCollisionChecker() { this._tree = new RBush(); } function RectangleWithId() { this.minX = 0.0; this.minY = 0.0; this.maxX = 0.0; this.maxY = 0.0; this.id = ""; } RectangleWithId.fromRectangleAndId = function (id, rectangle, result) { result.minX = rectangle.west; result.minY = rectangle.south; result.maxX = rectangle.east; result.maxY = rectangle.north; result.id = id; return result; }; /** * Insert a rectangle into the collision checker. * * @param {String} id Unique string ID for the rectangle being inserted. * @param {Rectangle} rectangle A Rectangle * @private */ RectangleCollisionChecker.prototype.insert = function (id, rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("id", id); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var withId = RectangleWithId.fromRectangleAndId( id, rectangle, new RectangleWithId() ); this._tree.insert(withId); }; function idCompare(a, b) { return a.id === b.id; } var removalScratch = new RectangleWithId(); /** * Remove a rectangle from the collision checker. * * @param {String} id Unique string ID for the rectangle being removed. * @param {Rectangle} rectangle A Rectangle * @private */ RectangleCollisionChecker.prototype.remove = function (id, rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("id", id); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var withId = RectangleWithId.fromRectangleAndId( id, rectangle, removalScratch ); this._tree.remove(withId, idCompare); }; var collisionScratch = new RectangleWithId(); /** * Checks if a given rectangle collides with any of the rectangles in the collection. * * @param {Rectangle} rectangle A Rectangle that should be checked against the rectangles in the collision checker. * @returns {Boolean} Whether the rectangle collides with any of the rectangles in the collision checker. */ RectangleCollisionChecker.prototype.collides = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); var withId = RectangleWithId.fromRectangleAndId( "", rectangle, collisionScratch ); return this._tree.collides(withId); }; var cos = Math.cos; var sin = Math.sin; var sqrt = Math.sqrt; /** * @private */ var RectangleGeometryLibrary = {}; /** * @private */ RectangleGeometryLibrary.computePosition = function ( computedOptions, ellipsoid, computeST, row, col, position, st ) { var radiiSquared = ellipsoid.radiiSquared; var nwCorner = computedOptions.nwCorner; var rectangle = computedOptions.boundingRectangle; var stLatitude = nwCorner.latitude - computedOptions.granYCos * row + col * computedOptions.granXSin; var cosLatitude = cos(stLatitude); var nZ = sin(stLatitude); var kZ = radiiSquared.z * nZ; var stLongitude = nwCorner.longitude + row * computedOptions.granYSin + col * computedOptions.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); position.x = kX / gamma; position.y = kY / gamma; position.z = kZ / gamma; if (computeST) { var stNwCorner = computedOptions.stNwCorner; if (defined(stNwCorner)) { stLatitude = stNwCorner.latitude - computedOptions.stGranYCos * row + col * computedOptions.stGranXSin; stLongitude = stNwCorner.longitude + row * computedOptions.stGranYSin + col * computedOptions.stGranXCos; st.x = (stLongitude - computedOptions.stWest) * computedOptions.lonScalar; st.y = (stLatitude - computedOptions.stSouth) * computedOptions.latScalar; } else { st.x = (stLongitude - rectangle.west) * computedOptions.lonScalar; st.y = (stLatitude - rectangle.south) * computedOptions.latScalar; } } }; var rotationMatrixScratch = new Matrix2(); var nwCartesian = new Cartesian3(); var centerScratch$4 = new Cartographic(); var centerCartesian = new Cartesian3(); var proj = new GeographicProjection(); function getRotationOptions( nwCorner, rotation, granularityX, granularityY, center, width, height ) { var cosRotation = Math.cos(rotation); var granYCos = granularityY * cosRotation; var granXCos = granularityX * cosRotation; var sinRotation = Math.sin(rotation); var granYSin = granularityY * sinRotation; var granXSin = granularityX * sinRotation; nwCartesian = proj.project(nwCorner, nwCartesian); nwCartesian = Cartesian3.subtract(nwCartesian, centerCartesian, nwCartesian); var rotationMatrix = Matrix2.fromRotation(rotation, rotationMatrixScratch); nwCartesian = Matrix2.multiplyByVector( rotationMatrix, nwCartesian, nwCartesian ); nwCartesian = Cartesian3.add(nwCartesian, centerCartesian, nwCartesian); nwCorner = proj.unproject(nwCartesian, nwCorner); width -= 1; height -= 1; var latitude = nwCorner.latitude; var latitude0 = latitude + width * granXSin; var latitude1 = latitude - granYCos * height; var latitude2 = latitude - granYCos * height + width * granXSin; var north = Math.max(latitude, latitude0, latitude1, latitude2); var south = Math.min(latitude, latitude0, latitude1, latitude2); var longitude = nwCorner.longitude; var longitude0 = longitude + width * granXCos; var longitude1 = longitude + height * granYSin; var longitude2 = longitude + height * granYSin + width * granXCos; var east = Math.max(longitude, longitude0, longitude1, longitude2); var west = Math.min(longitude, longitude0, longitude1, longitude2); return { north: north, south: south, east: east, west: west, granYCos: granYCos, granYSin: granYSin, granXCos: granXCos, granXSin: granXSin, nwCorner: nwCorner, }; } /** * @private */ RectangleGeometryLibrary.computeOptions = function ( rectangle, granularity, rotation, stRotation, boundingRectangleScratch, nwCornerResult, stNwCornerResult ) { var east = rectangle.east; var west = rectangle.west; var north = rectangle.north; var south = rectangle.south; var northCap = false; var southCap = false; if (north === CesiumMath.PI_OVER_TWO) { northCap = true; } if (south === -CesiumMath.PI_OVER_TWO) { southCap = true; } var width; var height; var granularityX; var granularityY; var dx; var dy = north - south; if (west > east) { dx = CesiumMath.TWO_PI - west + east; } else { dx = east - west; } width = Math.ceil(dx / granularity) + 1; height = Math.ceil(dy / granularity) + 1; granularityX = dx / (width - 1); granularityY = dy / (height - 1); var nwCorner = Rectangle.northwest(rectangle, nwCornerResult); var center = Rectangle.center(rectangle, centerScratch$4); if (rotation !== 0 || stRotation !== 0) { if (center.longitude < nwCorner.longitude) { center.longitude += CesiumMath.TWO_PI; } centerCartesian = proj.project(center, centerCartesian); } var granYCos = granularityY; var granXCos = granularityX; var granYSin = 0.0; var granXSin = 0.0; var boundingRectangle = Rectangle.clone(rectangle, boundingRectangleScratch); var computedOptions = { granYCos: granYCos, granYSin: granYSin, granXCos: granXCos, granXSin: granXSin, nwCorner: nwCorner, boundingRectangle: boundingRectangle, width: width, height: height, northCap: northCap, southCap: southCap, }; if (rotation !== 0) { var rotationOptions = getRotationOptions( nwCorner, rotation, granularityX, granularityY, center, width, height ); north = rotationOptions.north; south = rotationOptions.south; east = rotationOptions.east; west = rotationOptions.west; //>>includeStart('debug', pragmas.debug); if ( north < -CesiumMath.PI_OVER_TWO || north > CesiumMath.PI_OVER_TWO || south < -CesiumMath.PI_OVER_TWO || south > CesiumMath.PI_OVER_TWO ) { throw new DeveloperError( "Rotated rectangle is invalid. It crosses over either the north or south pole." ); } //>>includeEnd('debug') computedOptions.granYCos = rotationOptions.granYCos; computedOptions.granYSin = rotationOptions.granYSin; computedOptions.granXCos = rotationOptions.granXCos; computedOptions.granXSin = rotationOptions.granXSin; boundingRectangle.north = north; boundingRectangle.south = south; boundingRectangle.east = east; boundingRectangle.west = west; } if (stRotation !== 0) { rotation = rotation - stRotation; var stNwCorner = Rectangle.northwest(boundingRectangle, stNwCornerResult); var stRotationOptions = getRotationOptions( stNwCorner, rotation, granularityX, granularityY, center, width, height ); computedOptions.stGranYCos = stRotationOptions.granYCos; computedOptions.stGranXCos = stRotationOptions.granXCos; computedOptions.stGranYSin = stRotationOptions.granYSin; computedOptions.stGranXSin = stRotationOptions.granXSin; computedOptions.stNwCorner = stNwCorner; computedOptions.stWest = stRotationOptions.west; computedOptions.stSouth = stRotationOptions.south; } return computedOptions; }; var positionScratch$9 = new Cartesian3(); var normalScratch$2 = new Cartesian3(); var tangentScratch = new Cartesian3(); var bitangentScratch = new Cartesian3(); var rectangleScratch$4 = new Rectangle(); var stScratch = new Cartesian2(); var bottomBoundingSphere$1 = new BoundingSphere(); var topBoundingSphere$1 = new BoundingSphere(); function createAttributes(vertexFormat, attributes) { var geo = new Geometry({ attributes: new GeometryAttributes(), primitiveType: PrimitiveType$1.TRIANGLES, }); geo.attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: attributes.positions, }); if (vertexFormat.normal) { geo.attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attributes.normals, }); } if (vertexFormat.tangent) { geo.attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attributes.tangents, }); } if (vertexFormat.bitangent) { geo.attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: attributes.bitangents, }); } return geo; } function calculateAttributes( positions, vertexFormat, ellipsoid, tangentRotationMatrix ) { var length = positions.length; var normals = vertexFormat.normal ? new Float32Array(length) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(length) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(length) : undefined; var attrIndex = 0; var bitangent = bitangentScratch; var tangent = tangentScratch; var normal = normalScratch$2; if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) { for (var i = 0; i < length; i += 3) { var p = Cartesian3.fromArray(positions, i, positionScratch$9); var attrIndex1 = attrIndex + 1; var attrIndex2 = attrIndex + 2; normal = ellipsoid.geodeticSurfaceNormal(p, normal); if (vertexFormat.tangent || vertexFormat.bitangent) { Cartesian3.cross(Cartesian3.UNIT_Z, normal, tangent); Matrix3.multiplyByVector(tangentRotationMatrix, tangent, tangent); Cartesian3.normalize(tangent, tangent); if (vertexFormat.bitangent) { Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); } } if (vertexFormat.normal) { normals[attrIndex] = normal.x; normals[attrIndex1] = normal.y; normals[attrIndex2] = normal.z; } if (vertexFormat.tangent) { tangents[attrIndex] = tangent.x; tangents[attrIndex1] = tangent.y; tangents[attrIndex2] = tangent.z; } if (vertexFormat.bitangent) { bitangents[attrIndex] = bitangent.x; bitangents[attrIndex1] = bitangent.y; bitangents[attrIndex2] = bitangent.z; } attrIndex += 3; } } return createAttributes(vertexFormat, { positions: positions, normals: normals, tangents: tangents, bitangents: bitangents, }); } var v1Scratch = new Cartesian3(); var v2Scratch = new Cartesian3(); function calculateAttributesWall(positions, vertexFormat, ellipsoid) { var length = positions.length; var normals = vertexFormat.normal ? new Float32Array(length) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(length) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(length) : undefined; var normalIndex = 0; var tangentIndex = 0; var bitangentIndex = 0; var recomputeNormal = true; var bitangent = bitangentScratch; var tangent = tangentScratch; var normal = normalScratch$2; if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) { for (var i = 0; i < length; i += 6) { var p = Cartesian3.fromArray(positions, i, positionScratch$9); var p1 = Cartesian3.fromArray(positions, (i + 6) % length, v1Scratch); if (recomputeNormal) { var p2 = Cartesian3.fromArray(positions, (i + 3) % length, v2Scratch); Cartesian3.subtract(p1, p, p1); Cartesian3.subtract(p2, p, p2); normal = Cartesian3.normalize(Cartesian3.cross(p2, p1, normal), normal); recomputeNormal = false; } if (Cartesian3.equalsEpsilon(p1, p, CesiumMath.EPSILON10)) { // if we've reached a corner recomputeNormal = true; } if (vertexFormat.tangent || vertexFormat.bitangent) { bitangent = ellipsoid.geodeticSurfaceNormal(p, bitangent); if (vertexFormat.tangent) { tangent = Cartesian3.normalize( Cartesian3.cross(bitangent, normal, tangent), tangent ); } } if (vertexFormat.normal) { normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; } if (vertexFormat.tangent) { tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; } if (vertexFormat.bitangent) { bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; } } } return createAttributes(vertexFormat, { positions: positions, normals: normals, tangents: tangents, bitangents: bitangents, }); } function constructRectangle$1(rectangleGeometry, computedOptions) { var vertexFormat = rectangleGeometry._vertexFormat; var ellipsoid = rectangleGeometry._ellipsoid; var height = computedOptions.height; var width = computedOptions.width; var northCap = computedOptions.northCap; var southCap = computedOptions.southCap; var rowStart = 0; var rowEnd = height; var rowHeight = height; var size = 0; if (northCap) { rowStart = 1; rowHeight -= 1; size += 1; } if (southCap) { rowEnd -= 1; rowHeight -= 1; size += 1; } size += width * rowHeight; var positions = vertexFormat.position ? new Float64Array(size * 3) : undefined; var textureCoordinates = vertexFormat.st ? new Float32Array(size * 2) : undefined; var posIndex = 0; var stIndex = 0; var position = positionScratch$9; var st = stScratch; var minX = Number.MAX_VALUE; var minY = Number.MAX_VALUE; var maxX = -Number.MAX_VALUE; var maxY = -Number.MAX_VALUE; for (var row = rowStart; row < rowEnd; ++row) { for (var col = 0; col < width; ++col) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, vertexFormat.st, row, col, position, st ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; if (vertexFormat.st) { textureCoordinates[stIndex++] = st.x; textureCoordinates[stIndex++] = st.y; minX = Math.min(minX, st.x); minY = Math.min(minY, st.y); maxX = Math.max(maxX, st.x); maxY = Math.max(maxY, st.y); } } } if (northCap) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, vertexFormat.st, 0, 0, position, st ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; if (vertexFormat.st) { textureCoordinates[stIndex++] = st.x; textureCoordinates[stIndex++] = st.y; minX = st.x; minY = st.y; maxX = st.x; maxY = st.y; } } if (southCap) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, vertexFormat.st, height - 1, 0, position, st ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex] = position.z; if (vertexFormat.st) { textureCoordinates[stIndex++] = st.x; textureCoordinates[stIndex] = st.y; minX = Math.min(minX, st.x); minY = Math.min(minY, st.y); maxX = Math.max(maxX, st.x); maxY = Math.max(maxY, st.y); } } if ( vertexFormat.st && (minX < 0.0 || minY < 0.0 || maxX > 1.0 || maxY > 1.0) ) { for (var k = 0; k < textureCoordinates.length; k += 2) { textureCoordinates[k] = (textureCoordinates[k] - minX) / (maxX - minX); textureCoordinates[k + 1] = (textureCoordinates[k + 1] - minY) / (maxY - minY); } } var geo = calculateAttributes( positions, vertexFormat, ellipsoid, computedOptions.tangentRotationMatrix ); var indicesSize = 6 * (width - 1) * (rowHeight - 1); if (northCap) { indicesSize += 3 * (width - 1); } if (southCap) { indicesSize += 3 * (width - 1); } var indices = IndexDatatype$1.createTypedArray(size, indicesSize); var index = 0; var indicesIndex = 0; var i; for (i = 0; i < rowHeight - 1; ++i) { for (var j = 0; j < width - 1; ++j) { var upperLeft = index; var lowerLeft = upperLeft + width; var lowerRight = lowerLeft + 1; var upperRight = upperLeft + 1; indices[indicesIndex++] = upperLeft; indices[indicesIndex++] = lowerLeft; indices[indicesIndex++] = upperRight; indices[indicesIndex++] = upperRight; indices[indicesIndex++] = lowerLeft; indices[indicesIndex++] = lowerRight; ++index; } ++index; } if (northCap || southCap) { var northIndex = size - 1; var southIndex = size - 1; if (northCap && southCap) { northIndex = size - 2; } var p1; var p2; index = 0; if (northCap) { for (i = 0; i < width - 1; i++) { p1 = index; p2 = p1 + 1; indices[indicesIndex++] = northIndex; indices[indicesIndex++] = p1; indices[indicesIndex++] = p2; ++index; } } if (southCap) { index = (rowHeight - 1) * width; for (i = 0; i < width - 1; i++) { p1 = index; p2 = p1 + 1; indices[indicesIndex++] = p1; indices[indicesIndex++] = southIndex; indices[indicesIndex++] = p2; ++index; } } } geo.indices = indices; if (vertexFormat.st) { geo.attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } return geo; } function addWallPositions( wallPositions, posIndex, i, topPositions, bottomPositions ) { wallPositions[posIndex++] = topPositions[i]; wallPositions[posIndex++] = topPositions[i + 1]; wallPositions[posIndex++] = topPositions[i + 2]; wallPositions[posIndex++] = bottomPositions[i]; wallPositions[posIndex++] = bottomPositions[i + 1]; wallPositions[posIndex] = bottomPositions[i + 2]; return wallPositions; } function addWallTextureCoordinates(wallTextures, stIndex, i, st) { wallTextures[stIndex++] = st[i]; wallTextures[stIndex++] = st[i + 1]; wallTextures[stIndex++] = st[i]; wallTextures[stIndex] = st[i + 1]; return wallTextures; } var scratchVertexFormat$1 = new VertexFormat(); function constructExtrudedRectangle$1(rectangleGeometry, computedOptions) { var shadowVolume = rectangleGeometry._shadowVolume; var offsetAttributeValue = rectangleGeometry._offsetAttribute; var vertexFormat = rectangleGeometry._vertexFormat; var minHeight = rectangleGeometry._extrudedHeight; var maxHeight = rectangleGeometry._surfaceHeight; var ellipsoid = rectangleGeometry._ellipsoid; var height = computedOptions.height; var width = computedOptions.width; var i; if (shadowVolume) { var newVertexFormat = VertexFormat.clone(vertexFormat, scratchVertexFormat$1); newVertexFormat.normal = true; rectangleGeometry._vertexFormat = newVertexFormat; } var topBottomGeo = constructRectangle$1(rectangleGeometry, computedOptions); if (shadowVolume) { rectangleGeometry._vertexFormat = vertexFormat; } var topPositions = PolygonPipeline.scaleToGeodeticHeight( topBottomGeo.attributes.position.values, maxHeight, ellipsoid, false ); topPositions = new Float64Array(topPositions); var length = topPositions.length; var newLength = length * 2; var positions = new Float64Array(newLength); positions.set(topPositions); var bottomPositions = PolygonPipeline.scaleToGeodeticHeight( topBottomGeo.attributes.position.values, minHeight, ellipsoid ); positions.set(bottomPositions, length); topBottomGeo.attributes.position.values = positions; var normals = vertexFormat.normal ? new Float32Array(newLength) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(newLength) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(newLength) : undefined; var textures = vertexFormat.st ? new Float32Array((newLength / 3) * 2) : undefined; var topSt; var topNormals; if (vertexFormat.normal) { topNormals = topBottomGeo.attributes.normal.values; normals.set(topNormals); for (i = 0; i < length; i++) { topNormals[i] = -topNormals[i]; } normals.set(topNormals, length); topBottomGeo.attributes.normal.values = normals; } if (shadowVolume) { topNormals = topBottomGeo.attributes.normal.values; if (!vertexFormat.normal) { topBottomGeo.attributes.normal = undefined; } var extrudeNormals = new Float32Array(newLength); for (i = 0; i < length; i++) { topNormals[i] = -topNormals[i]; } extrudeNormals.set(topNormals, length); //only get normals for bottom layer that's going to be pushed down topBottomGeo.attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: extrudeNormals, }); } var offsetValue; var hasOffsets = defined(offsetAttributeValue); if (hasOffsets) { var size = (length / 3) * 2; var offsetAttribute = new Uint8Array(size); if (offsetAttributeValue === GeometryOffsetAttribute$1.TOP) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else { offsetValue = offsetAttributeValue === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } topBottomGeo.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, }); } if (vertexFormat.tangent) { var topTangents = topBottomGeo.attributes.tangent.values; tangents.set(topTangents); for (i = 0; i < length; i++) { topTangents[i] = -topTangents[i]; } tangents.set(topTangents, length); topBottomGeo.attributes.tangent.values = tangents; } if (vertexFormat.bitangent) { var topBitangents = topBottomGeo.attributes.bitangent.values; bitangents.set(topBitangents); bitangents.set(topBitangents, length); topBottomGeo.attributes.bitangent.values = bitangents; } if (vertexFormat.st) { topSt = topBottomGeo.attributes.st.values; textures.set(topSt); textures.set(topSt, (length / 3) * 2); topBottomGeo.attributes.st.values = textures; } var indices = topBottomGeo.indices; var indicesLength = indices.length; var posLength = length / 3; var newIndices = IndexDatatype$1.createTypedArray( newLength / 3, indicesLength * 2 ); newIndices.set(indices); for (i = 0; i < indicesLength; i += 3) { newIndices[i + indicesLength] = indices[i + 2] + posLength; newIndices[i + 1 + indicesLength] = indices[i + 1] + posLength; newIndices[i + 2 + indicesLength] = indices[i] + posLength; } topBottomGeo.indices = newIndices; var northCap = computedOptions.northCap; var southCap = computedOptions.southCap; var rowHeight = height; var widthMultiplier = 2; var perimeterPositions = 0; var corners = 4; var dupliateCorners = 4; if (northCap) { widthMultiplier -= 1; rowHeight -= 1; perimeterPositions += 1; corners -= 2; dupliateCorners -= 1; } if (southCap) { widthMultiplier -= 1; rowHeight -= 1; perimeterPositions += 1; corners -= 2; dupliateCorners -= 1; } perimeterPositions += widthMultiplier * width + 2 * rowHeight - corners; var wallCount = (perimeterPositions + dupliateCorners) * 2; var wallPositions = new Float64Array(wallCount * 3); var wallExtrudeNormals = shadowVolume ? new Float32Array(wallCount * 3) : undefined; var wallOffsetAttribute = hasOffsets ? new Uint8Array(wallCount) : undefined; var wallTextures = vertexFormat.st ? new Float32Array(wallCount * 2) : undefined; var computeTopOffsets = offsetAttributeValue === GeometryOffsetAttribute$1.TOP; if (hasOffsets && !computeTopOffsets) { offsetValue = offsetAttributeValue === GeometryOffsetAttribute$1.ALL ? 1 : 0; wallOffsetAttribute = arrayFill(wallOffsetAttribute, offsetValue); } var posIndex = 0; var stIndex = 0; var extrudeNormalIndex = 0; var wallOffsetIndex = 0; var area = width * rowHeight; var threeI; for (i = 0; i < area; i += width) { threeI = i * 3; wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, i * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } if (!southCap) { for (i = area - width; i < area; i++) { threeI = i * 3; wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, i * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } } else { var southIndex = northCap ? area + 1 : area; threeI = southIndex * 3; for (i = 0; i < 2; i++) { // duplicate corner points wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, southIndex * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } } for (i = area - 1; i > 0; i -= width) { threeI = i * 3; wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, i * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } if (!northCap) { for (i = width - 1; i >= 0; i--) { threeI = i * 3; wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, i * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } } else { var northIndex = area; threeI = northIndex * 3; for (i = 0; i < 2; i++) { // duplicate corner points wallPositions = addWallPositions( wallPositions, posIndex, threeI, topPositions, bottomPositions ); posIndex += 6; if (vertexFormat.st) { wallTextures = addWallTextureCoordinates( wallTextures, stIndex, northIndex * 2, topSt ); stIndex += 4; } if (shadowVolume) { extrudeNormalIndex += 3; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } if (computeTopOffsets) { wallOffsetAttribute[wallOffsetIndex++] = 1; wallOffsetIndex += 1; } } } var geo = calculateAttributesWall(wallPositions, vertexFormat, ellipsoid); if (vertexFormat.st) { geo.attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: wallTextures, }); } if (shadowVolume) { geo.attributes.extrudeDirection = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: wallExtrudeNormals, }); } if (hasOffsets) { geo.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: wallOffsetAttribute, }); } var wallIndices = IndexDatatype$1.createTypedArray( wallCount, perimeterPositions * 6 ); var upperLeft; var lowerLeft; var lowerRight; var upperRight; length = wallPositions.length / 3; var index = 0; for (i = 0; i < length - 1; i += 2) { upperLeft = i; upperRight = (upperLeft + 2) % length; var p1 = Cartesian3.fromArray(wallPositions, upperLeft * 3, v1Scratch); var p2 = Cartesian3.fromArray(wallPositions, upperRight * 3, v2Scratch); if (Cartesian3.equalsEpsilon(p1, p2, CesiumMath.EPSILON10)) { continue; } lowerLeft = (upperLeft + 1) % length; lowerRight = (lowerLeft + 2) % length; wallIndices[index++] = upperLeft; wallIndices[index++] = lowerLeft; wallIndices[index++] = upperRight; wallIndices[index++] = upperRight; wallIndices[index++] = lowerLeft; wallIndices[index++] = lowerRight; } geo.indices = wallIndices; geo = GeometryPipeline.combineInstances([ new GeometryInstance({ geometry: topBottomGeo, }), new GeometryInstance({ geometry: geo, }), ]); return geo[0]; } var scratchRectanglePoints = [ new Cartesian3(), new Cartesian3(), new Cartesian3(), new Cartesian3(), ]; var nwScratch$1 = new Cartographic(); var stNwScratch = new Cartographic(); function computeRectangle(rectangle, granularity, rotation, ellipsoid, result) { if (rotation === 0.0) { return Rectangle.clone(rectangle, result); } var computedOptions = RectangleGeometryLibrary.computeOptions( rectangle, granularity, rotation, 0, rectangleScratch$4, nwScratch$1 ); var height = computedOptions.height; var width = computedOptions.width; var positions = scratchRectanglePoints; RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, 0, 0, positions[0] ); RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, 0, width - 1, positions[1] ); RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, height - 1, 0, positions[2] ); RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, height - 1, width - 1, positions[3] ); return Rectangle.fromCartesianArray(positions, ellipsoid, result); } /** * A description of a cartographic rectangle on an ellipsoid centered at the origin. Rectangle geometry can be rendered with both {@link Primitive} and {@link GroundPrimitive}. * * @alias RectangleGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle 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 distance in meters between the rectangle and the ellipsoid surface. * @param {Number} [options.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise. * @param {Number} [options.stRotation=0.0] The rotation of the texture coordinates, in radians. A positive rotation is counter-clockwise. * @param {Number} [options.extrudedHeight] The distance in meters between the rectangle's extruded face and the ellipsoid surface. * * @exception {DeveloperError} options.rectangle.north must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} options.rectangle.south must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} options.rectangle.east must be in the interval [-Pi, Pi]. * @exception {DeveloperError} options.rectangle.west must be in the interval [-Pi, Pi]. * @exception {DeveloperError} options.rectangle.north must be greater than options.rectangle.south. * * @see RectangleGeometry#createGeometry * * @demo {@link https://sandcastle.cesium.com/index.html?src=Rectangle.html|Cesium Sandcastle Rectangle Demo} * * @example * // 1. create a rectangle * var rectangle = new Cesium.RectangleGeometry({ * ellipsoid : Cesium.Ellipsoid.WGS84, * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0), * height : 10000.0 * }); * var geometry = Cesium.RectangleGeometry.createGeometry(rectangle); * * // 2. create an extruded rectangle without a top * var rectangle = new Cesium.RectangleGeometry({ * ellipsoid : Cesium.Ellipsoid.WGS84, * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0), * height : 10000.0, * extrudedHeight: 300000 * }); * var geometry = Cesium.RectangleGeometry.createGeometry(rectangle); */ function RectangleGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var rectangle = options.rectangle; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Rectangle.validate(rectangle); if (rectangle.north < rectangle.south) { throw new DeveloperError( "options.rectangle.north must be greater than or equal to options.rectangle.south" ); } //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._rectangle = Rectangle.clone(rectangle); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._ellipsoid = Ellipsoid.clone( defaultValue(options.ellipsoid, Ellipsoid.WGS84) ); this._surfaceHeight = Math.max(height, extrudedHeight); this._rotation = defaultValue(options.rotation, 0.0); this._stRotation = defaultValue(options.stRotation, 0.0); this._vertexFormat = VertexFormat.clone( defaultValue(options.vertexFormat, VertexFormat.DEFAULT) ); this._extrudedHeight = Math.min(height, extrudedHeight); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = "createRectangleGeometry"; this._offsetAttribute = options.offsetAttribute; this._rotatedRectangle = undefined; this._textureCoordinateRotationPoints = undefined; } /** * The number of elements used to pack the object into an array. * @type {Number} */ RectangleGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 7; /** * Stores the provided instance into the provided array. * * @param {RectangleGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ RectangleGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Rectangle.pack(value._rectangle, array, startingIndex); startingIndex += Rectangle.packedLength; Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex++] = value._granularity; array[startingIndex++] = value._surfaceHeight; array[startingIndex++] = value._rotation; array[startingIndex++] = value._stRotation; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchRectangle$7 = new Rectangle(); var scratchEllipsoid$4 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchOptions$5 = { rectangle: scratchRectangle$7, ellipsoid: scratchEllipsoid$4, vertexFormat: scratchVertexFormat$1, granularity: undefined, height: undefined, rotation: undefined, stRotation: undefined, extrudedHeight: undefined, shadowVolume: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {RectangleGeometry} [result] The object into which to store the result. * @returns {RectangleGeometry} The modified result parameter or a new RectangleGeometry instance if one was not provided. */ RectangleGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var rectangle = Rectangle.unpack(array, startingIndex, scratchRectangle$7); startingIndex += Rectangle.packedLength; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$4); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat$1 ); startingIndex += VertexFormat.packedLength; var granularity = array[startingIndex++]; var surfaceHeight = array[startingIndex++]; var rotation = array[startingIndex++]; var stRotation = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var shadowVolume = array[startingIndex++] === 1.0; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$5.granularity = granularity; scratchOptions$5.height = surfaceHeight; scratchOptions$5.rotation = rotation; scratchOptions$5.stRotation = stRotation; scratchOptions$5.extrudedHeight = extrudedHeight; scratchOptions$5.shadowVolume = shadowVolume; scratchOptions$5.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new RectangleGeometry(scratchOptions$5); } result._rectangle = Rectangle.clone(rectangle, result._rectangle); result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._granularity = granularity; result._surfaceHeight = surfaceHeight; result._rotation = rotation; result._stRotation = stRotation; result._extrudedHeight = extrudedHeight; result._shadowVolume = shadowVolume; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; /** * Computes the bounding rectangle based on the provided options * * @param {Object} options Object with the following properties: * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle 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.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise. * @param {Rectangle} [result] An object in which to store the result. * * @returns {Rectangle} The result rectangle */ RectangleGeometry.computeRectangle = function (options, result) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var rectangle = options.rectangle; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); Rectangle.validate(rectangle); if (rectangle.north < rectangle.south) { throw new DeveloperError( "options.rectangle.north must be greater than or equal to options.rectangle.south" ); } //>>includeEnd('debug'); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var rotation = defaultValue(options.rotation, 0.0); return computeRectangle(rectangle, granularity, rotation, ellipsoid, result); }; var tangentRotationMatrixScratch = new Matrix3(); var quaternionScratch = new Quaternion(); var centerScratch$3 = new Cartographic(); /** * Computes the geometric representation of a rectangle, including its vertices, indices, and a bounding sphere. * * @param {RectangleGeometry} rectangleGeometry A description of the rectangle. * @returns {Geometry|undefined} The computed vertices and indices. * * @exception {DeveloperError} Rotated rectangle is invalid. */ RectangleGeometry.createGeometry = function (rectangleGeometry) { if ( CesiumMath.equalsEpsilon( rectangleGeometry._rectangle.north, rectangleGeometry._rectangle.south, CesiumMath.EPSILON10 ) || CesiumMath.equalsEpsilon( rectangleGeometry._rectangle.east, rectangleGeometry._rectangle.west, CesiumMath.EPSILON10 ) ) { return undefined; } var rectangle = rectangleGeometry._rectangle; var ellipsoid = rectangleGeometry._ellipsoid; var rotation = rectangleGeometry._rotation; var stRotation = rectangleGeometry._stRotation; var vertexFormat = rectangleGeometry._vertexFormat; var computedOptions = RectangleGeometryLibrary.computeOptions( rectangle, rectangleGeometry._granularity, rotation, stRotation, rectangleScratch$4, nwScratch$1, stNwScratch ); var tangentRotationMatrix = tangentRotationMatrixScratch; if (stRotation !== 0 || rotation !== 0) { var center = Rectangle.center(rectangle, centerScratch$3); var axis = ellipsoid.geodeticSurfaceNormalCartographic(center, v1Scratch); Quaternion.fromAxisAngle(axis, -stRotation, quaternionScratch); Matrix3.fromQuaternion(quaternionScratch, tangentRotationMatrix); } else { Matrix3.clone(Matrix3.IDENTITY, tangentRotationMatrix); } var surfaceHeight = rectangleGeometry._surfaceHeight; var extrudedHeight = rectangleGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( surfaceHeight, extrudedHeight, 0, CesiumMath.EPSILON2 ); computedOptions.lonScalar = 1.0 / rectangleGeometry._rectangle.width; computedOptions.latScalar = 1.0 / rectangleGeometry._rectangle.height; computedOptions.tangentRotationMatrix = tangentRotationMatrix; var geometry; var boundingSphere; rectangle = rectangleGeometry._rectangle; if (extrude) { geometry = constructExtrudedRectangle$1(rectangleGeometry, computedOptions); var topBS = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, surfaceHeight, topBoundingSphere$1 ); var bottomBS = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, extrudedHeight, bottomBoundingSphere$1 ); boundingSphere = BoundingSphere.union(topBS, bottomBS); } else { geometry = constructRectangle$1(rectangleGeometry, computedOptions); geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( geometry.attributes.position.values, surfaceHeight, ellipsoid, false ); if (defined(rectangleGeometry._offsetAttribute)) { var length = geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); var offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } boundingSphere = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, surfaceHeight ); } if (!vertexFormat.position) { delete geometry.attributes.position; } return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: geometry.primitiveType, boundingSphere: boundingSphere, offsetAttribute: rectangleGeometry._offsetAttribute, }); }; /** * @private */ RectangleGeometry.createShadowVolume = function ( rectangleGeometry, minHeightFunc, maxHeightFunc ) { var granularity = rectangleGeometry._granularity; var ellipsoid = rectangleGeometry._ellipsoid; var minHeight = minHeightFunc(granularity, ellipsoid); var maxHeight = maxHeightFunc(granularity, ellipsoid); return new RectangleGeometry({ rectangle: rectangleGeometry._rectangle, rotation: rectangleGeometry._rotation, ellipsoid: ellipsoid, stRotation: rectangleGeometry._stRotation, granularity: granularity, extrudedHeight: maxHeight, height: minHeight, vertexFormat: VertexFormat.POSITION_ONLY, shadowVolume: true, }); }; var unrotatedTextureRectangleScratch = new Rectangle(); var points2DScratch$1 = [new Cartesian2(), new Cartesian2(), new Cartesian2()]; var rotation2DScratch = new Matrix2(); var rectangleCenterScratch$2 = new Cartographic(); function textureCoordinateRotationPoints(rectangleGeometry) { if (rectangleGeometry._stRotation === 0.0) { return [0, 0, 0, 1, 1, 0]; } var rectangle = Rectangle.clone( rectangleGeometry._rectangle, unrotatedTextureRectangleScratch ); var granularity = rectangleGeometry._granularity; var ellipsoid = rectangleGeometry._ellipsoid; // Rotate to align the texture coordinates with ENU var rotation = rectangleGeometry._rotation - rectangleGeometry._stRotation; var unrotatedTextureRectangle = computeRectangle( rectangle, granularity, rotation, ellipsoid, unrotatedTextureRectangleScratch ); // Assume a computed "east-north" texture coordinate system based on spherical or planar tricks, bounded by `boundingRectangle`. // The "desired" texture coordinate system forms an oriented rectangle (un-oriented computed) around the geometry that completely and tightly bounds it. // We want to map from the "east-north" texture coordinate system into the "desired" system using a pair of lines (analagous planes in 2D) // Compute 3 corners of the "desired" texture coordinate system in "east-north" texture space by the following in cartographic space: // - rotate 3 of the corners in unrotatedTextureRectangle by stRotation around the center of the bounding rectangle // - apply the "east-north" system's normalization formula to the rotated cartographics, even though this is likely to produce values outside [0-1]. // This gives us a set of points in the "east-north" texture coordinate system that can be used to map "east-north" texture coordinates to "desired." var points2D = points2DScratch$1; points2D[0].x = unrotatedTextureRectangle.west; points2D[0].y = unrotatedTextureRectangle.south; points2D[1].x = unrotatedTextureRectangle.west; points2D[1].y = unrotatedTextureRectangle.north; points2D[2].x = unrotatedTextureRectangle.east; points2D[2].y = unrotatedTextureRectangle.south; var boundingRectangle = rectangleGeometry.rectangle; var toDesiredInComputed = Matrix2.fromRotation( rectangleGeometry._stRotation, rotation2DScratch ); var boundingRectangleCenter = Rectangle.center( boundingRectangle, rectangleCenterScratch$2 ); for (var i = 0; i < 3; ++i) { var point2D = points2D[i]; point2D.x -= boundingRectangleCenter.longitude; point2D.y -= boundingRectangleCenter.latitude; Matrix2.multiplyByVector(toDesiredInComputed, point2D, point2D); point2D.x += boundingRectangleCenter.longitude; point2D.y += boundingRectangleCenter.latitude; // Convert point into east-north texture coordinate space point2D.x = (point2D.x - boundingRectangle.west) / boundingRectangle.width; point2D.y = (point2D.y - boundingRectangle.south) / boundingRectangle.height; } var minXYCorner = points2D[0]; var maxYCorner = points2D[1]; var maxXCorner = points2D[2]; var result = new Array(6); Cartesian2.pack(minXYCorner, result); Cartesian2.pack(maxYCorner, result, 2); Cartesian2.pack(maxXCorner, result, 4); return result; } Object.defineProperties(RectangleGeometry.prototype, { /** * @private */ rectangle: { get: function () { if (!defined(this._rotatedRectangle)) { this._rotatedRectangle = computeRectangle( this._rectangle, this._granularity, this._rotation, this._ellipsoid ); } return this._rotatedRectangle; }, }, /** * For remapping texture coordinates when rendering RectangleGeometries as GroundPrimitives. * This version permits skew in textures by computing offsets directly in cartographic space and * more accurately approximates rendering RectangleGeometries with height as standard Primitives. * @see Geometry#_textureCoordinateRotationPoints * @private */ textureCoordinateRotationPoints: { get: function () { if (!defined(this._textureCoordinateRotationPoints)) { this._textureCoordinateRotationPoints = textureCoordinateRotationPoints( this ); } return this._textureCoordinateRotationPoints; }, }, }); var bottomBoundingSphere = new BoundingSphere(); var topBoundingSphere = new BoundingSphere(); var positionScratch$8 = new Cartesian3(); var rectangleScratch$3 = new Rectangle(); function constructRectangle(geometry, computedOptions) { var ellipsoid = geometry._ellipsoid; var height = computedOptions.height; var width = computedOptions.width; var northCap = computedOptions.northCap; var southCap = computedOptions.southCap; var rowHeight = height; var widthMultiplier = 2; var size = 0; var corners = 4; if (northCap) { widthMultiplier -= 1; rowHeight -= 1; size += 1; corners -= 2; } if (southCap) { widthMultiplier -= 1; rowHeight -= 1; size += 1; corners -= 2; } size += widthMultiplier * width + 2 * rowHeight - corners; var positions = new Float64Array(size * 3); var posIndex = 0; var row = 0; var col; var position = positionScratch$8; if (northCap) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, row, 0, position ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; } else { for (col = 0; col < width; col++) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, row, col, position ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; } } col = width - 1; for (row = 1; row < height; row++) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, row, col, position ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; } row = height - 1; if (!southCap) { // if southCap is true, we dont need to add any more points because the south pole point was added by the iteration above for (col = width - 2; col >= 0; col--) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, row, col, position ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; } } col = 0; for (row = height - 2; row > 0; row--) { RectangleGeometryLibrary.computePosition( computedOptions, ellipsoid, false, row, col, position ); positions[posIndex++] = position.x; positions[posIndex++] = position.y; positions[posIndex++] = position.z; } var indicesSize = (positions.length / 3) * 2; var indices = IndexDatatype$1.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; var geo = new Geometry({ attributes: new GeometryAttributes(), primitiveType: PrimitiveType$1.LINES, }); geo.attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); geo.indices = indices; return geo; } function constructExtrudedRectangle(rectangleGeometry, computedOptions) { var surfaceHeight = rectangleGeometry._surfaceHeight; var extrudedHeight = rectangleGeometry._extrudedHeight; var ellipsoid = rectangleGeometry._ellipsoid; var minHeight = extrudedHeight; var maxHeight = surfaceHeight; var geo = constructRectangle(rectangleGeometry, computedOptions); var height = computedOptions.height; var width = computedOptions.width; var topPositions = PolygonPipeline.scaleToGeodeticHeight( geo.attributes.position.values, maxHeight, ellipsoid, false ); var length = topPositions.length; var positions = new Float64Array(length * 2); positions.set(topPositions); var bottomPositions = PolygonPipeline.scaleToGeodeticHeight( geo.attributes.position.values, minHeight, ellipsoid ); positions.set(bottomPositions, length); geo.attributes.position.values = positions; var northCap = computedOptions.northCap; var southCap = computedOptions.southCap; var corners = 4; if (northCap) { corners -= 1; } if (southCap) { corners -= 1; } var indicesSize = (positions.length / 3 + corners) * 2; var indices = IndexDatatype$1.createTypedArray( positions.length / 3, indicesSize ); 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; var bottomCorner; if (northCap) { bottomCorner = height - 1; } else { var topRightCorner = width - 1; indices[index++] = topRightCorner; indices[index++] = topRightCorner + length; bottomCorner = width + height - 2; } indices[index++] = bottomCorner; indices[index++] = bottomCorner + length; if (!southCap) { var bottomLeftCorner = width + bottomCorner - 1; indices[index++] = bottomLeftCorner; indices[index] = bottomLeftCorner + length; } geo.indices = indices; return geo; } /** * A description of the outline of a a cartographic rectangle on an ellipsoid centered at the origin. * * @alias RectangleOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Rectangle} options.rectangle A cartographic rectangle with north, south, east and west properties in radians. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the rectangle 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 distance in meters between the rectangle and the ellipsoid surface. * @param {Number} [options.rotation=0.0] The rotation of the rectangle, in radians. A positive rotation is counter-clockwise. * @param {Number} [options.extrudedHeight] The distance in meters between the rectangle's extruded face and the ellipsoid surface. * * @exception {DeveloperError} options.rectangle.north must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} options.rectangle.south must be in the interval [-Pi/2, Pi/2]. * @exception {DeveloperError} options.rectangle.east must be in the interval [-Pi, Pi]. * @exception {DeveloperError} options.rectangle.west must be in the interval [-Pi, Pi]. * @exception {DeveloperError} options.rectangle.north must be greater than rectangle.south. * * @see RectangleOutlineGeometry#createGeometry * * @example * var rectangle = new Cesium.RectangleOutlineGeometry({ * ellipsoid : Cesium.Ellipsoid.WGS84, * rectangle : Cesium.Rectangle.fromDegrees(-80.0, 39.0, -74.0, 42.0), * height : 10000.0 * }); * var geometry = Cesium.RectangleOutlineGeometry.createGeometry(rectangle); */ function RectangleOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var rectangle = options.rectangle; var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); var rotation = defaultValue(options.rotation, 0.0); //>>includeStart('debug', pragmas.debug); if (!defined(rectangle)) { throw new DeveloperError("rectangle is required."); } Rectangle.validate(rectangle); if (rectangle.north < rectangle.south) { throw new DeveloperError( "options.rectangle.north must be greater than options.rectangle.south" ); } //>>includeEnd('debug'); var height = defaultValue(options.height, 0.0); var extrudedHeight = defaultValue(options.extrudedHeight, height); this._rectangle = Rectangle.clone(rectangle); this._granularity = granularity; this._ellipsoid = ellipsoid; this._surfaceHeight = Math.max(height, extrudedHeight); this._rotation = rotation; this._extrudedHeight = Math.min(height, extrudedHeight); this._offsetAttribute = options.offsetAttribute; this._workerName = "createRectangleOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ RectangleOutlineGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + 5; /** * Stores the provided instance into the provided array. * * @param {RectangleOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ RectangleOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); Rectangle.pack(value._rectangle, array, startingIndex); startingIndex += Rectangle.packedLength; Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._granularity; array[startingIndex++] = value._surfaceHeight; array[startingIndex++] = value._rotation; array[startingIndex++] = value._extrudedHeight; array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; var scratchRectangle$6 = new Rectangle(); var scratchEllipsoid$3 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchOptions$4 = { rectangle: scratchRectangle$6, ellipsoid: scratchEllipsoid$3, granularity: undefined, height: undefined, rotation: undefined, extrudedHeight: undefined, offsetAttribute: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {RectangleOutlineGeometry} [result] The object into which to store the result. * @returns {RectangleOutlineGeometry} The modified result parameter or a new Quaternion instance if one was not provided. */ RectangleOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var rectangle = Rectangle.unpack(array, startingIndex, scratchRectangle$6); startingIndex += Rectangle.packedLength; var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$3); startingIndex += Ellipsoid.packedLength; var granularity = array[startingIndex++]; var height = array[startingIndex++]; var rotation = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions$4.granularity = granularity; scratchOptions$4.height = height; scratchOptions$4.rotation = rotation; scratchOptions$4.extrudedHeight = extrudedHeight; scratchOptions$4.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new RectangleOutlineGeometry(scratchOptions$4); } result._rectangle = Rectangle.clone(rectangle, result._rectangle); result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._surfaceHeight = height; result._rotation = rotation; result._extrudedHeight = extrudedHeight; result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; var nwScratch = new Cartographic(); /** * Computes the geometric representation of an outline of a rectangle, including its vertices, indices, and a bounding sphere. * * @param {RectangleOutlineGeometry} rectangleGeometry A description of the rectangle outline. * @returns {Geometry|undefined} The computed vertices and indices. * * @exception {DeveloperError} Rotated rectangle is invalid. */ RectangleOutlineGeometry.createGeometry = function (rectangleGeometry) { var rectangle = rectangleGeometry._rectangle; var ellipsoid = rectangleGeometry._ellipsoid; var computedOptions = RectangleGeometryLibrary.computeOptions( rectangle, rectangleGeometry._granularity, rectangleGeometry._rotation, 0, rectangleScratch$3, nwScratch ); var geometry; var boundingSphere; if ( CesiumMath.equalsEpsilon( rectangle.north, rectangle.south, CesiumMath.EPSILON10 ) || CesiumMath.equalsEpsilon( rectangle.east, rectangle.west, CesiumMath.EPSILON10 ) ) { return undefined; } var surfaceHeight = rectangleGeometry._surfaceHeight; var extrudedHeight = rectangleGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon( surfaceHeight, extrudedHeight, 0, CesiumMath.EPSILON2 ); var offsetValue; if (extrude) { geometry = constructExtrudedRectangle(rectangleGeometry, computedOptions); if (defined(rectangleGeometry._offsetAttribute)) { var size = geometry.attributes.position.values.length / 3; var offsetAttribute = new Uint8Array(size); if (rectangleGeometry._offsetAttribute === GeometryOffsetAttribute$1.TOP) { offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); } else { offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; offsetAttribute = arrayFill(offsetAttribute, offsetValue); } geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: offsetAttribute, }); } var topBS = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, surfaceHeight, topBoundingSphere ); var bottomBS = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, extrudedHeight, bottomBoundingSphere ); boundingSphere = BoundingSphere.union(topBS, bottomBS); } else { geometry = constructRectangle(rectangleGeometry, computedOptions); geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight( geometry.attributes.position.values, surfaceHeight, ellipsoid, false ); if (defined(rectangleGeometry._offsetAttribute)) { var length = geometry.attributes.position.values.length; var applyOffset = new Uint8Array(length / 3); offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute$1.NONE ? 0 : 1; arrayFill(applyOffset, offsetValue); geometry.attributes.applyOffset = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1, values: applyOffset, }); } boundingSphere = BoundingSphere.fromRectangle3D( rectangle, ellipsoid, surfaceHeight ); } return new Geometry({ attributes: geometry.attributes, indices: geometry.indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: boundingSphere, offsetAttribute: rectangleGeometry._offsetAttribute, }); }; /** * Constants for identifying well-known reference frames. * * @enum {Number} */ var ReferenceFrame = { /** * The fixed frame. * * @type {Number} * @constant */ FIXED: 0, /** * The inertial frame. * * @type {Number} * @constant */ INERTIAL: 1, }; var ReferenceFrame$1 = Object.freeze(ReferenceFrame); /** * This enumerated type is for classifying mouse events: down, up, click, double click, move and move while a button is held down. * * @enum {Number} */ var ScreenSpaceEventType = { /** * Represents a mouse left button down event. * * @type {Number} * @constant */ LEFT_DOWN: 0, /** * Represents a mouse left button up event. * * @type {Number} * @constant */ LEFT_UP: 1, /** * Represents a mouse left click event. * * @type {Number} * @constant */ LEFT_CLICK: 2, /** * Represents a mouse left double click event. * * @type {Number} * @constant */ LEFT_DOUBLE_CLICK: 3, /** * Represents a mouse left button down event. * * @type {Number} * @constant */ RIGHT_DOWN: 5, /** * Represents a mouse right button up event. * * @type {Number} * @constant */ RIGHT_UP: 6, /** * Represents a mouse right click event. * * @type {Number} * @constant */ RIGHT_CLICK: 7, /** * Represents a mouse middle button down event. * * @type {Number} * @constant */ MIDDLE_DOWN: 10, /** * Represents a mouse middle button up event. * * @type {Number} * @constant */ MIDDLE_UP: 11, /** * Represents a mouse middle click event. * * @type {Number} * @constant */ MIDDLE_CLICK: 12, /** * Represents a mouse move event. * * @type {Number} * @constant */ MOUSE_MOVE: 15, /** * Represents a mouse wheel event. * * @type {Number} * @constant */ WHEEL: 16, /** * Represents the start of a two-finger event on a touch surface. * * @type {Number} * @constant */ PINCH_START: 17, /** * Represents the end of a two-finger event on a touch surface. * * @type {Number} * @constant */ PINCH_END: 18, /** * Represents a change of a two-finger event on a touch surface. * * @type {Number} * @constant */ PINCH_MOVE: 19, }; var ScreenSpaceEventType$1 = Object.freeze(ScreenSpaceEventType); function getPosition$1(screenSpaceEventHandler, event, result) { var element = screenSpaceEventHandler._element; if (element === document) { result.x = event.clientX; result.y = event.clientY; return result; } var rect = element.getBoundingClientRect(); result.x = event.clientX - rect.left; result.y = event.clientY - rect.top; return result; } function getInputEventKey(type, modifier) { var key = type; if (defined(modifier)) { key += "+" + modifier; } return key; } function getModifier(event) { if (event.shiftKey) { return KeyboardEventModifier$1.SHIFT; } else if (event.ctrlKey) { return KeyboardEventModifier$1.CTRL; } else if (event.altKey) { return KeyboardEventModifier$1.ALT; } return undefined; } var MouseButton = { LEFT: 0, MIDDLE: 1, RIGHT: 2, }; function registerListener(screenSpaceEventHandler, domType, element, callback) { function listener(e) { callback(screenSpaceEventHandler, e); } if (FeatureDetection.isInternetExplorer()) { element.addEventListener(domType, listener, false); } else { element.addEventListener(domType, listener, { capture: false, passive: false, }); } screenSpaceEventHandler._removalFunctions.push(function () { element.removeEventListener(domType, listener, false); }); } function registerListeners(screenSpaceEventHandler) { var element = screenSpaceEventHandler._element; // some listeners may be registered on the document, so we still get events even after // leaving the bounds of element. // this is affected by the existence of an undocumented disableRootEvents property on element. var alternateElement = !defined(element.disableRootEvents) ? document : element; if (FeatureDetection.supportsPointerEvents()) { registerListener( screenSpaceEventHandler, "pointerdown", element, handlePointerDown ); registerListener( screenSpaceEventHandler, "pointerup", element, handlePointerUp ); registerListener( screenSpaceEventHandler, "pointermove", element, handlePointerMove ); registerListener( screenSpaceEventHandler, "pointercancel", element, handlePointerUp ); } else { registerListener( screenSpaceEventHandler, "mousedown", element, handleMouseDown ); registerListener( screenSpaceEventHandler, "mouseup", alternateElement, handleMouseUp ); registerListener( screenSpaceEventHandler, "mousemove", alternateElement, handleMouseMove ); registerListener( screenSpaceEventHandler, "touchstart", element, handleTouchStart ); registerListener( screenSpaceEventHandler, "touchend", alternateElement, handleTouchEnd ); registerListener( screenSpaceEventHandler, "touchmove", alternateElement, handleTouchMove ); registerListener( screenSpaceEventHandler, "touchcancel", alternateElement, handleTouchEnd ); } registerListener( screenSpaceEventHandler, "dblclick", element, handleDblClick ); // detect available wheel event var wheelEvent; if ("onwheel" in element) { // spec event type wheelEvent = "wheel"; } else if (document.onmousewheel !== undefined) { // legacy event type wheelEvent = "mousewheel"; } else { // older Firefox wheelEvent = "DOMMouseScroll"; } registerListener(screenSpaceEventHandler, wheelEvent, element, handleWheel); } function unregisterListeners(screenSpaceEventHandler) { var removalFunctions = screenSpaceEventHandler._removalFunctions; for (var i = 0; i < removalFunctions.length; ++i) { removalFunctions[i](); } } var mouseDownEvent = { position: new Cartesian2(), }; function gotTouchEvent(screenSpaceEventHandler) { screenSpaceEventHandler._lastSeenTouchEvent = getTimestamp$1(); } function canProcessMouseEvent(screenSpaceEventHandler) { return ( getTimestamp$1() - screenSpaceEventHandler._lastSeenTouchEvent > ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds ); } function checkPixelTolerance(startPosition, endPosition, pixelTolerance) { var xDiff = startPosition.x - endPosition.x; var yDiff = startPosition.y - endPosition.y; var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff); return totalPixels < pixelTolerance; } function handleMouseDown(screenSpaceEventHandler, event) { if (!canProcessMouseEvent(screenSpaceEventHandler)) { return; } var button = event.button; screenSpaceEventHandler._buttonDown[button] = true; var screenSpaceEventType; if (button === MouseButton.LEFT) { screenSpaceEventType = ScreenSpaceEventType$1.LEFT_DOWN; } else if (button === MouseButton.MIDDLE) { screenSpaceEventType = ScreenSpaceEventType$1.MIDDLE_DOWN; } else if (button === MouseButton.RIGHT) { screenSpaceEventType = ScreenSpaceEventType$1.RIGHT_DOWN; } else { return; } var position = getPosition$1( screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition ); Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition); Cartesian2.clone(position, screenSpaceEventHandler._primaryPreviousPosition); var modifier = getModifier(event); var action = screenSpaceEventHandler.getInputAction( screenSpaceEventType, modifier ); if (defined(action)) { Cartesian2.clone(position, mouseDownEvent.position); action(mouseDownEvent); event.preventDefault(); } } var mouseUpEvent = { position: new Cartesian2(), }; var mouseClickEvent = { position: new Cartesian2(), }; function cancelMouseEvent( screenSpaceEventHandler, screenSpaceEventType, clickScreenSpaceEventType, event ) { var modifier = getModifier(event); var action = screenSpaceEventHandler.getInputAction( screenSpaceEventType, modifier ); var clickAction = screenSpaceEventHandler.getInputAction( clickScreenSpaceEventType, modifier ); if (defined(action) || defined(clickAction)) { var position = getPosition$1( screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition ); if (defined(action)) { Cartesian2.clone(position, mouseUpEvent.position); action(mouseUpEvent); } if (defined(clickAction)) { var startPosition = screenSpaceEventHandler._primaryStartPosition; if ( checkPixelTolerance( startPosition, position, screenSpaceEventHandler._clickPixelTolerance ) ) { Cartesian2.clone(position, mouseClickEvent.position); clickAction(mouseClickEvent); } } } } function handleMouseUp(screenSpaceEventHandler, event) { if (!canProcessMouseEvent(screenSpaceEventHandler)) { return; } var button = event.button; if ( button !== MouseButton.LEFT && button !== MouseButton.MIDDLE && button !== MouseButton.RIGHT ) { return; } if (screenSpaceEventHandler._buttonDown[MouseButton.LEFT]) { cancelMouseEvent( screenSpaceEventHandler, ScreenSpaceEventType$1.LEFT_UP, ScreenSpaceEventType$1.LEFT_CLICK, event ); screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false; } if (screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE]) { cancelMouseEvent( screenSpaceEventHandler, ScreenSpaceEventType$1.MIDDLE_UP, ScreenSpaceEventType$1.MIDDLE_CLICK, event ); screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] = false; } if (screenSpaceEventHandler._buttonDown[MouseButton.RIGHT]) { cancelMouseEvent( screenSpaceEventHandler, ScreenSpaceEventType$1.RIGHT_UP, ScreenSpaceEventType$1.RIGHT_CLICK, event ); screenSpaceEventHandler._buttonDown[MouseButton.RIGHT] = false; } } var mouseMoveEvent = { startPosition: new Cartesian2(), endPosition: new Cartesian2(), }; function handleMouseMove(screenSpaceEventHandler, event) { if (!canProcessMouseEvent(screenSpaceEventHandler)) { return; } var modifier = getModifier(event); var position = getPosition$1( screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition ); var previousPosition = screenSpaceEventHandler._primaryPreviousPosition; var action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.MOUSE_MOVE, modifier ); if (defined(action)) { Cartesian2.clone(previousPosition, mouseMoveEvent.startPosition); Cartesian2.clone(position, mouseMoveEvent.endPosition); action(mouseMoveEvent); } Cartesian2.clone(position, previousPosition); if ( screenSpaceEventHandler._buttonDown[MouseButton.LEFT] || screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] || screenSpaceEventHandler._buttonDown[MouseButton.RIGHT] ) { event.preventDefault(); } } var mouseDblClickEvent = { position: new Cartesian2(), }; function handleDblClick(screenSpaceEventHandler, event) { var button = event.button; var screenSpaceEventType; if (button === MouseButton.LEFT) { screenSpaceEventType = ScreenSpaceEventType$1.LEFT_DOUBLE_CLICK; } else { return; } var modifier = getModifier(event); var action = screenSpaceEventHandler.getInputAction( screenSpaceEventType, modifier ); if (defined(action)) { getPosition$1(screenSpaceEventHandler, event, mouseDblClickEvent.position); action(mouseDblClickEvent); } } function handleWheel(screenSpaceEventHandler, event) { // currently this event exposes the delta value in terms of // the obsolete mousewheel event type. so, for now, we adapt the other // values to that scheme. var delta; // standard wheel event uses deltaY. sign is opposite wheelDelta. // deltaMode indicates what unit it is in. if (defined(event.deltaY)) { var deltaMode = event.deltaMode; if (deltaMode === event.DOM_DELTA_PIXEL) { delta = -event.deltaY; } else if (deltaMode === event.DOM_DELTA_LINE) { delta = -event.deltaY * 40; } else { // DOM_DELTA_PAGE delta = -event.deltaY * 120; } } else if (event.detail > 0) { // old Firefox versions use event.detail to count the number of clicks. The sign // of the integer is the direction the wheel is scrolled. delta = event.detail * -120; } else { delta = event.wheelDelta; } if (!defined(delta)) { return; } var modifier = getModifier(event); var action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.WHEEL, modifier ); if (defined(action)) { action(delta); event.preventDefault(); } } function handleTouchStart(screenSpaceEventHandler, event) { gotTouchEvent(screenSpaceEventHandler); var changedTouches = event.changedTouches; var i; var length = changedTouches.length; var touch; var identifier; var positions = screenSpaceEventHandler._positions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; positions.set( identifier, getPosition$1(screenSpaceEventHandler, touch, new Cartesian2()) ); } fireTouchEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; previousPositions.set( identifier, Cartesian2.clone(positions.get(identifier)) ); } } function handleTouchEnd(screenSpaceEventHandler, event) { gotTouchEvent(screenSpaceEventHandler); var changedTouches = event.changedTouches; var i; var length = changedTouches.length; var touch; var identifier; var positions = screenSpaceEventHandler._positions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; positions.remove(identifier); } fireTouchEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; previousPositions.remove(identifier); } } var touchStartEvent = { position: new Cartesian2(), }; var touch2StartEvent = { position1: new Cartesian2(), position2: new Cartesian2(), }; var touchEndEvent = { position: new Cartesian2(), }; var touchClickEvent = { position: new Cartesian2(), }; var touchHoldEvent = { position: new Cartesian2(), }; function fireTouchEvents(screenSpaceEventHandler, event) { var modifier = getModifier(event); var positions = screenSpaceEventHandler._positions; var numberOfTouches = positions.length; var action; var clickAction; var pinching = screenSpaceEventHandler._isPinching; if ( numberOfTouches !== 1 && screenSpaceEventHandler._buttonDown[MouseButton.LEFT] ) { // transitioning from single touch, trigger UP and might trigger CLICK screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false; if (defined(screenSpaceEventHandler._touchHoldTimer)) { clearTimeout(screenSpaceEventHandler._touchHoldTimer); screenSpaceEventHandler._touchHoldTimer = undefined; } action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.LEFT_UP, modifier ); if (defined(action)) { Cartesian2.clone( screenSpaceEventHandler._primaryPosition, touchEndEvent.position ); action(touchEndEvent); } if (numberOfTouches === 0 && !screenSpaceEventHandler._isTouchHolding) { // releasing single touch, check for CLICK clickAction = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.LEFT_CLICK, modifier ); if (defined(clickAction)) { var startPosition = screenSpaceEventHandler._primaryStartPosition; var endPosition = screenSpaceEventHandler._previousPositions.values[0]; if ( checkPixelTolerance( startPosition, endPosition, screenSpaceEventHandler._clickPixelTolerance ) ) { Cartesian2.clone( screenSpaceEventHandler._primaryPosition, touchClickEvent.position ); clickAction(touchClickEvent); } } } screenSpaceEventHandler._isTouchHolding = false; // Otherwise don't trigger CLICK, because we are adding more touches. } if (numberOfTouches === 0 && pinching) { // transitioning from pinch, trigger PINCH_END screenSpaceEventHandler._isPinching = false; action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.PINCH_END, modifier ); if (defined(action)) { action(); } } if (numberOfTouches === 1 && !pinching) { // transitioning to single touch, trigger DOWN var position = positions.values[0]; Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition); Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition); Cartesian2.clone( position, screenSpaceEventHandler._primaryPreviousPosition ); screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = true; action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.LEFT_DOWN, modifier ); if (defined(action)) { Cartesian2.clone(position, touchStartEvent.position); action(touchStartEvent); } screenSpaceEventHandler._touchHoldTimer = setTimeout(function () { if (!screenSpaceEventHandler.isDestroyed()) { screenSpaceEventHandler._touchHoldTimer = undefined; screenSpaceEventHandler._isTouchHolding = true; clickAction = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.RIGHT_CLICK, modifier ); if (defined(clickAction)) { var startPosition = screenSpaceEventHandler._primaryStartPosition; var endPosition = screenSpaceEventHandler._previousPositions.values[0]; if ( checkPixelTolerance( startPosition, endPosition, screenSpaceEventHandler._holdPixelTolerance ) ) { Cartesian2.clone( screenSpaceEventHandler._primaryPosition, touchHoldEvent.position ); clickAction(touchHoldEvent); } } } }, ScreenSpaceEventHandler.touchHoldDelayMilliseconds); event.preventDefault(); } if (numberOfTouches === 2 && !pinching) { // transitioning to pinch, trigger PINCH_START screenSpaceEventHandler._isPinching = true; action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.PINCH_START, modifier ); if (defined(action)) { Cartesian2.clone(positions.values[0], touch2StartEvent.position1); Cartesian2.clone(positions.values[1], touch2StartEvent.position2); action(touch2StartEvent); // Touch-enabled devices, in particular iOS can have many default behaviours for // "pinch" events, which can still be executed unless we prevent them here. event.preventDefault(); } } } function handleTouchMove(screenSpaceEventHandler, event) { gotTouchEvent(screenSpaceEventHandler); var changedTouches = event.changedTouches; var i; var length = changedTouches.length; var touch; var identifier; var positions = screenSpaceEventHandler._positions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; var position = positions.get(identifier); if (defined(position)) { getPosition$1(screenSpaceEventHandler, touch, position); } } fireTouchMoveEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; for (i = 0; i < length; ++i) { touch = changedTouches[i]; identifier = touch.identifier; Cartesian2.clone( positions.get(identifier), previousPositions.get(identifier) ); } } var touchMoveEvent = { startPosition: new Cartesian2(), endPosition: new Cartesian2(), }; var touchPinchMovementEvent = { distance: { startPosition: new Cartesian2(), endPosition: new Cartesian2(), }, angleAndHeight: { startPosition: new Cartesian2(), endPosition: new Cartesian2(), }, }; function fireTouchMoveEvents(screenSpaceEventHandler, event) { var modifier = getModifier(event); var positions = screenSpaceEventHandler._positions; var previousPositions = screenSpaceEventHandler._previousPositions; var numberOfTouches = positions.length; var action; if ( numberOfTouches === 1 && screenSpaceEventHandler._buttonDown[MouseButton.LEFT] ) { // moving single touch var position = positions.values[0]; Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition); var previousPosition = screenSpaceEventHandler._primaryPreviousPosition; action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.MOUSE_MOVE, modifier ); if (defined(action)) { Cartesian2.clone(previousPosition, touchMoveEvent.startPosition); Cartesian2.clone(position, touchMoveEvent.endPosition); action(touchMoveEvent); } Cartesian2.clone(position, previousPosition); event.preventDefault(); } else if (numberOfTouches === 2 && screenSpaceEventHandler._isPinching) { // moving pinch action = screenSpaceEventHandler.getInputAction( ScreenSpaceEventType$1.PINCH_MOVE, modifier ); if (defined(action)) { var position1 = positions.values[0]; var position2 = positions.values[1]; var previousPosition1 = previousPositions.values[0]; var previousPosition2 = previousPositions.values[1]; var dX = position2.x - position1.x; var dY = position2.y - position1.y; var dist = Math.sqrt(dX * dX + dY * dY) * 0.25; var prevDX = previousPosition2.x - previousPosition1.x; var prevDY = previousPosition2.y - previousPosition1.y; var prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY) * 0.25; var cY = (position2.y + position1.y) * 0.125; var prevCY = (previousPosition2.y + previousPosition1.y) * 0.125; var angle = Math.atan2(dY, dX); var prevAngle = Math.atan2(prevDY, prevDX); Cartesian2.fromElements( 0.0, prevDist, touchPinchMovementEvent.distance.startPosition ); Cartesian2.fromElements( 0.0, dist, touchPinchMovementEvent.distance.endPosition ); Cartesian2.fromElements( prevAngle, prevCY, touchPinchMovementEvent.angleAndHeight.startPosition ); Cartesian2.fromElements( angle, cY, touchPinchMovementEvent.angleAndHeight.endPosition ); action(touchPinchMovementEvent); } } } function handlePointerDown(screenSpaceEventHandler, event) { event.target.setPointerCapture(event.pointerId); if (event.pointerType === "touch") { var positions = screenSpaceEventHandler._positions; var identifier = event.pointerId; positions.set( identifier, getPosition$1(screenSpaceEventHandler, event, new Cartesian2()) ); fireTouchEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; previousPositions.set( identifier, Cartesian2.clone(positions.get(identifier)) ); } else { handleMouseDown(screenSpaceEventHandler, event); } } function handlePointerUp(screenSpaceEventHandler, event) { if (event.pointerType === "touch") { var positions = screenSpaceEventHandler._positions; var identifier = event.pointerId; positions.remove(identifier); fireTouchEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; previousPositions.remove(identifier); } else { handleMouseUp(screenSpaceEventHandler, event); } } function handlePointerMove(screenSpaceEventHandler, event) { if (event.pointerType === "touch") { var positions = screenSpaceEventHandler._positions; var identifier = event.pointerId; var position = positions.get(identifier); if (!defined(position)) { return; } getPosition$1(screenSpaceEventHandler, event, position); fireTouchMoveEvents(screenSpaceEventHandler, event); var previousPositions = screenSpaceEventHandler._previousPositions; Cartesian2.clone( positions.get(identifier), previousPositions.get(identifier) ); } else { handleMouseMove(screenSpaceEventHandler, event); } } /** * Handles user input events. Custom functions can be added to be executed on * when the user enters input. * * @alias ScreenSpaceEventHandler * * @param {HTMLCanvasElement} [element=document] The element to add events to. * * @constructor */ function ScreenSpaceEventHandler(element) { this._inputEvents = {}; this._buttonDown = { LEFT: false, MIDDLE: false, RIGHT: false, }; this._isPinching = false; this._isTouchHolding = false; this._lastSeenTouchEvent = -ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds; this._primaryStartPosition = new Cartesian2(); this._primaryPosition = new Cartesian2(); this._primaryPreviousPosition = new Cartesian2(); this._positions = new AssociativeArray(); this._previousPositions = new AssociativeArray(); this._removalFunctions = []; this._touchHoldTimer = undefined; // TODO: Revisit when doing mobile development. May need to be configurable // or determined based on the platform? this._clickPixelTolerance = 5; this._holdPixelTolerance = 25; this._element = defaultValue(element, document); registerListeners(this); } /** * Set a function to be executed on an input event. * * @param {Function} action Function to be executed when the input event occurs. * @param {Number} type The ScreenSpaceEventType of input event. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a type * event occurs. * * @see ScreenSpaceEventHandler#getInputAction * @see ScreenSpaceEventHandler#removeInputAction */ ScreenSpaceEventHandler.prototype.setInputAction = function ( action, type, modifier ) { //>>includeStart('debug', pragmas.debug); if (!defined(action)) { throw new DeveloperError("action is required."); } if (!defined(type)) { throw new DeveloperError("type is required."); } //>>includeEnd('debug'); var key = getInputEventKey(type, modifier); this._inputEvents[key] = action; }; /** * Returns the function to be executed on an input event. * * @param {Number} type The ScreenSpaceEventType of input event. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a type * event occurs. * * @returns {Function} The function to be executed on an input event. * * @see ScreenSpaceEventHandler#setInputAction * @see ScreenSpaceEventHandler#removeInputAction */ ScreenSpaceEventHandler.prototype.getInputAction = function (type, modifier) { //>>includeStart('debug', pragmas.debug); if (!defined(type)) { throw new DeveloperError("type is required."); } //>>includeEnd('debug'); var key = getInputEventKey(type, modifier); return this._inputEvents[key]; }; /** * Removes the function to be executed on an input event. * * @param {Number} type The ScreenSpaceEventType of input event. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a type * event occurs. * * @see ScreenSpaceEventHandler#getInputAction * @see ScreenSpaceEventHandler#setInputAction */ ScreenSpaceEventHandler.prototype.removeInputAction = function ( type, modifier ) { //>>includeStart('debug', pragmas.debug); if (!defined(type)) { throw new DeveloperError("type is required."); } //>>includeEnd('debug'); var key = getInputEventKey(type, modifier); delete this._inputEvents[key]; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see ScreenSpaceEventHandler#destroy */ ScreenSpaceEventHandler.prototype.isDestroyed = function () { return false; }; /** * Removes listeners held by this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * handler = handler && handler.destroy(); * * @see ScreenSpaceEventHandler#isDestroyed */ ScreenSpaceEventHandler.prototype.destroy = function () { unregisterListeners(this); return destroyObject(this); }; /** * The amount of time, in milliseconds, that mouse events will be disabled after * receiving any touch events, such that any emulated mouse events will be ignored. * @type {Number} * @default 800 */ ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds = 800; /** * The amount of time, in milliseconds, before a touch on the screen becomes a * touch and hold. * @type {Number} * @default 1500 */ ScreenSpaceEventHandler.touchHoldDelayMilliseconds = 1500; /** * Value and type information for per-instance geometry attribute that determines if the geometry instance will be shown. * * @alias ShowGeometryInstanceAttribute * @constructor * * @param {Boolean} [show=true] Determines if the geometry instance will be shown. * * * @example * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.BoxGeometry({ * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL, * minimum : new Cesium.Cartesian3(-250000.0, -250000.0, -250000.0), * maximum : new Cesium.Cartesian3(250000.0, 250000.0, 250000.0) * }), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 1000000.0), new Cesium.Matrix4()), * id : 'box', * attributes : { * show : new Cesium.ShowGeometryInstanceAttribute(false) * } * }); * * @see GeometryInstance * @see GeometryInstanceAttribute */ function ShowGeometryInstanceAttribute(show) { show = defaultValue(show, true); /** * The values for the attributes stored in a typed array. * * @type Uint8Array * * @default [1.0] */ this.value = ShowGeometryInstanceAttribute.toValue(show); } Object.defineProperties(ShowGeometryInstanceAttribute.prototype, { /** * The datatype of each component in the attribute, e.g., individual elements in * {@link ColorGeometryInstanceAttribute#value}. * * @memberof ShowGeometryInstanceAttribute.prototype * * @type {ComponentDatatype} * @readonly * * @default {@link ComponentDatatype.UNSIGNED_BYTE} */ componentDatatype: { get: function () { return ComponentDatatype$1.UNSIGNED_BYTE; }, }, /** * The number of components in the attributes, i.e., {@link ColorGeometryInstanceAttribute#value}. * * @memberof ShowGeometryInstanceAttribute.prototype * * @type {Number} * @readonly * * @default 1 */ componentsPerAttribute: { get: function () { return 1; }, }, /** * When true and componentDatatype 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. * * @memberof ShowGeometryInstanceAttribute.prototype * * @type {Boolean} * @readonly * * @default true */ normalize: { get: function () { return false; }, }, }); /** * Converts a boolean show to a typed array that can be used to assign a show attribute. * * @param {Boolean} show The show value. * @param {Uint8Array} [result] The array to store the result in, if undefined a new instance will be created. * @returns {Uint8Array} The modified result parameter or a new instance if result was undefined. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true, attributes.show); */ ShowGeometryInstanceAttribute.toValue = function (show, result) { //>>includeStart('debug', pragmas.debug); if (!defined(show)) { throw new DeveloperError("show is required."); } //>>includeEnd('debug'); if (!defined(result)) { return new Uint8Array([show]); } result[0] = show; return result; }; /** * Contains functions for finding the Cartesian coordinates of the sun and the moon in the * Earth-centered inertial frame. * * @namespace Simon1994PlanetaryPositions */ var Simon1994PlanetaryPositions = {}; function computeTdbMinusTtSpice(daysSinceJ2000InTerrestrialTime) { /* STK Comments ------------------------------------------------------ * This function uses constants designed to be consistent with * the SPICE Toolkit from JPL version N0051 (unitim.c) * M0 = 6.239996 * M0Dot = 1.99096871e-7 rad/s = 0.01720197 rad/d * EARTH_ECC = 1.671e-2 * TDB_AMPL = 1.657e-3 secs *--------------------------------------------------------------------*/ //* Values taken as specified in STK Comments except: 0.01720197 rad/day = 1.99096871e-7 rad/sec //* Here we use the more precise value taken from the SPICE value 1.99096871e-7 rad/sec converted to rad/day //* All other constants are consistent with the SPICE implementation of the TDB conversion //* except where we treat the independent time parameter to be in TT instead of TDB. //* This is an approximation made to facilitate performance due to the higher prevalance of //* the TT2TDB conversion over TDB2TT in order to avoid having to iterate when converting to TDB for the JPL ephemeris. //* Days are used instead of seconds to provide a slight improvement in numerical precision. //* For more information see: //* http://www.cv.nrao.edu/~rfisher/Ephemerides/times.html#TDB //* ftp://ssd.jpl.nasa.gov/pub/eph/planets/ioms/ExplSupplChap8.pdf var g = 6.239996 + 0.0172019696544 * daysSinceJ2000InTerrestrialTime; return 1.657e-3 * Math.sin(g + 1.671e-2 * Math.sin(g)); } var TdtMinusTai = 32.184; var J2000d = 2451545; function taiToTdb(date, result) { //Converts TAI to TT result = JulianDate.addSeconds(date, TdtMinusTai, result); //Converts TT to TDB var days = JulianDate.totalDays(result) - J2000d; result = JulianDate.addSeconds(result, computeTdbMinusTtSpice(days), result); return result; } var epoch = new JulianDate(2451545, 0, TimeStandard$1.TAI); //Actually TDB (not TAI) var MetersPerKilometer = 1000.0; var RadiansPerDegree = CesiumMath.RADIANS_PER_DEGREE; var RadiansPerArcSecond = CesiumMath.RADIANS_PER_ARCSECOND; var MetersPerAstronomicalUnit = 1.4959787e11; // IAU 1976 value var perifocalToEquatorial = new Matrix3(); function elementsToCartesian( semimajorAxis, eccentricity, inclination, longitudeOfPerigee, longitudeOfNode, meanLongitude, result ) { if (inclination < 0.0) { inclination = -inclination; longitudeOfNode += CesiumMath.PI; } //>>includeStart('debug', pragmas.debug); if (inclination < 0 || inclination > CesiumMath.PI) { throw new DeveloperError( "The inclination is out of range. Inclination must be greater than or equal to zero and less than or equal to Pi radians." ); } //>>includeEnd('debug') var radiusOfPeriapsis = semimajorAxis * (1.0 - eccentricity); var argumentOfPeriapsis = longitudeOfPerigee - longitudeOfNode; var rightAscensionOfAscendingNode = longitudeOfNode; var trueAnomaly = meanAnomalyToTrueAnomaly( meanLongitude - longitudeOfPerigee, eccentricity ); var type = chooseOrbit(eccentricity, 0.0); //>>includeStart('debug', pragmas.debug); if ( type === "Hyperbolic" && Math.abs(CesiumMath.negativePiToPi(trueAnomaly)) >= Math.acos(-1.0 / eccentricity) ) { throw new DeveloperError( "The true anomaly of the hyperbolic orbit lies outside of the bounds of the hyperbola." ); } //>>includeEnd('debug') perifocalToCartesianMatrix( argumentOfPeriapsis, inclination, rightAscensionOfAscendingNode, perifocalToEquatorial ); var semilatus = radiusOfPeriapsis * (1.0 + eccentricity); var costheta = Math.cos(trueAnomaly); var sintheta = Math.sin(trueAnomaly); var denom = 1.0 + eccentricity * costheta; //>>includeStart('debug', pragmas.debug); if (denom <= CesiumMath.Epsilon10) { throw new DeveloperError("elements cannot be converted to cartesian"); } //>>includeEnd('debug') var radius = semilatus / denom; if (!defined(result)) { result = new Cartesian3(radius * costheta, radius * sintheta, 0.0); } else { result.x = radius * costheta; result.y = radius * sintheta; result.z = 0.0; } return Matrix3.multiplyByVector(perifocalToEquatorial, result, result); } function chooseOrbit(eccentricity, tolerance) { //>>includeStart('debug', pragmas.debug); if (eccentricity < 0) { throw new DeveloperError("eccentricity cannot be negative."); } //>>includeEnd('debug') if (eccentricity <= tolerance) { return "Circular"; } else if (eccentricity < 1.0 - tolerance) { return "Elliptical"; } else if (eccentricity <= 1.0 + tolerance) { return "Parabolic"; } return "Hyperbolic"; } // Calculates the true anomaly given the mean anomaly and the eccentricity. function meanAnomalyToTrueAnomaly(meanAnomaly, eccentricity) { //>>includeStart('debug', pragmas.debug); if (eccentricity < 0.0 || eccentricity >= 1.0) { throw new DeveloperError("eccentricity out of range."); } //>>includeEnd('debug') var eccentricAnomaly = meanAnomalyToEccentricAnomaly( meanAnomaly, eccentricity ); return eccentricAnomalyToTrueAnomaly(eccentricAnomaly, eccentricity); } var maxIterationCount = 50; var keplerEqConvergence = CesiumMath.EPSILON8; // Calculates the eccentric anomaly given the mean anomaly and the eccentricity. function meanAnomalyToEccentricAnomaly(meanAnomaly, eccentricity) { //>>includeStart('debug', pragmas.debug); if (eccentricity < 0.0 || eccentricity >= 1.0) { throw new DeveloperError("eccentricity out of range."); } //>>includeEnd('debug') var revs = Math.floor(meanAnomaly / CesiumMath.TWO_PI); // Find angle in current revolution meanAnomaly -= revs * CesiumMath.TWO_PI; // calculate starting value for iteration sequence var iterationValue = meanAnomaly + (eccentricity * Math.sin(meanAnomaly)) / (1.0 - Math.sin(meanAnomaly + eccentricity) + Math.sin(meanAnomaly)); // Perform Newton-Raphson iteration on Kepler's equation var eccentricAnomaly = Number.MAX_VALUE; var count; for ( count = 0; count < maxIterationCount && Math.abs(eccentricAnomaly - iterationValue) > keplerEqConvergence; ++count ) { eccentricAnomaly = iterationValue; var NRfunction = eccentricAnomaly - eccentricity * Math.sin(eccentricAnomaly) - meanAnomaly; var dNRfunction = 1 - eccentricity * Math.cos(eccentricAnomaly); iterationValue = eccentricAnomaly - NRfunction / dNRfunction; } //>>includeStart('debug', pragmas.debug); if (count >= maxIterationCount) { throw new DeveloperError("Kepler equation did not converge"); // STK Components uses a numerical method to find the eccentric anomaly in the case that Kepler's // equation does not converge. We don't expect that to ever be necessary for the reasonable orbits used here. } //>>includeEnd('debug') eccentricAnomaly = iterationValue + revs * CesiumMath.TWO_PI; return eccentricAnomaly; } // Calculates the true anomaly given the eccentric anomaly and the eccentricity. function eccentricAnomalyToTrueAnomaly(eccentricAnomaly, eccentricity) { //>>includeStart('debug', pragmas.debug); if (eccentricity < 0.0 || eccentricity >= 1.0) { throw new DeveloperError("eccentricity out of range."); } //>>includeEnd('debug') // Calculate the number of previous revolutions var revs = Math.floor(eccentricAnomaly / CesiumMath.TWO_PI); // Find angle in current revolution eccentricAnomaly -= revs * CesiumMath.TWO_PI; // Calculate true anomaly from eccentric anomaly var trueAnomalyX = Math.cos(eccentricAnomaly) - eccentricity; var trueAnomalyY = Math.sin(eccentricAnomaly) * Math.sqrt(1 - eccentricity * eccentricity); var trueAnomaly = Math.atan2(trueAnomalyY, trueAnomalyX); // Ensure the correct quadrant trueAnomaly = CesiumMath.zeroToTwoPi(trueAnomaly); if (eccentricAnomaly < 0) { trueAnomaly -= CesiumMath.TWO_PI; } // Add on previous revolutions trueAnomaly += revs * CesiumMath.TWO_PI; return trueAnomaly; } // Calculates the transformation matrix to convert from the perifocal (PQW) coordinate // system to inertial cartesian coordinates. function perifocalToCartesianMatrix( argumentOfPeriapsis, inclination, rightAscension, result ) { //>>includeStart('debug', pragmas.debug); if (inclination < 0 || inclination > CesiumMath.PI) { throw new DeveloperError("inclination out of range"); } //>>includeEnd('debug') var cosap = Math.cos(argumentOfPeriapsis); var sinap = Math.sin(argumentOfPeriapsis); var cosi = Math.cos(inclination); var sini = Math.sin(inclination); var cosraan = Math.cos(rightAscension); var sinraan = Math.sin(rightAscension); if (!defined(result)) { result = new Matrix3( cosraan * cosap - sinraan * sinap * cosi, -cosraan * sinap - sinraan * cosap * cosi, sinraan * sini, sinraan * cosap + cosraan * sinap * cosi, -sinraan * sinap + cosraan * cosap * cosi, -cosraan * sini, sinap * sini, cosap * sini, cosi ); } else { result[0] = cosraan * cosap - sinraan * sinap * cosi; result[1] = sinraan * cosap + cosraan * sinap * cosi; result[2] = sinap * sini; result[3] = -cosraan * sinap - sinraan * cosap * cosi; result[4] = -sinraan * sinap + cosraan * cosap * cosi; result[5] = cosap * sini; result[6] = sinraan * sini; result[7] = -cosraan * sini; result[8] = cosi; } return result; } // From section 5.8 var semiMajorAxis0 = 1.0000010178 * MetersPerAstronomicalUnit; var meanLongitude0 = 100.46645683 * RadiansPerDegree; var meanLongitude1 = 1295977422.83429 * RadiansPerArcSecond; // From table 6 var p1u = 16002; var p2u = 21863; var p3u = 32004; var p4u = 10931; var p5u = 14529; var p6u = 16368; var p7u = 15318; var p8u = 32794; var Ca1 = 64 * 1e-7 * MetersPerAstronomicalUnit; var Ca2 = -152 * 1e-7 * MetersPerAstronomicalUnit; var Ca3 = 62 * 1e-7 * MetersPerAstronomicalUnit; var Ca4 = -8 * 1e-7 * MetersPerAstronomicalUnit; var Ca5 = 32 * 1e-7 * MetersPerAstronomicalUnit; var Ca6 = -41 * 1e-7 * MetersPerAstronomicalUnit; var Ca7 = 19 * 1e-7 * MetersPerAstronomicalUnit; var Ca8 = -11 * 1e-7 * MetersPerAstronomicalUnit; var Sa1 = -150 * 1e-7 * MetersPerAstronomicalUnit; var Sa2 = -46 * 1e-7 * MetersPerAstronomicalUnit; var Sa3 = 68 * 1e-7 * MetersPerAstronomicalUnit; var Sa4 = 54 * 1e-7 * MetersPerAstronomicalUnit; var Sa5 = 14 * 1e-7 * MetersPerAstronomicalUnit; var Sa6 = 24 * 1e-7 * MetersPerAstronomicalUnit; var Sa7 = -28 * 1e-7 * MetersPerAstronomicalUnit; var Sa8 = 22 * 1e-7 * MetersPerAstronomicalUnit; var q1u = 10; var q2u = 16002; var q3u = 21863; var q4u = 10931; var q5u = 1473; var q6u = 32004; var q7u = 4387; var q8u = 73; var Cl1 = -325 * 1e-7; var Cl2 = -322 * 1e-7; var Cl3 = -79 * 1e-7; var Cl4 = 232 * 1e-7; var Cl5 = -52 * 1e-7; var Cl6 = 97 * 1e-7; var Cl7 = 55 * 1e-7; var Cl8 = -41 * 1e-7; var Sl1 = -105 * 1e-7; var Sl2 = -137 * 1e-7; var Sl3 = 258 * 1e-7; var Sl4 = 35 * 1e-7; var Sl5 = -116 * 1e-7; var Sl6 = -88 * 1e-7; var Sl7 = -112 * 1e-7; var Sl8 = -80 * 1e-7; var scratchDate$1 = new JulianDate(0, 0.0, TimeStandard$1.TAI); // Gets a point describing the motion of the Earth-Moon barycenter according to the equations described in section 6. function computeSimonEarthMoonBarycenter(date, result) { // t is thousands of years from J2000 TDB taiToTdb(date, scratchDate$1); var x = scratchDate$1.dayNumber - epoch.dayNumber + (scratchDate$1.secondsOfDay - epoch.secondsOfDay) / TimeConstants$1.SECONDS_PER_DAY; var t = x / (TimeConstants$1.DAYS_PER_JULIAN_CENTURY * 10.0); var u = 0.3595362 * t; var semimajorAxis = semiMajorAxis0 + Ca1 * Math.cos(p1u * u) + Sa1 * Math.sin(p1u * u) + Ca2 * Math.cos(p2u * u) + Sa2 * Math.sin(p2u * u) + Ca3 * Math.cos(p3u * u) + Sa3 * Math.sin(p3u * u) + Ca4 * Math.cos(p4u * u) + Sa4 * Math.sin(p4u * u) + Ca5 * Math.cos(p5u * u) + Sa5 * Math.sin(p5u * u) + Ca6 * Math.cos(p6u * u) + Sa6 * Math.sin(p6u * u) + Ca7 * Math.cos(p7u * u) + Sa7 * Math.sin(p7u * u) + Ca8 * Math.cos(p8u * u) + Sa8 * Math.sin(p8u * u); var meanLongitude = meanLongitude0 + meanLongitude1 * t + Cl1 * Math.cos(q1u * u) + Sl1 * Math.sin(q1u * u) + Cl2 * Math.cos(q2u * u) + Sl2 * Math.sin(q2u * u) + Cl3 * Math.cos(q3u * u) + Sl3 * Math.sin(q3u * u) + Cl4 * Math.cos(q4u * u) + Sl4 * Math.sin(q4u * u) + Cl5 * Math.cos(q5u * u) + Sl5 * Math.sin(q5u * u) + Cl6 * Math.cos(q6u * u) + Sl6 * Math.sin(q6u * u) + Cl7 * Math.cos(q7u * u) + Sl7 * Math.sin(q7u * u) + Cl8 * Math.cos(q8u * u) + Sl8 * Math.sin(q8u * u); // All constants in this part are from section 5.8 var eccentricity = 0.0167086342 - 0.0004203654 * t; var longitudeOfPerigee = 102.93734808 * RadiansPerDegree + 11612.3529 * RadiansPerArcSecond * t; var inclination = 469.97289 * RadiansPerArcSecond * t; var longitudeOfNode = 174.87317577 * RadiansPerDegree - 8679.27034 * RadiansPerArcSecond * t; return elementsToCartesian( semimajorAxis, eccentricity, inclination, longitudeOfPerigee, longitudeOfNode, meanLongitude, result ); } // Gets a point describing the position of the moon according to the equations described in section 4. function computeSimonMoon(date, result) { taiToTdb(date, scratchDate$1); var x = scratchDate$1.dayNumber - epoch.dayNumber + (scratchDate$1.secondsOfDay - epoch.secondsOfDay) / TimeConstants$1.SECONDS_PER_DAY; var t = x / TimeConstants$1.DAYS_PER_JULIAN_CENTURY; var t2 = t * t; var t3 = t2 * t; var t4 = t3 * t; // Terms from section 3.4 (b.1) var semimajorAxis = 383397.7725 + 0.004 * t; var eccentricity = 0.055545526 - 0.000000016 * t; var inclinationConstant = 5.15668983 * RadiansPerDegree; var inclinationSecPart = -0.00008 * t + 0.02966 * t2 - 0.000042 * t3 - 0.00000013 * t4; var longitudeOfPerigeeConstant = 83.35324312 * RadiansPerDegree; var longitudeOfPerigeeSecPart = 14643420.2669 * t - 38.2702 * t2 - 0.045047 * t3 + 0.00021301 * t4; var longitudeOfNodeConstant = 125.04455501 * RadiansPerDegree; var longitudeOfNodeSecPart = -6967919.3631 * t + 6.3602 * t2 + 0.007625 * t3 - 0.00003586 * t4; var meanLongitudeConstant = 218.31664563 * RadiansPerDegree; var meanLongitudeSecPart = 1732559343.4847 * t - 6.391 * t2 + 0.006588 * t3 - 0.00003169 * t4; // Delaunay arguments from section 3.5 b var D = 297.85019547 * RadiansPerDegree + RadiansPerArcSecond * (1602961601.209 * t - 6.3706 * t2 + 0.006593 * t3 - 0.00003169 * t4); var F = 93.27209062 * RadiansPerDegree + RadiansPerArcSecond * (1739527262.8478 * t - 12.7512 * t2 - 0.001037 * t3 + 0.00000417 * t4); var l = 134.96340251 * RadiansPerDegree + RadiansPerArcSecond * (1717915923.2178 * t + 31.8792 * t2 + 0.051635 * t3 - 0.0002447 * t4); var lprime = 357.52910918 * RadiansPerDegree + RadiansPerArcSecond * (129596581.0481 * t - 0.5532 * t2 + 0.000136 * t3 - 0.00001149 * t4); var psi = 310.17137918 * RadiansPerDegree - RadiansPerArcSecond * (6967051.436 * t + 6.2068 * t2 + 0.007618 * t3 - 0.00003219 * t4); // Add terms from Table 4 var twoD = 2.0 * D; var fourD = 4.0 * D; var sixD = 6.0 * D; var twol = 2.0 * l; var threel = 3.0 * l; var fourl = 4.0 * l; var twoF = 2.0 * F; semimajorAxis += 3400.4 * Math.cos(twoD) - 635.6 * Math.cos(twoD - l) - 235.6 * Math.cos(l) + 218.1 * Math.cos(twoD - lprime) + 181.0 * Math.cos(twoD + l); eccentricity += 0.014216 * Math.cos(twoD - l) + 0.008551 * Math.cos(twoD - twol) - 0.001383 * Math.cos(l) + 0.001356 * Math.cos(twoD + l) - 0.001147 * Math.cos(fourD - threel) - 0.000914 * Math.cos(fourD - twol) + 0.000869 * Math.cos(twoD - lprime - l) - 0.000627 * Math.cos(twoD) - 0.000394 * Math.cos(fourD - fourl) + 0.000282 * Math.cos(twoD - lprime - twol) - 0.000279 * Math.cos(D - l) - 0.000236 * Math.cos(twol) + 0.000231 * Math.cos(fourD) + 0.000229 * Math.cos(sixD - fourl) - 0.000201 * Math.cos(twol - twoF); inclinationSecPart += 486.26 * Math.cos(twoD - twoF) - 40.13 * Math.cos(twoD) + 37.51 * Math.cos(twoF) + 25.73 * Math.cos(twol - twoF) + 19.97 * Math.cos(twoD - lprime - twoF); longitudeOfPerigeeSecPart += -55609 * Math.sin(twoD - l) - 34711 * Math.sin(twoD - twol) - 9792 * Math.sin(l) + 9385 * Math.sin(fourD - threel) + 7505 * Math.sin(fourD - twol) + 5318 * Math.sin(twoD + l) + 3484 * Math.sin(fourD - fourl) - 3417 * Math.sin(twoD - lprime - l) - 2530 * Math.sin(sixD - fourl) - 2376 * Math.sin(twoD) - 2075 * Math.sin(twoD - threel) - 1883 * Math.sin(twol) - 1736 * Math.sin(sixD - 5.0 * l) + 1626 * Math.sin(lprime) - 1370 * Math.sin(sixD - threel); longitudeOfNodeSecPart += -5392 * Math.sin(twoD - twoF) - 540 * Math.sin(lprime) - 441 * Math.sin(twoD) + 423 * Math.sin(twoF) - 288 * Math.sin(twol - twoF); meanLongitudeSecPart += -3332.9 * Math.sin(twoD) + 1197.4 * Math.sin(twoD - l) - 662.5 * Math.sin(lprime) + 396.3 * Math.sin(l) - 218.0 * Math.sin(twoD - lprime); // Add terms from Table 5 var twoPsi = 2.0 * psi; var threePsi = 3.0 * psi; inclinationSecPart += 46.997 * Math.cos(psi) * t - 0.614 * Math.cos(twoD - twoF + psi) * t + 0.614 * Math.cos(twoD - twoF - psi) * t - 0.0297 * Math.cos(twoPsi) * t2 - 0.0335 * Math.cos(psi) * t2 + 0.0012 * Math.cos(twoD - twoF + twoPsi) * t2 - 0.00016 * Math.cos(psi) * t3 + 0.00004 * Math.cos(threePsi) * t3 + 0.00004 * Math.cos(twoPsi) * t3; var perigeeAndMean = 2.116 * Math.sin(psi) * t - 0.111 * Math.sin(twoD - twoF - psi) * t - 0.0015 * Math.sin(psi) * t2; longitudeOfPerigeeSecPart += perigeeAndMean; meanLongitudeSecPart += perigeeAndMean; longitudeOfNodeSecPart += -520.77 * Math.sin(psi) * t + 13.66 * Math.sin(twoD - twoF + psi) * t + 1.12 * Math.sin(twoD - psi) * t - 1.06 * Math.sin(twoF - psi) * t + 0.66 * Math.sin(twoPsi) * t2 + 0.371 * Math.sin(psi) * t2 - 0.035 * Math.sin(twoD - twoF + twoPsi) * t2 - 0.015 * Math.sin(twoD - twoF + psi) * t2 + 0.0014 * Math.sin(psi) * t3 - 0.0011 * Math.sin(threePsi) * t3 - 0.0009 * Math.sin(twoPsi) * t3; // Add constants and convert units semimajorAxis *= MetersPerKilometer; var inclination = inclinationConstant + inclinationSecPart * RadiansPerArcSecond; var longitudeOfPerigee = longitudeOfPerigeeConstant + longitudeOfPerigeeSecPart * RadiansPerArcSecond; var meanLongitude = meanLongitudeConstant + meanLongitudeSecPart * RadiansPerArcSecond; var longitudeOfNode = longitudeOfNodeConstant + longitudeOfNodeSecPart * RadiansPerArcSecond; return elementsToCartesian( semimajorAxis, eccentricity, inclination, longitudeOfPerigee, longitudeOfNode, meanLongitude, result ); } // Gets a point describing the motion of the Earth. This point uses the Moon point and // the 1992 mu value (ratio between Moon and Earth masses) in Table 2 of the paper in order // to determine the position of the Earth relative to the Earth-Moon barycenter. var moonEarthMassRatio = 0.012300034; // From 1992 mu value in Table 2 var factor = (moonEarthMassRatio / (moonEarthMassRatio + 1.0)) * -1; function computeSimonEarth(date, result) { result = computeSimonMoon(date, result); return Cartesian3.multiplyByScalar(result, factor, result); } // Values for the axesTransformation needed for the rotation were found using the STK Components // GreographicTransformer on the position of the sun center of mass point and the earth J2000 frame. var axesTransformation = new Matrix3( 1.0000000000000002, 5.619723173785822e-16, 4.690511510146299e-19, -5.154129427414611e-16, 0.9174820620691819, -0.39777715593191376, -2.23970096136568e-16, 0.39777715593191376, 0.9174820620691819 ); var translation = new Cartesian3(); /** * Computes the position of the Sun in the Earth-centered inertial frame * * @param {JulianDate} [julianDate] The time at which to compute the Sun's position, if not provided the current system time is used. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} Calculated sun position */ Simon1994PlanetaryPositions.computeSunPositionInEarthInertialFrame = function ( julianDate, result ) { if (!defined(julianDate)) { julianDate = JulianDate.now(); } if (!defined(result)) { result = new Cartesian3(); } //first forward transformation translation = computeSimonEarthMoonBarycenter(julianDate, translation); result = Cartesian3.negate(translation, result); //second forward transformation computeSimonEarth(julianDate, translation); Cartesian3.subtract(result, translation, result); Matrix3.multiplyByVector(axesTransformation, result, result); return result; }; /** * Computes the position of the Moon in the Earth-centered inertial frame * * @param {JulianDate} [julianDate] The time at which to compute the Sun's position, if not provided the current system time is used. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} Calculated moon position */ Simon1994PlanetaryPositions.computeMoonPositionInEarthInertialFrame = function ( julianDate, result ) { if (!defined(julianDate)) { julianDate = JulianDate.now(); } result = computeSimonMoon(julianDate, result); Matrix3.multiplyByVector(axesTransformation, result, result); return result; }; function interpolateColors(p0, p1, color0, color1, minDistance, array, offset) { var numPoints = PolylinePipeline.numberOfPoints(p0, p1, minDistance); var i; var r0 = color0.red; var g0 = color0.green; var b0 = color0.blue; var a0 = color0.alpha; var r1 = color1.red; var g1 = color1.green; var b1 = color1.blue; var a1 = color1.alpha; if (Color.equals(color0, color1)) { for (i = 0; i < numPoints; i++) { array[offset++] = Color.floatToByte(r0); array[offset++] = Color.floatToByte(g0); array[offset++] = Color.floatToByte(b0); array[offset++] = Color.floatToByte(a0); } return offset; } var redPerVertex = (r1 - r0) / numPoints; var greenPerVertex = (g1 - g0) / numPoints; var bluePerVertex = (b1 - b0) / numPoints; var alphaPerVertex = (a1 - a0) / numPoints; var index = offset; for (i = 0; i < numPoints; i++) { array[index++] = Color.floatToByte(r0 + i * redPerVertex); array[index++] = Color.floatToByte(g0 + i * greenPerVertex); array[index++] = Color.floatToByte(b0 + i * bluePerVertex); array[index++] = Color.floatToByte(a0 + i * alphaPerVertex); } return index; } /** * A description of a polyline modeled as a line strip; the first two positions define a line segment, * and each additional position defines a line segment from the previous position. * * @alias SimplePolylineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of {@link Cartesian3} defining the positions in the polyline as a line strip. * @param {Color[]} [options.colors] An Array of {@link Color} defining the per vertex or per segment colors. * @param {Boolean} [options.colorsPerVertex=false] A boolean that determines whether the colors will be flat across each segment of the line or interpolated across the vertices. * @param {ArcType} [options.arcType=ArcType.GEODESIC] The type of line the polyline segments must follow. * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude if options.arcType is not ArcType.NONE. Determines the number of positions in the buffer. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid to be used as a reference. * * @exception {DeveloperError} At least two positions are required. * @exception {DeveloperError} colors has an invalid length. * * @see SimplePolylineGeometry#createGeometry * * @example * // A polyline with two connected line segments * var polyline = new Cesium.SimplePolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 5.0, 0.0, * 5.0, 5.0 * ]) * }); * var geometry = Cesium.SimplePolylineGeometry.createGeometry(polyline); */ function SimplePolylineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; var colors = options.colors; var colorsPerVertex = defaultValue(options.colorsPerVertex, false); //>>includeStart('debug', pragmas.debug); if (!defined(positions) || positions.length < 2) { throw new DeveloperError("At least two positions are required."); } if ( defined(colors) && ((colorsPerVertex && colors.length < positions.length) || (!colorsPerVertex && colors.length < positions.length - 1)) ) { throw new DeveloperError("colors has an invalid length."); } //>>includeEnd('debug'); this._positions = positions; this._colors = colors; this._colorsPerVertex = colorsPerVertex; this._arcType = defaultValue(options.arcType, ArcType$1.GEODESIC); this._granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._workerName = "createSimplePolylineGeometry"; var numComponents = 1 + positions.length * Cartesian3.packedLength; numComponents += defined(colors) ? 1 + colors.length * Color.packedLength : 1; /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + 3; } /** * Stores the provided instance into the provided array. * * @param {SimplePolylineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ SimplePolylineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var colors = value._colors; length = defined(colors) ? colors.length : 0.0; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Color.packedLength) { Color.pack(colors[i], array, startingIndex); } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex++] = value._colorsPerVertex ? 1.0 : 0.0; array[startingIndex++] = value._arcType; array[startingIndex] = value._granularity; return array; }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {SimplePolylineGeometry} [result] The object into which to store the result. * @returns {SimplePolylineGeometry} The modified result parameter or a new SimplePolylineGeometry instance if one was not provided. */ SimplePolylineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var colors = length > 0 ? new Array(length) : undefined; for (i = 0; i < length; ++i, startingIndex += Color.packedLength) { colors[i] = Color.unpack(array, startingIndex); } var ellipsoid = Ellipsoid.unpack(array, startingIndex); startingIndex += Ellipsoid.packedLength; var colorsPerVertex = array[startingIndex++] === 1.0; var arcType = array[startingIndex++]; var granularity = array[startingIndex]; if (!defined(result)) { return new SimplePolylineGeometry({ positions: positions, colors: colors, ellipsoid: ellipsoid, colorsPerVertex: colorsPerVertex, arcType: arcType, granularity: granularity, }); } result._positions = positions; result._colors = colors; result._ellipsoid = ellipsoid; result._colorsPerVertex = colorsPerVertex; result._arcType = arcType; result._granularity = granularity; return result; }; var scratchArray1 = new Array(2); var scratchArray2 = new Array(2); var generateArcOptionsScratch$1 = { positions: scratchArray1, height: scratchArray2, ellipsoid: undefined, minDistance: undefined, granularity: undefined, }; /** * Computes the geometric representation of a simple polyline, including its vertices, indices, and a bounding sphere. * * @param {SimplePolylineGeometry} simplePolylineGeometry A description of the polyline. * @returns {Geometry|undefined} The computed vertices and indices. */ SimplePolylineGeometry.createGeometry = function (simplePolylineGeometry) { var positions = simplePolylineGeometry._positions; var colors = simplePolylineGeometry._colors; var colorsPerVertex = simplePolylineGeometry._colorsPerVertex; var arcType = simplePolylineGeometry._arcType; var granularity = simplePolylineGeometry._granularity; var ellipsoid = simplePolylineGeometry._ellipsoid; var minDistance = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); var perSegmentColors = defined(colors) && !colorsPerVertex; var i; var length = positions.length; var positionValues; var numberOfPositions; var colorValues; var color; var offset = 0; if (arcType === ArcType$1.GEODESIC || arcType === ArcType$1.RHUMB) { var subdivisionSize; var numberOfPointsFunction; var generateArcFunction; if (arcType === ArcType$1.GEODESIC) { subdivisionSize = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); numberOfPointsFunction = PolylinePipeline.numberOfPoints; generateArcFunction = PolylinePipeline.generateArc; } else { subdivisionSize = granularity; numberOfPointsFunction = PolylinePipeline.numberOfPointsRhumbLine; generateArcFunction = PolylinePipeline.generateRhumbArc; } var heights = PolylinePipeline.extractHeights(positions, ellipsoid); var generateArcOptions = generateArcOptionsScratch$1; if (arcType === ArcType$1.GEODESIC) { generateArcOptions.minDistance = minDistance; } else { generateArcOptions.granularity = granularity; } generateArcOptions.ellipsoid = ellipsoid; if (perSegmentColors) { var positionCount = 0; for (i = 0; i < length - 1; i++) { positionCount += numberOfPointsFunction( positions[i], positions[i + 1], subdivisionSize ) + 1; } positionValues = new Float64Array(positionCount * 3); colorValues = new Uint8Array(positionCount * 4); generateArcOptions.positions = scratchArray1; generateArcOptions.height = scratchArray2; var ci = 0; for (i = 0; i < length - 1; ++i) { scratchArray1[0] = positions[i]; scratchArray1[1] = positions[i + 1]; scratchArray2[0] = heights[i]; scratchArray2[1] = heights[i + 1]; var pos = generateArcFunction(generateArcOptions); if (defined(colors)) { var segLen = pos.length / 3; color = colors[i]; for (var k = 0; k < segLen; ++k) { colorValues[ci++] = Color.floatToByte(color.red); colorValues[ci++] = Color.floatToByte(color.green); colorValues[ci++] = Color.floatToByte(color.blue); colorValues[ci++] = Color.floatToByte(color.alpha); } } positionValues.set(pos, offset); offset += pos.length; } } else { generateArcOptions.positions = positions; generateArcOptions.height = heights; positionValues = new Float64Array( generateArcFunction(generateArcOptions) ); if (defined(colors)) { colorValues = new Uint8Array((positionValues.length / 3) * 4); for (i = 0; i < length - 1; ++i) { var p0 = positions[i]; var p1 = positions[i + 1]; var c0 = colors[i]; var c1 = colors[i + 1]; offset = interpolateColors( p0, p1, c0, c1, minDistance, colorValues, offset ); } var lastColor = colors[length - 1]; colorValues[offset++] = Color.floatToByte(lastColor.red); colorValues[offset++] = Color.floatToByte(lastColor.green); colorValues[offset++] = Color.floatToByte(lastColor.blue); colorValues[offset++] = Color.floatToByte(lastColor.alpha); } } } else { numberOfPositions = perSegmentColors ? length * 2 - 2 : length; positionValues = new Float64Array(numberOfPositions * 3); colorValues = defined(colors) ? new Uint8Array(numberOfPositions * 4) : undefined; var positionIndex = 0; var colorIndex = 0; for (i = 0; i < length; ++i) { var p = positions[i]; if (perSegmentColors && i > 0) { Cartesian3.pack(p, positionValues, positionIndex); positionIndex += 3; color = colors[i - 1]; colorValues[colorIndex++] = Color.floatToByte(color.red); colorValues[colorIndex++] = Color.floatToByte(color.green); colorValues[colorIndex++] = Color.floatToByte(color.blue); colorValues[colorIndex++] = Color.floatToByte(color.alpha); } if (perSegmentColors && i === length - 1) { break; } Cartesian3.pack(p, positionValues, positionIndex); positionIndex += 3; if (defined(colors)) { color = colors[i]; colorValues[colorIndex++] = Color.floatToByte(color.red); colorValues[colorIndex++] = Color.floatToByte(color.green); colorValues[colorIndex++] = Color.floatToByte(color.blue); colorValues[colorIndex++] = Color.floatToByte(color.alpha); } } } var attributes = new GeometryAttributes(); attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positionValues, }); if (defined(colors)) { attributes.color = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 4, values: colorValues, normalize: true, }); } numberOfPositions = positionValues.length / 3; var numberOfIndices = (numberOfPositions - 1) * 2; var indices = IndexDatatype$1.createTypedArray( numberOfPositions, numberOfIndices ); var index = 0; for (i = 0; i < numberOfPositions - 1; ++i) { indices[index++] = i; indices[index++] = i + 1; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: BoundingSphere.fromPoints(positions), }); }; /** * A description of a sphere centered at the origin. * * @alias SphereGeometry * @constructor * * @param {Object} [options] Object with the following properties: * @param {Number} [options.radius=1.0] The radius of the sphere. * @param {Number} [options.stackPartitions=64] The number of times to partition the ellipsoid into stacks. * @param {Number} [options.slicePartitions=64] The number of times to partition the ellipsoid into radial slices. * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @exception {DeveloperError} options.slicePartitions cannot be less than three. * @exception {DeveloperError} options.stackPartitions cannot be less than three. * * @see SphereGeometry#createGeometry * * @example * var sphere = new Cesium.SphereGeometry({ * radius : 100.0, * vertexFormat : Cesium.VertexFormat.POSITION_ONLY * }); * var geometry = Cesium.SphereGeometry.createGeometry(sphere); */ function SphereGeometry(options) { var radius = defaultValue(options.radius, 1.0); var radii = new Cartesian3(radius, radius, radius); var ellipsoidOptions = { radii: radii, stackPartitions: options.stackPartitions, slicePartitions: options.slicePartitions, vertexFormat: options.vertexFormat, }; this._ellipsoidGeometry = new EllipsoidGeometry(ellipsoidOptions); this._workerName = "createSphereGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ SphereGeometry.packedLength = EllipsoidGeometry.packedLength; /** * Stores the provided instance into the provided array. * * @param {SphereGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ SphereGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); //>>includeEnd('debug'); return EllipsoidGeometry.pack(value._ellipsoidGeometry, array, startingIndex); }; var scratchEllipsoidGeometry$1 = new EllipsoidGeometry(); var scratchOptions$3 = { radius: undefined, radii: new Cartesian3(), vertexFormat: new VertexFormat(), stackPartitions: undefined, slicePartitions: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {SphereGeometry} [result] The object into which to store the result. * @returns {SphereGeometry} The modified result parameter or a new SphereGeometry instance if one was not provided. */ SphereGeometry.unpack = function (array, startingIndex, result) { var ellipsoidGeometry = EllipsoidGeometry.unpack( array, startingIndex, scratchEllipsoidGeometry$1 ); scratchOptions$3.vertexFormat = VertexFormat.clone( ellipsoidGeometry._vertexFormat, scratchOptions$3.vertexFormat ); scratchOptions$3.stackPartitions = ellipsoidGeometry._stackPartitions; scratchOptions$3.slicePartitions = ellipsoidGeometry._slicePartitions; if (!defined(result)) { scratchOptions$3.radius = ellipsoidGeometry._radii.x; return new SphereGeometry(scratchOptions$3); } Cartesian3.clone(ellipsoidGeometry._radii, scratchOptions$3.radii); result._ellipsoidGeometry = new EllipsoidGeometry(scratchOptions$3); return result; }; /** * Computes the geometric representation of a sphere, including its vertices, indices, and a bounding sphere. * * @param {SphereGeometry} sphereGeometry A description of the sphere. * @returns {Geometry|undefined} The computed vertices and indices. */ SphereGeometry.createGeometry = function (sphereGeometry) { return EllipsoidGeometry.createGeometry(sphereGeometry._ellipsoidGeometry); }; /** * A description of the outline of a sphere. * * @alias SphereOutlineGeometry * @constructor * * @param {Object} [options] Object with the following properties: * @param {Number} [options.radius=1.0] The radius of the sphere. * @param {Number} [options.stackPartitions=10] The count of stacks for the sphere (1 greater than the number of parallel lines). * @param {Number} [options.slicePartitions=8] The count of slices for the sphere (Equal to the number of radial lines). * @param {Number} [options.subdivisions=200] The number of points per line, determining the granularity of the curvature . * * @exception {DeveloperError} options.stackPartitions must be greater than or equal to one. * @exception {DeveloperError} options.slicePartitions must be greater than or equal to zero. * @exception {DeveloperError} options.subdivisions must be greater than or equal to zero. * * @example * var sphere = new Cesium.SphereOutlineGeometry({ * radius : 100.0, * stackPartitions : 6, * slicePartitions: 5 * }); * var geometry = Cesium.SphereOutlineGeometry.createGeometry(sphere); */ function SphereOutlineGeometry(options) { var radius = defaultValue(options.radius, 1.0); var radii = new Cartesian3(radius, radius, radius); var ellipsoidOptions = { radii: radii, stackPartitions: options.stackPartitions, slicePartitions: options.slicePartitions, subdivisions: options.subdivisions, }; this._ellipsoidGeometry = new EllipsoidOutlineGeometry(ellipsoidOptions); this._workerName = "createSphereOutlineGeometry"; } /** * The number of elements used to pack the object into an array. * @type {Number} */ SphereOutlineGeometry.packedLength = EllipsoidOutlineGeometry.packedLength; /** * Stores the provided instance into the provided array. * * @param {SphereOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ SphereOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); //>>includeEnd('debug'); return EllipsoidOutlineGeometry.pack( value._ellipsoidGeometry, array, startingIndex ); }; var scratchEllipsoidGeometry = new EllipsoidOutlineGeometry(); var scratchOptions$2 = { radius: undefined, radii: new Cartesian3(), stackPartitions: undefined, slicePartitions: undefined, subdivisions: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {SphereOutlineGeometry} [result] The object into which to store the result. * @returns {SphereOutlineGeometry} The modified result parameter or a new SphereOutlineGeometry instance if one was not provided. */ SphereOutlineGeometry.unpack = function (array, startingIndex, result) { var ellipsoidGeometry = EllipsoidOutlineGeometry.unpack( array, startingIndex, scratchEllipsoidGeometry ); scratchOptions$2.stackPartitions = ellipsoidGeometry._stackPartitions; scratchOptions$2.slicePartitions = ellipsoidGeometry._slicePartitions; scratchOptions$2.subdivisions = ellipsoidGeometry._subdivisions; if (!defined(result)) { scratchOptions$2.radius = ellipsoidGeometry._radii.x; return new SphereOutlineGeometry(scratchOptions$2); } Cartesian3.clone(ellipsoidGeometry._radii, scratchOptions$2.radii); result._ellipsoidGeometry = new EllipsoidOutlineGeometry(scratchOptions$2); return result; }; /** * Computes the geometric representation of an outline of a sphere, including its vertices, indices, and a bounding sphere. * * @param {SphereOutlineGeometry} sphereGeometry A description of the sphere outline. * @returns {Geometry|undefined} The computed vertices and indices. */ SphereOutlineGeometry.createGeometry = function (sphereGeometry) { return EllipsoidOutlineGeometry.createGeometry( sphereGeometry._ellipsoidGeometry ); }; /** * A set of curvilinear 3-dimensional coordinates. * * @alias Spherical * @constructor * * @param {Number} [clock=0.0] The angular coordinate lying in the xy-plane measured from the positive x-axis and toward the positive y-axis. * @param {Number} [cone=0.0] The angular coordinate measured from the positive z-axis and toward the negative z-axis. * @param {Number} [magnitude=1.0] The linear coordinate measured from the origin. */ function Spherical(clock, cone, magnitude) { /** * The clock component. * @type {Number} * @default 0.0 */ this.clock = defaultValue(clock, 0.0); /** * The cone component. * @type {Number} * @default 0.0 */ this.cone = defaultValue(cone, 0.0); /** * The magnitude component. * @type {Number} * @default 1.0 */ this.magnitude = defaultValue(magnitude, 1.0); } /** * Converts the provided Cartesian3 into Spherical coordinates. * * @param {Cartesian3} cartesian3 The Cartesian3 to be converted to Spherical. * @param {Spherical} [result] The object in which the result will be stored, if undefined a new instance will be created. * @returns {Spherical} The modified result parameter, or a new instance if one was not provided. */ Spherical.fromCartesian3 = function (cartesian3, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("cartesian3", cartesian3); //>>includeEnd('debug'); var x = cartesian3.x; var y = cartesian3.y; var z = cartesian3.z; var radialSquared = x * x + y * y; if (!defined(result)) { result = new Spherical(); } result.clock = Math.atan2(y, x); result.cone = Math.atan2(Math.sqrt(radialSquared), z); result.magnitude = Math.sqrt(radialSquared + z * z); return result; }; /** * Creates a duplicate of a Spherical. * * @param {Spherical} spherical The spherical to clone. * @param {Spherical} [result] The object to store the result into, if undefined a new instance will be created. * @returns {Spherical} The modified result parameter or a new instance if result was undefined. (Returns undefined if spherical is undefined) */ Spherical.clone = function (spherical, result) { if (!defined(spherical)) { return undefined; } if (!defined(result)) { return new Spherical(spherical.clock, spherical.cone, spherical.magnitude); } result.clock = spherical.clock; result.cone = spherical.cone; result.magnitude = spherical.magnitude; return result; }; /** * Computes the normalized version of the provided spherical. * * @param {Spherical} spherical The spherical to be normalized. * @param {Spherical} [result] The object to store the result into, if undefined a new instance will be created. * @returns {Spherical} The modified result parameter or a new instance if result was undefined. */ Spherical.normalize = function (spherical, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("spherical", spherical); //>>includeEnd('debug'); if (!defined(result)) { return new Spherical(spherical.clock, spherical.cone, 1.0); } result.clock = spherical.clock; result.cone = spherical.cone; result.magnitude = 1.0; return result; }; /** * Returns true if the first spherical is equal to the second spherical, false otherwise. * * @param {Spherical} left The first Spherical to be compared. * @param {Spherical} right The second Spherical to be compared. * @returns {Boolean} true if the first spherical is equal to the second spherical, false otherwise. */ Spherical.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left.clock === right.clock && left.cone === right.cone && left.magnitude === right.magnitude) ); }; /** * Returns true if the first spherical is within the provided epsilon of the second spherical, false otherwise. * * @param {Spherical} left The first Spherical to be compared. * @param {Spherical} right The second Spherical to be compared. * @param {Number} [epsilon=0.0] The epsilon to compare against. * @returns {Boolean} true if the first spherical is within the provided epsilon of the second spherical, false otherwise. */ Spherical.equalsEpsilon = function (left, right, epsilon) { epsilon = defaultValue(epsilon, 0.0); return ( left === right || (defined(left) && defined(right) && Math.abs(left.clock - right.clock) <= epsilon && Math.abs(left.cone - right.cone) <= epsilon && Math.abs(left.magnitude - right.magnitude) <= epsilon) ); }; /** * Returns true if this spherical is equal to the provided spherical, false otherwise. * * @param {Spherical} other The Spherical to be compared. * @returns {Boolean} true if this spherical is equal to the provided spherical, false otherwise. */ Spherical.prototype.equals = function (other) { return Spherical.equals(this, other); }; /** * Creates a duplicate of this Spherical. * * @param {Spherical} [result] The object to store the result into, if undefined a new instance will be created. * @returns {Spherical} The modified result parameter or a new instance if result was undefined. */ Spherical.prototype.clone = function (result) { return Spherical.clone(this, result); }; /** * Returns true if this spherical is within the provided epsilon of the provided spherical, false otherwise. * * @param {Spherical} other The Spherical to be compared. * @param {Number} epsilon The epsilon to compare against. * @returns {Boolean} true if this spherical is within the provided epsilon of the provided spherical, false otherwise. */ Spherical.prototype.equalsEpsilon = function (other, epsilon) { return Spherical.equalsEpsilon(this, other, epsilon); }; /** * Returns a string representing this instance in the format (clock, cone, magnitude). * * @returns {String} A string representing this instance. */ Spherical.prototype.toString = function () { return "(" + this.clock + ", " + this.cone + ", " + this.magnitude + ")"; }; /** * @private */ var TileEdge = { WEST: 0, NORTH: 1, EAST: 2, SOUTH: 3, NORTHWEST: 4, NORTHEAST: 5, SOUTHWEST: 6, SOUTHEAST: 7, }; /** * A tiling scheme for geometry or imagery on the surface of an ellipsoid. At level-of-detail zero, * the coarsest, least-detailed level, the number of tiles is configurable. * At level of detail one, each of the level zero tiles has four children, two in each direction. * At level of detail two, each of the level one tiles has four children, two in each direction. * This continues for as many levels as are present in the geometry or imagery source. * * @alias TilingScheme * @constructor * * @see WebMercatorTilingScheme * @see GeographicTilingScheme */ function TilingScheme(options) { //>>includeStart('debug', pragmas.debug); throw new DeveloperError( "This type should not be instantiated directly. Instead, use WebMercatorTilingScheme or GeographicTilingScheme." ); //>>includeEnd('debug'); } Object.defineProperties(TilingScheme.prototype, { /** * Gets the ellipsoid that is tiled by the tiling scheme. * @memberof TilingScheme.prototype * @type {Ellipsoid} */ ellipsoid: { get: DeveloperError.throwInstantiationError, }, /** * Gets the rectangle, in radians, covered by this tiling scheme. * @memberof TilingScheme.prototype * @type {Rectangle} */ rectangle: { get: DeveloperError.throwInstantiationError, }, /** * Gets the map projection used by the tiling scheme. * @memberof TilingScheme.prototype * @type {MapProjection} */ projection: { get: DeveloperError.throwInstantiationError, }, }); /** * Gets the total number of tiles in the X direction at a specified level-of-detail. * @function * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the X direction at the given level. */ TilingScheme.prototype.getNumberOfXTilesAtLevel = DeveloperError.throwInstantiationError; /** * Gets the total number of tiles in the Y direction at a specified level-of-detail. * @function * * @param {Number} level The level-of-detail. * @returns {Number} The number of tiles in the Y direction at the given level. */ TilingScheme.prototype.getNumberOfYTilesAtLevel = DeveloperError.throwInstantiationError; /** * Transforms a rectangle specified in geodetic radians to the native coordinate system * of this tiling scheme. * @function * * @param {Rectangle} rectangle The rectangle to transform. * @param {Rectangle} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the native rectangle if 'result' * is undefined. */ TilingScheme.prototype.rectangleToNativeRectangle = DeveloperError.throwInstantiationError; /** * Converts tile x, y coordinates and level to a rectangle expressed in the native coordinates * of the tiling scheme. * @function * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ TilingScheme.prototype.tileXYToNativeRectangle = DeveloperError.throwInstantiationError; /** * Converts tile x, y coordinates and level to a cartographic rectangle in radians. * @function * * @param {Number} x The integer x coordinate of the tile. * @param {Number} y The integer y coordinate of the tile. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Object} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Rectangle} The specified 'result', or a new object containing the rectangle * if 'result' is undefined. */ TilingScheme.prototype.tileXYToRectangle = DeveloperError.throwInstantiationError; /** * Calculates the tile x, y coordinates of the tile containing * a given cartographic position. * @function * * @param {Cartographic} position The position. * @param {Number} level The tile level-of-detail. Zero is the least detailed. * @param {Cartesian2} [result] The instance to which to copy the result, or undefined if a new instance * should be created. * @returns {Cartesian2} The specified 'result', or a new object containing the tile x, y coordinates * if 'result' is undefined. */ TilingScheme.prototype.positionToTileXY = DeveloperError.throwInstantiationError; function compareIntervalStartTimes(left, right) { return JulianDate.compare(left.start, right.start); } /** * A non-overlapping collection of {@link TimeInterval} instances sorted by start time. * @alias TimeIntervalCollection * @constructor * * @param {TimeInterval[]} [intervals] An array of intervals to add to the collection. */ function TimeIntervalCollection(intervals) { this._intervals = []; this._changedEvent = new Event(); if (defined(intervals)) { var length = intervals.length; for (var i = 0; i < length; i++) { this.addInterval(intervals[i]); } } } Object.defineProperties(TimeIntervalCollection.prototype, { /** * Gets an event that is raised whenever the collection of intervals change. * @memberof TimeIntervalCollection.prototype * @type {Event} * @readonly */ changedEvent: { get: function () { return this._changedEvent; }, }, /** * Gets the start time of the collection. * @memberof TimeIntervalCollection.prototype * @type {JulianDate} * @readonly */ start: { get: function () { var intervals = this._intervals; return intervals.length === 0 ? undefined : intervals[0].start; }, }, /** * Gets whether or not the start time is included in the collection. * @memberof TimeIntervalCollection.prototype * @type {Boolean} * @readonly */ isStartIncluded: { get: function () { var intervals = this._intervals; return intervals.length === 0 ? false : intervals[0].isStartIncluded; }, }, /** * Gets the stop time of the collection. * @memberof TimeIntervalCollection.prototype * @type {JulianDate} * @readonly */ stop: { get: function () { var intervals = this._intervals; var length = intervals.length; return length === 0 ? undefined : intervals[length - 1].stop; }, }, /** * Gets whether or not the stop time is included in the collection. * @memberof TimeIntervalCollection.prototype * @type {Boolean} * @readonly */ isStopIncluded: { get: function () { var intervals = this._intervals; var length = intervals.length; return length === 0 ? false : intervals[length - 1].isStopIncluded; }, }, /** * Gets the number of intervals in the collection. * @memberof TimeIntervalCollection.prototype * @type {Number} * @readonly */ length: { get: function () { return this._intervals.length; }, }, /** * Gets whether or not the collection is empty. * @memberof TimeIntervalCollection.prototype * @type {Boolean} * @readonly */ isEmpty: { get: function () { return this._intervals.length === 0; }, }, }); /** * Compares this instance against the provided instance componentwise and returns * true if they are equal, false otherwise. * * @param {TimeIntervalCollection} [right] The right hand side collection. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @returns {Boolean} true if they are equal, false otherwise. */ TimeIntervalCollection.prototype.equals = function (right, dataComparer) { if (this === right) { return true; } if (!(right instanceof TimeIntervalCollection)) { return false; } var intervals = this._intervals; var rightIntervals = right._intervals; var length = intervals.length; if (length !== rightIntervals.length) { return false; } for (var i = 0; i < length; i++) { if (!TimeInterval.equals(intervals[i], rightIntervals[i], dataComparer)) { return false; } } return true; }; /** * Gets the interval at the specified index. * * @param {Number} index The index of the interval to retrieve. * @returns {TimeInterval|undefined} The interval at the specified index, or undefined if no interval exists as that index. */ TimeIntervalCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); return this._intervals[index]; }; /** * Removes all intervals from the collection. */ TimeIntervalCollection.prototype.removeAll = function () { if (this._intervals.length > 0) { this._intervals.length = 0; this._changedEvent.raiseEvent(this); } }; /** * Finds and returns the interval that contains the specified date. * * @param {JulianDate} date The date to search for. * @returns {TimeInterval|undefined} The interval containing the specified date, undefined if no such interval exists. */ TimeIntervalCollection.prototype.findIntervalContainingDate = function (date) { var index = this.indexOf(date); return index >= 0 ? this._intervals[index] : undefined; }; /** * Finds and returns the data for the interval that contains the specified date. * * @param {JulianDate} date The date to search for. * @returns {Object} The data for the interval containing the specified date, or undefined if no such interval exists. */ TimeIntervalCollection.prototype.findDataForIntervalContainingDate = function ( date ) { var index = this.indexOf(date); return index >= 0 ? this._intervals[index].data : undefined; }; /** * Checks if the specified date is inside this collection. * * @param {JulianDate} julianDate The date to check. * @returns {Boolean} true if the collection contains the specified date, false otherwise. */ TimeIntervalCollection.prototype.contains = function (julianDate) { return this.indexOf(julianDate) >= 0; }; var indexOfScratch = new TimeInterval(); /** * Finds and returns the index of the interval in the collection that contains the specified date. * * @param {JulianDate} date The date to search for. * @returns {Number} The index of the interval that contains the specified date, if no such interval exists, * it returns a negative number which is the bitwise complement of the index of the next interval that * starts after the date, or if no interval starts after the specified date, the bitwise complement of * the length of the collection. */ TimeIntervalCollection.prototype.indexOf = function (date) { //>>includeStart('debug', pragmas.debug); if (!defined(date)) { throw new DeveloperError("date is required"); } //>>includeEnd('debug'); var intervals = this._intervals; indexOfScratch.start = date; indexOfScratch.stop = date; var index = binarySearch( intervals, indexOfScratch, compareIntervalStartTimes ); if (index >= 0) { if (intervals[index].isStartIncluded) { return index; } if ( index > 0 && intervals[index - 1].stop.equals(date) && intervals[index - 1].isStopIncluded ) { return index - 1; } return ~index; } index = ~index; if ( index > 0 && index - 1 < intervals.length && TimeInterval.contains(intervals[index - 1], date) ) { return index - 1; } return ~index; }; /** * Returns the first interval in the collection that matches the specified parameters. * All parameters are optional and undefined parameters are treated as a don't care condition. * * @param {Object} [options] Object with the following properties: * @param {JulianDate} [options.start] The start time of the interval. * @param {JulianDate} [options.stop] The stop time of the interval. * @param {Boolean} [options.isStartIncluded] true if options.start is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded] true if options.stop is included in the interval, false otherwise. * @returns {TimeInterval|undefined} The first interval in the collection that matches the specified parameters. */ TimeIntervalCollection.prototype.findInterval = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var start = options.start; var stop = options.stop; var isStartIncluded = options.isStartIncluded; var isStopIncluded = options.isStopIncluded; var intervals = this._intervals; for (var i = 0, len = intervals.length; i < len; i++) { var interval = intervals[i]; if ( (!defined(start) || interval.start.equals(start)) && (!defined(stop) || interval.stop.equals(stop)) && (!defined(isStartIncluded) || interval.isStartIncluded === isStartIncluded) && (!defined(isStopIncluded) || interval.isStopIncluded === isStopIncluded) ) { return intervals[i]; } } return undefined; }; /** * Adds an interval to the collection, merging intervals that contain the same data and * splitting intervals of different data as needed in order to maintain a non-overlapping collection. * The data in the new interval takes precedence over any existing intervals in the collection. * * @param {TimeInterval} interval The interval to add. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. */ TimeIntervalCollection.prototype.addInterval = function ( interval, dataComparer ) { //>>includeStart('debug', pragmas.debug); if (!defined(interval)) { throw new DeveloperError("interval is required"); } //>>includeEnd('debug'); if (interval.isEmpty) { return; } var intervals = this._intervals; // Handle the common case quickly: we're adding a new interval which is after all existing intervals. if ( intervals.length === 0 || JulianDate.greaterThan(interval.start, intervals[intervals.length - 1].stop) ) { intervals.push(interval); this._changedEvent.raiseEvent(this); return; } // Keep the list sorted by the start date var index = binarySearch(intervals, interval, compareIntervalStartTimes); if (index < 0) { index = ~index; } else { // interval's start date exactly equals the start date of at least one interval in the collection. // It could actually equal the start date of two intervals if one of them does not actually // include the date. In that case, the binary search could have found either. We need to // look at the surrounding intervals and their IsStartIncluded properties in order to make sure // we're working with the correct interval. // eslint-disable-next-line no-lonely-if if ( index > 0 && interval.isStartIncluded && intervals[index - 1].isStartIncluded && intervals[index - 1].start.equals(interval.start) ) { --index; } else if ( index < intervals.length && !interval.isStartIncluded && intervals[index].isStartIncluded && intervals[index].start.equals(interval.start) ) { ++index; } } var comparison; if (index > 0) { // Not the first thing in the list, so see if the interval before this one // overlaps this one. comparison = JulianDate.compare(intervals[index - 1].stop, interval.start); if ( comparison > 0 || (comparison === 0 && (intervals[index - 1].isStopIncluded || interval.isStartIncluded)) ) { // There is an overlap if ( defined(dataComparer) ? dataComparer(intervals[index - 1].data, interval.data) : intervals[index - 1].data === interval.data ) { // Overlapping intervals have the same data, so combine them if (JulianDate.greaterThan(interval.stop, intervals[index - 1].stop)) { interval = new TimeInterval({ start: intervals[index - 1].start, stop: interval.stop, isStartIncluded: intervals[index - 1].isStartIncluded, isStopIncluded: interval.isStopIncluded, data: interval.data, }); } else { interval = new TimeInterval({ start: intervals[index - 1].start, stop: intervals[index - 1].stop, isStartIncluded: intervals[index - 1].isStartIncluded, isStopIncluded: intervals[index - 1].isStopIncluded || (interval.stop.equals(intervals[index - 1].stop) && interval.isStopIncluded), data: interval.data, }); } intervals.splice(index - 1, 1); --index; } else { // Overlapping intervals have different data. The new interval // being added 'wins' so truncate the previous interval. // If the existing interval extends past the end of the new one, // split the existing interval into two intervals. comparison = JulianDate.compare( intervals[index - 1].stop, interval.stop ); if ( comparison > 0 || (comparison === 0 && intervals[index - 1].isStopIncluded && !interval.isStopIncluded) ) { intervals.splice( index, 0, new TimeInterval({ start: interval.stop, stop: intervals[index - 1].stop, isStartIncluded: !interval.isStopIncluded, isStopIncluded: intervals[index - 1].isStopIncluded, data: intervals[index - 1].data, }) ); } intervals[index - 1] = new TimeInterval({ start: intervals[index - 1].start, stop: interval.start, isStartIncluded: intervals[index - 1].isStartIncluded, isStopIncluded: !interval.isStartIncluded, data: intervals[index - 1].data, }); } } } while (index < intervals.length) { // Not the last thing in the list, so see if the intervals after this one overlap this one. comparison = JulianDate.compare(interval.stop, intervals[index].start); if ( comparison > 0 || (comparison === 0 && (interval.isStopIncluded || intervals[index].isStartIncluded)) ) { // There is an overlap if ( defined(dataComparer) ? dataComparer(intervals[index].data, interval.data) : intervals[index].data === interval.data ) { // Overlapping intervals have the same data, so combine them interval = new TimeInterval({ start: interval.start, stop: JulianDate.greaterThan(intervals[index].stop, interval.stop) ? intervals[index].stop : interval.stop, isStartIncluded: interval.isStartIncluded, isStopIncluded: JulianDate.greaterThan( intervals[index].stop, interval.stop ) ? intervals[index].isStopIncluded : interval.isStopIncluded, data: interval.data, }); intervals.splice(index, 1); } else { // Overlapping intervals have different data. The new interval // being added 'wins' so truncate the next interval. intervals[index] = new TimeInterval({ start: interval.stop, stop: intervals[index].stop, isStartIncluded: !interval.isStopIncluded, isStopIncluded: intervals[index].isStopIncluded, data: intervals[index].data, }); if (intervals[index].isEmpty) { intervals.splice(index, 1); } else { // Found a partial span, so it is not possible for the next // interval to be spanned at all. Stop looking. break; } } } else { // Found the last one we're spanning, so stop looking. break; } } // Add the new interval intervals.splice(index, 0, interval); this._changedEvent.raiseEvent(this); }; /** * Removes the specified interval from this interval collection, creating a hole over the specified interval. * The data property of the input interval is ignored. * * @param {TimeInterval} interval The interval to remove. * @returns {Boolean} true if the interval was removed, false if no part of the interval was in the collection. */ TimeIntervalCollection.prototype.removeInterval = function (interval) { //>>includeStart('debug', pragmas.debug); if (!defined(interval)) { throw new DeveloperError("interval is required"); } //>>includeEnd('debug'); if (interval.isEmpty) { return false; } var intervals = this._intervals; var index = binarySearch(intervals, interval, compareIntervalStartTimes); if (index < 0) { index = ~index; } var result = false; // Check for truncation of the end of the previous interval. if ( index > 0 && (JulianDate.greaterThan(intervals[index - 1].stop, interval.start) || (intervals[index - 1].stop.equals(interval.start) && intervals[index - 1].isStopIncluded && interval.isStartIncluded)) ) { result = true; if ( JulianDate.greaterThan(intervals[index - 1].stop, interval.stop) || (intervals[index - 1].isStopIncluded && !interval.isStopIncluded && intervals[index - 1].stop.equals(interval.stop)) ) { // Break the existing interval into two pieces intervals.splice( index, 0, new TimeInterval({ start: interval.stop, stop: intervals[index - 1].stop, isStartIncluded: !interval.isStopIncluded, isStopIncluded: intervals[index - 1].isStopIncluded, data: intervals[index - 1].data, }) ); } intervals[index - 1] = new TimeInterval({ start: intervals[index - 1].start, stop: interval.start, isStartIncluded: intervals[index - 1].isStartIncluded, isStopIncluded: !interval.isStartIncluded, data: intervals[index - 1].data, }); } // Check if the Start of the current interval should remain because interval.start is the same but // it is not included. if ( index < intervals.length && !interval.isStartIncluded && intervals[index].isStartIncluded && interval.start.equals(intervals[index].start) ) { result = true; intervals.splice( index, 0, new TimeInterval({ start: intervals[index].start, stop: intervals[index].start, isStartIncluded: true, isStopIncluded: true, data: intervals[index].data, }) ); ++index; } // Remove any intervals that are completely overlapped by the input interval. while ( index < intervals.length && JulianDate.greaterThan(interval.stop, intervals[index].stop) ) { result = true; intervals.splice(index, 1); } // Check for the case where the input interval ends on the same date // as an existing interval. if (index < intervals.length && interval.stop.equals(intervals[index].stop)) { result = true; if (!interval.isStopIncluded && intervals[index].isStopIncluded) { // Last point of interval should remain because the stop date is included in // the existing interval but is not included in the input interval. if ( index + 1 < intervals.length && intervals[index + 1].start.equals(interval.stop) && intervals[index].data === intervals[index + 1].data ) { // Combine single point with the next interval intervals.splice(index, 1); intervals[index] = new TimeInterval({ start: intervals[index].start, stop: intervals[index].stop, isStartIncluded: true, isStopIncluded: intervals[index].isStopIncluded, data: intervals[index].data, }); } else { intervals[index] = new TimeInterval({ start: interval.stop, stop: interval.stop, isStartIncluded: true, isStopIncluded: true, data: intervals[index].data, }); } } else { // Interval is completely overlapped intervals.splice(index, 1); } } // Truncate any partially-overlapped intervals. if ( index < intervals.length && (JulianDate.greaterThan(interval.stop, intervals[index].start) || (interval.stop.equals(intervals[index].start) && interval.isStopIncluded && intervals[index].isStartIncluded)) ) { result = true; intervals[index] = new TimeInterval({ start: interval.stop, stop: intervals[index].stop, isStartIncluded: !interval.isStopIncluded, isStopIncluded: intervals[index].isStopIncluded, data: intervals[index].data, }); } if (result) { this._changedEvent.raiseEvent(this); } return result; }; /** * Creates a new instance that is the intersection of this collection and the provided collection. * * @param {TimeIntervalCollection} other The collection to intersect with. * @param {TimeInterval.DataComparer} [dataComparer] A function which compares the data of the two intervals. If omitted, reference equality is used. * @param {TimeInterval.MergeCallback} [mergeCallback] A function which merges the data of the two intervals. If omitted, the data from the left interval will be used. * @returns {TimeIntervalCollection} A new TimeIntervalCollection which is the intersection of this collection and the provided collection. */ TimeIntervalCollection.prototype.intersect = function ( other, dataComparer, mergeCallback ) { //>>includeStart('debug', pragmas.debug); if (!defined(other)) { throw new DeveloperError("other is required."); } //>>includeEnd('debug'); var result = new TimeIntervalCollection(); var left = 0; var right = 0; var intervals = this._intervals; var otherIntervals = other._intervals; while (left < intervals.length && right < otherIntervals.length) { var leftInterval = intervals[left]; var rightInterval = otherIntervals[right]; if (JulianDate.lessThan(leftInterval.stop, rightInterval.start)) { ++left; } else if (JulianDate.lessThan(rightInterval.stop, leftInterval.start)) { ++right; } else { // The following will return an intersection whose data is 'merged' if the callback is defined if ( defined(mergeCallback) || (defined(dataComparer) && dataComparer(leftInterval.data, rightInterval.data)) || (!defined(dataComparer) && rightInterval.data === leftInterval.data) ) { var intersection = TimeInterval.intersect( leftInterval, rightInterval, new TimeInterval(), mergeCallback ); if (!intersection.isEmpty) { // Since we start with an empty collection for 'result', and there are no overlapping intervals in 'this' (as a rule), // the 'intersection' will never overlap with a previous interval in 'result'. So, no need to do any additional 'merging'. result.addInterval(intersection, dataComparer); } } if ( JulianDate.lessThan(leftInterval.stop, rightInterval.stop) || (leftInterval.stop.equals(rightInterval.stop) && !leftInterval.isStopIncluded && rightInterval.isStopIncluded) ) { ++left; } else { ++right; } } } return result; }; /** * Creates a new instance from a JulianDate array. * * @param {Object} options Object with the following properties: * @param {JulianDate[]} options.julianDates An array of ISO 8601 dates. * @param {Boolean} [options.isStartIncluded=true] true if start time is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if stop time is included in the interval, false otherwise. * @param {Boolean} [options.leadingInterval=false] true if you want to add a interval from Iso8601.MINIMUM_VALUE to start time, false otherwise. * @param {Boolean} [options.trailingInterval=false] true if you want to add a interval from stop time to Iso8601.MAXIMUM_VALUE, false otherwise. * @param {Function} [options.dataCallback] A function that will be return the data that is called with each interval before it is added to the collection. If unspecified, the data will be the index in the collection. * @param {TimeIntervalCollection} [result] An existing instance to use for the result. * @returns {TimeIntervalCollection} The modified result parameter or a new instance if none was provided. */ TimeIntervalCollection.fromJulianDateArray = function (options, result) { //>>includeStart('debug', pragmas.debug); if (!defined(options)) { throw new DeveloperError("options is required."); } if (!defined(options.julianDates)) { throw new DeveloperError("options.iso8601Array is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new TimeIntervalCollection(); } var julianDates = options.julianDates; var length = julianDates.length; var dataCallback = options.dataCallback; var isStartIncluded = defaultValue(options.isStartIncluded, true); var isStopIncluded = defaultValue(options.isStopIncluded, true); var leadingInterval = defaultValue(options.leadingInterval, false); var trailingInterval = defaultValue(options.trailingInterval, false); var interval; // Add a default interval, which will only end up being used up to first interval var startIndex = 0; if (leadingInterval) { ++startIndex; interval = new TimeInterval({ start: Iso8601.MINIMUM_VALUE, stop: julianDates[0], isStartIncluded: true, isStopIncluded: !isStartIncluded, }); interval.data = defined(dataCallback) ? dataCallback(interval, result.length) : result.length; result.addInterval(interval); } for (var i = 0; i < length - 1; ++i) { var startDate = julianDates[i]; var endDate = julianDates[i + 1]; interval = new TimeInterval({ start: startDate, stop: endDate, isStartIncluded: result.length === startIndex ? isStartIncluded : true, isStopIncluded: i === length - 2 ? isStopIncluded : false, }); interval.data = defined(dataCallback) ? dataCallback(interval, result.length) : result.length; result.addInterval(interval); startDate = endDate; } if (trailingInterval) { interval = new TimeInterval({ start: julianDates[length - 1], stop: Iso8601.MAXIMUM_VALUE, isStartIncluded: !isStopIncluded, isStopIncluded: true, }); interval.data = defined(dataCallback) ? dataCallback(interval, result.length) : result.length; result.addInterval(interval); } return result; }; var scratchGregorianDate = new GregorianDate(); var monthLengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; /** * Adds duration represented as a GregorianDate to a JulianDate * * @param {JulianDate} julianDate The date. * @param {GregorianDate} duration An duration represented as a GregorianDate. * @param {JulianDate} result An existing instance to use for the result. * @returns {JulianDate} The modified result parameter. * * @private */ function addToDate(julianDate, duration, result) { if (!defined(result)) { result = new JulianDate(); } JulianDate.toGregorianDate(julianDate, scratchGregorianDate); var millisecond = scratchGregorianDate.millisecond + duration.millisecond; var second = scratchGregorianDate.second + duration.second; var minute = scratchGregorianDate.minute + duration.minute; var hour = scratchGregorianDate.hour + duration.hour; var day = scratchGregorianDate.day + duration.day; var month = scratchGregorianDate.month + duration.month; var year = scratchGregorianDate.year + duration.year; if (millisecond >= 1000) { second += Math.floor(millisecond / 1000); millisecond = millisecond % 1000; } if (second >= 60) { minute += Math.floor(second / 60); second = second % 60; } if (minute >= 60) { hour += Math.floor(minute / 60); minute = minute % 60; } if (hour >= 24) { day += Math.floor(hour / 24); hour = hour % 24; } // If days is greater than the month's length we need to remove those number of days, // readjust month and year and repeat until days is less than the month's length. monthLengths[2] = isLeapYear(year) ? 29 : 28; while (day > monthLengths[month] || month >= 13) { if (day > monthLengths[month]) { day -= monthLengths[month]; ++month; } if (month >= 13) { --month; year += Math.floor(month / 12); month = month % 12; ++month; } monthLengths[2] = isLeapYear(year) ? 29 : 28; } scratchGregorianDate.millisecond = millisecond; scratchGregorianDate.second = second; scratchGregorianDate.minute = minute; scratchGregorianDate.hour = hour; scratchGregorianDate.day = day; scratchGregorianDate.month = month; scratchGregorianDate.year = year; return JulianDate.fromGregorianDate(scratchGregorianDate, result); } var scratchJulianDate$2 = new JulianDate(); var durationRegex = /P(?:([\d.,]+)Y)?(?:([\d.,]+)M)?(?:([\d.,]+)W)?(?:([\d.,]+)D)?(?:T(?:([\d.,]+)H)?(?:([\d.,]+)M)?(?:([\d.,]+)S)?)?/; /** * Parses ISO8601 duration string * * @param {String} iso8601 An ISO 8601 duration. * @param {GregorianDate} result An existing instance to use for the result. * @returns {Boolean} True is parsing succeeded, false otherwise * * @private */ function parseDuration(iso8601, result) { if (!defined(iso8601) || iso8601.length === 0) { return false; } // Reset object result.year = 0; result.month = 0; result.day = 0; result.hour = 0; result.minute = 0; result.second = 0; result.millisecond = 0; if (iso8601[0] === "P") { var matches = iso8601.match(durationRegex); if (!defined(matches)) { return false; } if (defined(matches[1])) { // Years result.year = Number(matches[1].replace(",", ".")); } if (defined(matches[2])) { // Months result.month = Number(matches[2].replace(",", ".")); } if (defined(matches[3])) { // Weeks result.day = Number(matches[3].replace(",", ".")) * 7; } if (defined(matches[4])) { // Days result.day += Number(matches[4].replace(",", ".")); } if (defined(matches[5])) { // Hours result.hour = Number(matches[5].replace(",", ".")); } if (defined(matches[6])) { // Weeks result.minute = Number(matches[6].replace(",", ".")); } if (defined(matches[7])) { // Seconds var seconds = Number(matches[7].replace(",", ".")); result.second = Math.floor(seconds); result.millisecond = (seconds % 1) * 1000; } } else { // They can technically specify the duration as a normal date with some caveats. Try our best to load it. if (iso8601[iso8601.length - 1] !== "Z") { // It's not a date, its a duration, so it always has to be UTC iso8601 += "Z"; } JulianDate.toGregorianDate( JulianDate.fromIso8601(iso8601, scratchJulianDate$2), result ); } // A duration of 0 will cause an infinite loop, so just make sure something is non-zero return ( result.year || result.month || result.day || result.hour || result.minute || result.second || result.millisecond ); } var scratchDuration = new GregorianDate(); /** * Creates a new instance from an {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} time interval (start/end/duration). * * @param {Object} options Object with the following properties: * @param {String} options.iso8601 An ISO 8601 interval. * @param {Boolean} [options.isStartIncluded=true] true if start time is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if stop time is included in the interval, false otherwise. * @param {Boolean} [options.leadingInterval=false] true if you want to add a interval from Iso8601.MINIMUM_VALUE to start time, false otherwise. * @param {Boolean} [options.trailingInterval=false] true if you want to add a interval from stop time to Iso8601.MAXIMUM_VALUE, false otherwise. * @param {Function} [options.dataCallback] A function that will be return the data that is called with each interval before it is added to the collection. If unspecified, the data will be the index in the collection. * @param {TimeIntervalCollection} [result] An existing instance to use for the result. * @returns {TimeIntervalCollection} The modified result parameter or a new instance if none was provided. */ TimeIntervalCollection.fromIso8601 = function (options, result) { //>>includeStart('debug', pragmas.debug); if (!defined(options)) { throw new DeveloperError("options is required."); } if (!defined(options.iso8601)) { throw new DeveloperError("options.iso8601 is required."); } //>>includeEnd('debug'); var dates = options.iso8601.split("/"); var start = JulianDate.fromIso8601(dates[0]); var stop = JulianDate.fromIso8601(dates[1]); var julianDates = []; if (!parseDuration(dates[2], scratchDuration)) { julianDates.push(start, stop); } else { var date = JulianDate.clone(start); julianDates.push(date); while (JulianDate.compare(date, stop) < 0) { date = addToDate(date, scratchDuration); var afterStop = JulianDate.compare(stop, date) <= 0; if (afterStop) { JulianDate.clone(stop, date); } julianDates.push(date); } } return TimeIntervalCollection.fromJulianDateArray( { julianDates: julianDates, isStartIncluded: options.isStartIncluded, isStopIncluded: options.isStopIncluded, leadingInterval: options.leadingInterval, trailingInterval: options.trailingInterval, dataCallback: options.dataCallback, }, result ); }; /** * Creates a new instance from a {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} date array. * * @param {Object} options Object with the following properties: * @param {String[]} options.iso8601Dates An array of ISO 8601 dates. * @param {Boolean} [options.isStartIncluded=true] true if start time is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if stop time is included in the interval, false otherwise. * @param {Boolean} [options.leadingInterval=false] true if you want to add a interval from Iso8601.MINIMUM_VALUE to start time, false otherwise. * @param {Boolean} [options.trailingInterval=false] true if you want to add a interval from stop time to Iso8601.MAXIMUM_VALUE, false otherwise. * @param {Function} [options.dataCallback] A function that will be return the data that is called with each interval before it is added to the collection. If unspecified, the data will be the index in the collection. * @param {TimeIntervalCollection} [result] An existing instance to use for the result. * @returns {TimeIntervalCollection} The modified result parameter or a new instance if none was provided. */ TimeIntervalCollection.fromIso8601DateArray = function (options, result) { //>>includeStart('debug', pragmas.debug); if (!defined(options)) { throw new DeveloperError("options is required."); } if (!defined(options.iso8601Dates)) { throw new DeveloperError("options.iso8601Dates is required."); } //>>includeEnd('debug'); return TimeIntervalCollection.fromJulianDateArray( { julianDates: options.iso8601Dates.map(function (date) { return JulianDate.fromIso8601(date); }), isStartIncluded: options.isStartIncluded, isStopIncluded: options.isStopIncluded, leadingInterval: options.leadingInterval, trailingInterval: options.trailingInterval, dataCallback: options.dataCallback, }, result ); }; /** * Creates a new instance from a {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} duration array. * * @param {Object} options Object with the following properties: * @param {JulianDate} options.epoch An date that the durations are relative to. * @param {String} options.iso8601Durations An array of ISO 8601 durations. * @param {Boolean} [options.relativeToPrevious=false] true if durations are relative to previous date, false if always relative to the epoch. * @param {Boolean} [options.isStartIncluded=true] true if start time is included in the interval, false otherwise. * @param {Boolean} [options.isStopIncluded=true] true if stop time is included in the interval, false otherwise. * @param {Boolean} [options.leadingInterval=false] true if you want to add a interval from Iso8601.MINIMUM_VALUE to start time, false otherwise. * @param {Boolean} [options.trailingInterval=false] true if you want to add a interval from stop time to Iso8601.MAXIMUM_VALUE, false otherwise. * @param {Function} [options.dataCallback] A function that will be return the data that is called with each interval before it is added to the collection. If unspecified, the data will be the index in the collection. * @param {TimeIntervalCollection} [result] An existing instance to use for the result. * @returns {TimeIntervalCollection} The modified result parameter or a new instance if none was provided. */ TimeIntervalCollection.fromIso8601DurationArray = function (options, result) { //>>includeStart('debug', pragmas.debug); if (!defined(options)) { throw new DeveloperError("options is required."); } if (!defined(options.epoch)) { throw new DeveloperError("options.epoch is required."); } if (!defined(options.iso8601Durations)) { throw new DeveloperError("options.iso8601Durations is required."); } //>>includeEnd('debug'); var epoch = options.epoch; var iso8601Durations = options.iso8601Durations; var relativeToPrevious = defaultValue(options.relativeToPrevious, false); var julianDates = []; var date, previousDate; var length = iso8601Durations.length; for (var i = 0; i < length; ++i) { // Allow a duration of 0 on the first iteration, because then it is just the epoch if (parseDuration(iso8601Durations[i], scratchDuration) || i === 0) { if (relativeToPrevious && defined(previousDate)) { date = addToDate(previousDate, scratchDuration); } else { date = addToDate(epoch, scratchDuration); } julianDates.push(date); previousDate = date; } } return TimeIntervalCollection.fromJulianDateArray( { julianDates: julianDates, isStartIncluded: options.isStartIncluded, isStopIncluded: options.isStopIncluded, leadingInterval: options.leadingInterval, trailingInterval: options.trailingInterval, dataCallback: options.dataCallback, }, result ); }; var defaultScale$3 = new Cartesian3(1.0, 1.0, 1.0); var defaultTranslation = Cartesian3.ZERO; var defaultRotation$1 = Quaternion.IDENTITY; /** * An affine transformation defined by a translation, rotation, and scale. * @alias TranslationRotationScale * @constructor * * @param {Cartesian3} [translation=Cartesian3.ZERO] A {@link Cartesian3} specifying the (x, y, z) translation to apply to the node. * @param {Quaternion} [rotation=Quaternion.IDENTITY] A {@link Quaternion} specifying the (x, y, z, w) rotation to apply to the node. * @param {Cartesian3} [scale=new Cartesian3(1.0, 1.0, 1.0)] A {@link Cartesian3} specifying the (x, y, z) scaling to apply to the node. */ function TranslationRotationScale(translation, rotation, scale) { /** * Gets or sets the (x, y, z) translation to apply to the node. * @type {Cartesian3} * @default Cartesian3.ZERO */ this.translation = Cartesian3.clone( defaultValue(translation, defaultTranslation) ); /** * Gets or sets the (x, y, z, w) rotation to apply to the node. * @type {Quaternion} * @default Quaternion.IDENTITY */ this.rotation = Quaternion.clone(defaultValue(rotation, defaultRotation$1)); /** * Gets or sets the (x, y, z) scaling to apply to the node. * @type {Cartesian3} * @default new Cartesian3(1.0, 1.0, 1.0) */ this.scale = Cartesian3.clone(defaultValue(scale, defaultScale$3)); } /** * Compares this instance against the provided instance and returns * true if they are equal, false otherwise. * * @param {TranslationRotationScale} [right] The right hand side TranslationRotationScale. * @returns {Boolean} true if they are equal, false otherwise. */ TranslationRotationScale.prototype.equals = function (right) { return ( this === right || (defined(right) && Cartesian3.equals(this.translation, right.translation) && Quaternion.equals(this.rotation, right.rotation) && Cartesian3.equals(this.scale, right.scale)) ); }; var context2DsByWidthAndHeight = {}; /** * Extract a pixel array from a loaded image. Draws the image * into a canvas so it can read the pixels back. * * @function getImagePixels * * @param {HTMLImageElement} image The image to extract pixels from. * @param {Number} width The width of the image. If not defined, then image.width is assigned. * @param {Number} height The height of the image. If not defined, then image.height is assigned. * @returns {ImageData} The pixels of the image. */ function getImagePixels(image, width, height) { if (!defined(width)) { width = image.width; } if (!defined(height)) { height = image.height; } var context2DsByHeight = context2DsByWidthAndHeight[width]; if (!defined(context2DsByHeight)) { context2DsByHeight = {}; context2DsByWidthAndHeight[width] = context2DsByHeight; } var context2d = context2DsByHeight[height]; if (!defined(context2d)) { var canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; context2d = canvas.getContext("2d"); context2d.globalCompositeOperation = "copy"; context2DsByHeight[height] = context2d; } context2d.drawImage(image, 0, 0, width, height); return context2d.getImageData(0, 0, width, height).data; } function DataRectangle(rectangle, maxLevel) { this.rectangle = rectangle; this.maxLevel = maxLevel; } /** * A {@link TerrainProvider} that produces terrain geometry by tessellating height maps * retrieved from a {@link http://vr-theworld.com/|VT MÄK VR-TheWorld server}. * * @alias VRTheWorldTerrainProvider * @constructor * * @param {Object} options Object with the following properties: * @param {Resource|String} options.url The URL of the VR-TheWorld TileMap. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid. If this parameter is not * specified, the WGS84 ellipsoid is used. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * * * @example * var terrainProvider = new Cesium.VRTheWorldTerrainProvider({ * url : 'https://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/' * }); * viewer.terrainProvider = terrainProvider; * * @see TerrainProvider */ function VRTheWorldTerrainProvider(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!defined(options.url)) { throw new DeveloperError("options.url is required."); } //>>includeEnd('debug'); var resource = Resource.createIfNeeded(options.url); this._resource = resource; this._errorEvent = new Event(); this._ready = false; this._readyPromise = when.defer(); this._terrainDataStructure = { heightScale: 1.0 / 1000.0, heightOffset: -1000.0, elementsPerHeight: 3, stride: 4, elementMultiplier: 256.0, isBigEndian: true, lowestEncodedHeight: 0, highestEncodedHeight: 256 * 256 * 256 - 1, }; var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } this._credit = credit; this._tilingScheme = undefined; this._rectangles = []; var that = this; var metadataError; var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); function metadataSuccess(xml) { var srs = xml.getElementsByTagName("SRS")[0].textContent; if (srs === "EPSG:4326") { that._tilingScheme = new GeographicTilingScheme({ ellipsoid: ellipsoid }); } else { metadataFailure("SRS " + srs + " is not supported."); return; } var tileFormat = xml.getElementsByTagName("TileFormat")[0]; that._heightmapWidth = parseInt(tileFormat.getAttribute("width"), 10); that._heightmapHeight = parseInt(tileFormat.getAttribute("height"), 10); that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap( ellipsoid, Math.min(that._heightmapWidth, that._heightmapHeight), that._tilingScheme.getNumberOfXTilesAtLevel(0) ); var dataRectangles = xml.getElementsByTagName("DataExtent"); for (var i = 0; i < dataRectangles.length; ++i) { var dataRectangle = dataRectangles[i]; var west = CesiumMath.toRadians( parseFloat(dataRectangle.getAttribute("minx")) ); var south = CesiumMath.toRadians( parseFloat(dataRectangle.getAttribute("miny")) ); var east = CesiumMath.toRadians( parseFloat(dataRectangle.getAttribute("maxx")) ); var north = CesiumMath.toRadians( parseFloat(dataRectangle.getAttribute("maxy")) ); var maxLevel = parseInt(dataRectangle.getAttribute("maxlevel"), 10); that._rectangles.push( new DataRectangle(new Rectangle(west, south, east, north), maxLevel) ); } that._ready = true; that._readyPromise.resolve(true); } function metadataFailure(e) { var message = defaultValue( e, "An error occurred while accessing " + that._resource.url + "." ); metadataError = TileProviderError.handleError( metadataError, that, that._errorEvent, message, undefined, undefined, undefined, requestMetadata ); } function requestMetadata() { when(that._resource.fetchXML(), metadataSuccess, metadataFailure); } requestMetadata(); } Object.defineProperties(VRTheWorldTerrainProvider.prototype, { /** * Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing * to the event, you will be notified of the error and can potentially recover from it. Event listeners * are passed an instance of {@link TileProviderError}. * @memberof VRTheWorldTerrainProvider.prototype * @type {Event} */ errorEvent: { get: function () { return this._errorEvent; }, }, /** * Gets the credit to display when this terrain provider is active. Typically this is used to credit * the source of the terrain. This function should not be called before {@link VRTheWorldTerrainProvider#ready} returns true. * @memberof VRTheWorldTerrainProvider.prototype * @type {Credit} */ credit: { get: function () { return this._credit; }, }, /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link VRTheWorldTerrainProvider#ready} returns true. * @memberof VRTheWorldTerrainProvider.prototype * @type {GeographicTilingScheme} */ tilingScheme: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "requestTileGeometry must not be called before ready returns true." ); } //>>includeEnd('debug'); return this._tilingScheme; }, }, /** * Gets a value indicating whether or not the provider is ready for use. * @memberof VRTheWorldTerrainProvider.prototype * @type {Boolean} */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves to true when the provider is ready for use. * @memberof VRTheWorldTerrainProvider.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * Gets a value indicating whether or not the provider includes a water mask. The water mask * indicates which areas of the globe are water rather than land, so they can be rendered * as a reflective surface with animated waves. This function should not be * called before {@link VRTheWorldTerrainProvider#ready} returns true. * @memberof VRTheWorldTerrainProvider.prototype * @type {Boolean} */ hasWaterMask: { get: function () { return false; }, }, /** * Gets a value indicating whether or not the requested tiles include vertex normals. * This function should not be called before {@link VRTheWorldTerrainProvider#ready} returns true. * @memberof VRTheWorldTerrainProvider.prototype * @type {Boolean} */ hasVertexNormals: { get: function () { return false; }, }, /** * Gets an object that can be used to determine availability of terrain from this provider, such as * at points and in rectangles. This function should not be called before * {@link TerrainProvider#ready} returns true. This property may be undefined if availability * information is not available. * @memberof VRTheWorldTerrainProvider.prototype * @type {TileAvailability} */ availability: { get: function () { return undefined; }, }, }); /** * Requests the geometry for a given tile. This function should not be called before * {@link VRTheWorldTerrainProvider#ready} returns true. The result includes terrain * data and indicates that all child tiles are available. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. */ VRTheWorldTerrainProvider.prototype.requestTileGeometry = function ( x, y, level, request ) { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "requestTileGeometry must not be called before ready returns true." ); } //>>includeEnd('debug'); var yTiles = this._tilingScheme.getNumberOfYTilesAtLevel(level); var resource = this._resource.getDerivedResource({ url: level + "/" + x + "/" + (yTiles - y - 1) + ".tif", queryParameters: { cesium: true, }, request: request, }); var promise = resource.fetchImage({ preferImageBitmap: true, }); if (!defined(promise)) { return undefined; } var that = this; return when(promise).then(function (image) { return new HeightmapTerrainData({ buffer: getImagePixels(image), width: that._heightmapWidth, height: that._heightmapHeight, childTileMask: getChildMask(that, x, y, level), structure: that._terrainDataStructure, }); }); }; /** * Gets the maximum geometric error allowed in a tile at a given level. * * @param {Number} level The tile level for which to get the maximum geometric error. * @returns {Number} The maximum geometric error. */ VRTheWorldTerrainProvider.prototype.getLevelMaximumGeometricError = function ( level ) { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "requestTileGeometry must not be called before ready returns true." ); } //>>includeEnd('debug'); return this._levelZeroMaximumGeometricError / (1 << level); }; var rectangleScratch$2 = new Rectangle(); function getChildMask(provider, x, y, level) { var tilingScheme = provider._tilingScheme; var rectangles = provider._rectangles; var parentRectangle = tilingScheme.tileXYToRectangle(x, y, level); var childMask = 0; for (var i = 0; i < rectangles.length && childMask !== 15; ++i) { var rectangle = rectangles[i]; if (rectangle.maxLevel <= level) { continue; } var testRectangle = rectangle.rectangle; var intersection = Rectangle.intersection( testRectangle, parentRectangle, rectangleScratch$2 ); if (defined(intersection)) { // Parent tile is inside this rectangle, so at least one child is, too. if ( isTileInRectangle(tilingScheme, testRectangle, x * 2, y * 2, level + 1) ) { childMask |= 4; // northwest } if ( isTileInRectangle( tilingScheme, testRectangle, x * 2 + 1, y * 2, level + 1 ) ) { childMask |= 8; // northeast } if ( isTileInRectangle( tilingScheme, testRectangle, x * 2, y * 2 + 1, level + 1 ) ) { childMask |= 1; // southwest } if ( isTileInRectangle( tilingScheme, testRectangle, x * 2 + 1, y * 2 + 1, level + 1 ) ) { childMask |= 2; // southeast } } } return childMask; } function isTileInRectangle(tilingScheme, rectangle, x, y, level) { var tileRectangle = tilingScheme.tileXYToRectangle(x, y, level); return defined( Rectangle.intersection(tileRectangle, rectangle, rectangleScratch$2) ); } /** * Determines whether data for a tile is available to be loaded. * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {Boolean} Undefined if not supported, otherwise true or false. */ VRTheWorldTerrainProvider.prototype.getTileDataAvailable = function ( x, y, level ) { return undefined; }; /** * Makes sure we load availability data for a tile * * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. * @returns {undefined|Promise} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded */ VRTheWorldTerrainProvider.prototype.loadTileDataAvailability = function ( x, y, level ) { return undefined; }; /** * Synchronizes a video element with a simulation clock. * * @alias VideoSynchronizer * @constructor * * @param {Object} [options] Object with the following properties: * @param {Clock} [options.clock] The clock instance used to drive the video. * @param {HTMLVideoElement} [options.element] The video element to be synchronized. * @param {JulianDate} [options.epoch=Iso8601.MINIMUM_VALUE] The simulation time that marks the start of the video. * @param {Number} [options.tolerance=1.0] The maximum amount of time, in seconds, that the clock and video can diverge. * * @demo {@link https://sandcastle.cesium.com/index.html?src=Video.html|Video Material Demo} */ function VideoSynchronizer(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._clock = undefined; this._element = undefined; this._clockSubscription = undefined; this._seekFunction = undefined; this._lastPlaybackRate = undefined; this.clock = options.clock; this.element = options.element; /** * Gets or sets the simulation time that marks the start of the video. * @type {JulianDate} * @default Iso8601.MINIMUM_VALUE */ this.epoch = defaultValue(options.epoch, Iso8601.MINIMUM_VALUE); /** * Gets or sets the amount of time in seconds the video's currentTime * and the clock's currentTime can diverge before a video seek is performed. * Lower values make the synchronization more accurate but video * performance might suffer. Higher values provide better performance * but at the cost of accuracy. * @type {Number} * @default 1.0 */ this.tolerance = defaultValue(options.tolerance, 1.0); this._seeking = false; this._seekFunction = undefined; this._firstTickAfterSeek = false; } Object.defineProperties(VideoSynchronizer.prototype, { /** * Gets or sets the clock used to drive the video element. * * @memberof VideoSynchronizer.prototype * @type {Clock} */ clock: { get: function () { return this._clock; }, set: function (value) { var oldValue = this._clock; if (oldValue === value) { return; } if (defined(oldValue)) { this._clockSubscription(); this._clockSubscription = undefined; } if (defined(value)) { this._clockSubscription = value.onTick.addEventListener( VideoSynchronizer.prototype._onTick, this ); } this._clock = value; }, }, /** * Gets or sets the video element to synchronize. * * @memberof VideoSynchronizer.prototype * @type {HTMLVideoElement} */ element: { get: function () { return this._element; }, set: function (value) { var oldValue = this._element; if (oldValue === value) { return; } if (defined(oldValue)) { oldValue.removeEventListener("seeked", this._seekFunction, false); } if (defined(value)) { this._seeking = false; this._seekFunction = createSeekFunction(this); value.addEventListener("seeked", this._seekFunction, false); } this._element = value; this._seeking = false; this._firstTickAfterSeek = false; }, }, }); /** * Destroys and resources used by the object. Once an object is destroyed, it should not be used. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ VideoSynchronizer.prototype.destroy = function () { this.element = undefined; this.clock = undefined; return destroyObject(this); }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ VideoSynchronizer.prototype.isDestroyed = function () { return false; }; VideoSynchronizer.prototype._trySetPlaybackRate = function (clock) { if (this._lastPlaybackRate === clock.multiplier) { return; } var element = this._element; try { element.playbackRate = clock.multiplier; } catch (error) { // Seek manually for unsupported playbackRates. element.playbackRate = 0.0; } this._lastPlaybackRate = clock.multiplier; }; VideoSynchronizer.prototype._onTick = function (clock) { var element = this._element; if (!defined(element) || element.readyState < 2) { return; } var paused = element.paused; var shouldAnimate = clock.shouldAnimate; if (shouldAnimate === paused) { if (shouldAnimate) { element.play(); } else { element.pause(); } } //We need to avoid constant seeking or the video will //never contain a complete frame for us to render. //So don't do anything if we're seeing or on the first //tick after a seek (the latter of which allows the frame //to actually be rendered. if (this._seeking || this._firstTickAfterSeek) { this._firstTickAfterSeek = false; return; } this._trySetPlaybackRate(clock); var clockTime = clock.currentTime; var epoch = defaultValue(this.epoch, Iso8601.MINIMUM_VALUE); var videoTime = JulianDate.secondsDifference(clockTime, epoch); var duration = element.duration; var desiredTime; var currentTime = element.currentTime; if (element.loop) { videoTime = videoTime % duration; if (videoTime < 0.0) { videoTime = duration - videoTime; } desiredTime = videoTime; } else if (videoTime > duration) { desiredTime = duration; } else if (videoTime < 0.0) { desiredTime = 0.0; } else { desiredTime = videoTime; } //If the playing video's time and the scene's clock time //ever drift too far apart, we want to set the video to match var tolerance = shouldAnimate ? defaultValue(this.tolerance, 1.0) : 0.001; if (Math.abs(desiredTime - currentTime) > tolerance) { this._seeking = true; element.currentTime = desiredTime; } }; function createSeekFunction(that) { return function () { that._seeking = false; that._firstTickAfterSeek = true; }; } /** * @private */ var WallGeometryLibrary = {}; function latLonEquals(c0, c1) { return ( CesiumMath.equalsEpsilon(c0.latitude, c1.latitude, CesiumMath.EPSILON10) && CesiumMath.equalsEpsilon(c0.longitude, c1.longitude, CesiumMath.EPSILON10) ); } var scratchCartographic1 = new Cartographic(); var scratchCartographic2 = new Cartographic(); function removeDuplicates$1(ellipsoid, positions, topHeights, bottomHeights) { positions = arrayRemoveDuplicates(positions, Cartesian3.equalsEpsilon); var length = positions.length; if (length < 2) { return; } var hasBottomHeights = defined(bottomHeights); var hasTopHeights = defined(topHeights); var cleanedPositions = new Array(length); var cleanedTopHeights = new Array(length); var cleanedBottomHeights = new Array(length); var v0 = positions[0]; cleanedPositions[0] = v0; var c0 = ellipsoid.cartesianToCartographic(v0, scratchCartographic1); if (hasTopHeights) { c0.height = topHeights[0]; } cleanedTopHeights[0] = c0.height; if (hasBottomHeights) { cleanedBottomHeights[0] = bottomHeights[0]; } else { cleanedBottomHeights[0] = 0.0; } var startTopHeight = cleanedTopHeights[0]; var startBottomHeight = cleanedBottomHeights[0]; var hasAllSameHeights = startTopHeight === startBottomHeight; var index = 1; for (var i = 1; i < length; ++i) { var v1 = positions[i]; var c1 = ellipsoid.cartesianToCartographic(v1, scratchCartographic2); if (hasTopHeights) { c1.height = topHeights[i]; } hasAllSameHeights = hasAllSameHeights && c1.height === 0; if (!latLonEquals(c0, c1)) { cleanedPositions[index] = v1; // Shallow copy! cleanedTopHeights[index] = c1.height; if (hasBottomHeights) { cleanedBottomHeights[index] = bottomHeights[i]; } else { cleanedBottomHeights[index] = 0.0; } hasAllSameHeights = hasAllSameHeights && cleanedTopHeights[index] === cleanedBottomHeights[index]; Cartographic.clone(c1, c0); ++index; } else if (c0.height < c1.height) { // two adjacent positions are the same, so use whichever has the greater height cleanedTopHeights[index - 1] = c1.height; } } if (hasAllSameHeights || index < 2) { return; } cleanedPositions.length = index; cleanedTopHeights.length = index; cleanedBottomHeights.length = index; return { positions: cleanedPositions, topHeights: cleanedTopHeights, bottomHeights: cleanedBottomHeights, }; } var positionsArrayScratch = new Array(2); var heightsArrayScratch = new Array(2); var generateArcOptionsScratch = { positions: undefined, height: undefined, granularity: undefined, ellipsoid: undefined, }; /** * @private */ WallGeometryLibrary.computePositions = function ( ellipsoid, wallPositions, maximumHeights, minimumHeights, granularity, duplicateCorners ) { var o = removeDuplicates$1( ellipsoid, wallPositions, maximumHeights, minimumHeights ); if (!defined(o)) { return; } wallPositions = o.positions; maximumHeights = o.topHeights; minimumHeights = o.bottomHeights; var length = wallPositions.length; var numCorners = length - 2; var topPositions; var bottomPositions; var minDistance = CesiumMath.chordLength( granularity, ellipsoid.maximumRadius ); var generateArcOptions = generateArcOptionsScratch; generateArcOptions.minDistance = minDistance; generateArcOptions.ellipsoid = ellipsoid; if (duplicateCorners) { var count = 0; var i; for (i = 0; i < length - 1; i++) { count += PolylinePipeline.numberOfPoints( wallPositions[i], wallPositions[i + 1], minDistance ) + 1; } topPositions = new Float64Array(count * 3); bottomPositions = new Float64Array(count * 3); var generateArcPositions = positionsArrayScratch; var generateArcHeights = heightsArrayScratch; generateArcOptions.positions = generateArcPositions; generateArcOptions.height = generateArcHeights; var offset = 0; for (i = 0; i < length - 1; i++) { generateArcPositions[0] = wallPositions[i]; generateArcPositions[1] = wallPositions[i + 1]; generateArcHeights[0] = maximumHeights[i]; generateArcHeights[1] = maximumHeights[i + 1]; var pos = PolylinePipeline.generateArc(generateArcOptions); topPositions.set(pos, offset); generateArcHeights[0] = minimumHeights[i]; generateArcHeights[1] = minimumHeights[i + 1]; bottomPositions.set( PolylinePipeline.generateArc(generateArcOptions), offset ); offset += pos.length; } } else { generateArcOptions.positions = wallPositions; generateArcOptions.height = maximumHeights; topPositions = new Float64Array( PolylinePipeline.generateArc(generateArcOptions) ); generateArcOptions.height = minimumHeights; bottomPositions = new Float64Array( PolylinePipeline.generateArc(generateArcOptions) ); } return { bottomPositions: bottomPositions, topPositions: topPositions, numCorners: numCorners, }; }; var scratchCartesian3Position1$1 = new Cartesian3(); var scratchCartesian3Position2$1 = new Cartesian3(); var scratchCartesian3Position4 = new Cartesian3(); var scratchCartesian3Position5 = new Cartesian3(); var scratchBitangent = new Cartesian3(); var scratchTangent = new Cartesian3(); var scratchNormal$1 = new Cartesian3(); /** * A description of a wall, which is similar to a KML line string. A wall is defined by a series of points, * which extrude down to the ground. Optionally, they can extrude downwards to a specified height. * * @alias WallGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall. * @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.maximumHeights] An array parallel to positions that give the maximum height of the * wall at positions. If undefined, the height of each position in used. * @param {Number[]} [options.minimumHeights] An array parallel to positions that give the minimum height of the * wall at positions. If undefined, the height at each position is 0.0. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * * @exception {DeveloperError} positions length must be greater than or equal to 2. * @exception {DeveloperError} positions and maximumHeights must have the same length. * @exception {DeveloperError} positions and minimumHeights must have the same length. * * @see WallGeometry#createGeometry * @see WallGeometry#fromConstantHeight * * @demo {@link https://sandcastle.cesium.com/index.html?src=Wall.html|Cesium Sandcastle Wall Demo} * * @example * // create a wall that spans from ground level to 10000 meters * var wall = new Cesium.WallGeometry({ * positions : Cesium.Cartesian3.fromDegreesArrayHeights([ * 19.0, 47.0, 10000.0, * 19.0, 48.0, 10000.0, * 20.0, 48.0, 10000.0, * 20.0, 47.0, 10000.0, * 19.0, 47.0, 10000.0 * ]) * }); * var geometry = Cesium.WallGeometry.createGeometry(wall); */ function WallGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var wallPositions = options.positions; var maximumHeights = options.maximumHeights; var minimumHeights = options.minimumHeights; //>>includeStart('debug', pragmas.debug); if (!defined(wallPositions)) { throw new DeveloperError("options.positions is required."); } if ( defined(maximumHeights) && maximumHeights.length !== wallPositions.length ) { throw new DeveloperError( "options.positions and options.maximumHeights must have the same length." ); } if ( defined(minimumHeights) && minimumHeights.length !== wallPositions.length ) { throw new DeveloperError( "options.positions and options.minimumHeights must have the same length." ); } //>>includeEnd('debug'); var vertexFormat = defaultValue(options.vertexFormat, VertexFormat.DEFAULT); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._positions = wallPositions; this._minimumHeights = minimumHeights; this._maximumHeights = maximumHeights; this._vertexFormat = VertexFormat.clone(vertexFormat); this._granularity = granularity; this._ellipsoid = Ellipsoid.clone(ellipsoid); this._workerName = "createWallGeometry"; var numComponents = 1 + wallPositions.length * Cartesian3.packedLength + 2; if (defined(minimumHeights)) { numComponents += minimumHeights.length; } if (defined(maximumHeights)) { numComponents += maximumHeights.length; } /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + VertexFormat.packedLength + 1; } /** * Stores the provided instance into the provided array. * * @param {WallGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ WallGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var minimumHeights = value._minimumHeights; length = defined(minimumHeights) ? minimumHeights.length : 0; array[startingIndex++] = length; if (defined(minimumHeights)) { for (i = 0; i < length; ++i) { array[startingIndex++] = minimumHeights[i]; } } var maximumHeights = value._maximumHeights; length = defined(maximumHeights) ? maximumHeights.length : 0; array[startingIndex++] = length; if (defined(maximumHeights)) { for (i = 0; i < length; ++i) { array[startingIndex++] = maximumHeights[i]; } } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; VertexFormat.pack(value._vertexFormat, array, startingIndex); startingIndex += VertexFormat.packedLength; array[startingIndex] = value._granularity; return array; }; var scratchEllipsoid$2 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat = new VertexFormat(); var scratchOptions$1 = { positions: undefined, minimumHeights: undefined, maximumHeights: undefined, ellipsoid: scratchEllipsoid$2, vertexFormat: scratchVertexFormat, granularity: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {WallGeometry} [result] The object into which to store the result. * @returns {WallGeometry} The modified result parameter or a new WallGeometry instance if one was not provided. */ WallGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var minimumHeights; if (length > 0) { minimumHeights = new Array(length); for (i = 0; i < length; ++i) { minimumHeights[i] = array[startingIndex++]; } } length = array[startingIndex++]; var maximumHeights; if (length > 0) { maximumHeights = new Array(length); for (i = 0; i < length; ++i) { maximumHeights[i] = array[startingIndex++]; } } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$2); startingIndex += Ellipsoid.packedLength; var vertexFormat = VertexFormat.unpack( array, startingIndex, scratchVertexFormat ); startingIndex += VertexFormat.packedLength; var granularity = array[startingIndex]; if (!defined(result)) { scratchOptions$1.positions = positions; scratchOptions$1.minimumHeights = minimumHeights; scratchOptions$1.maximumHeights = maximumHeights; scratchOptions$1.granularity = granularity; return new WallGeometry(scratchOptions$1); } result._positions = positions; result._minimumHeights = minimumHeights; result._maximumHeights = maximumHeights; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._vertexFormat = VertexFormat.clone(vertexFormat, result._vertexFormat); result._granularity = granularity; return result; }; /** * A description of a wall, which is similar to a KML line string. A wall is defined by a series of points, * which extrude down to the ground. Optionally, they can extrude downwards to a specified height. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall. * @param {Number} [options.maximumHeight] A constant that defines the maximum height of the * wall at positions. If undefined, the height of each position in used. * @param {Number} [options.minimumHeight] A constant that defines the minimum height of the * wall at positions. If undefined, the height at each position is 0.0. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation * @param {VertexFormat} [options.vertexFormat=VertexFormat.DEFAULT] The vertex attributes to be computed. * @returns {WallGeometry} * * * @example * // create a wall that spans from 10000 meters to 20000 meters * var wall = Cesium.WallGeometry.fromConstantHeights({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 19.0, 47.0, * 19.0, 48.0, * 20.0, 48.0, * 20.0, 47.0, * 19.0, 47.0, * ]), * minimumHeight : 20000.0, * maximumHeight : 10000.0 * }); * var geometry = Cesium.WallGeometry.createGeometry(wall); * * @see WallGeometry#createGeometry */ WallGeometry.fromConstantHeights = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.positions is required."); } //>>includeEnd('debug'); var minHeights; var maxHeights; var min = options.minimumHeight; var max = options.maximumHeight; var doMin = defined(min); var doMax = defined(max); if (doMin || doMax) { var length = positions.length; minHeights = doMin ? new Array(length) : undefined; maxHeights = doMax ? new Array(length) : undefined; for (var i = 0; i < length; ++i) { if (doMin) { minHeights[i] = min; } if (doMax) { maxHeights[i] = max; } } } var newOptions = { positions: positions, maximumHeights: maxHeights, minimumHeights: minHeights, ellipsoid: options.ellipsoid, vertexFormat: options.vertexFormat, }; return new WallGeometry(newOptions); }; /** * Computes the geometric representation of a wall, including its vertices, indices, and a bounding sphere. * * @param {WallGeometry} wallGeometry A description of the wall. * @returns {Geometry|undefined} The computed vertices and indices. */ WallGeometry.createGeometry = function (wallGeometry) { var wallPositions = wallGeometry._positions; var minimumHeights = wallGeometry._minimumHeights; var maximumHeights = wallGeometry._maximumHeights; var vertexFormat = wallGeometry._vertexFormat; var granularity = wallGeometry._granularity; var ellipsoid = wallGeometry._ellipsoid; var pos = WallGeometryLibrary.computePositions( ellipsoid, wallPositions, maximumHeights, minimumHeights, granularity, true ); if (!defined(pos)) { return; } var bottomPositions = pos.bottomPositions; var topPositions = pos.topPositions; var numCorners = pos.numCorners; var length = topPositions.length; var size = length * 2; var positions = vertexFormat.position ? new Float64Array(size) : undefined; var normals = vertexFormat.normal ? new Float32Array(size) : undefined; var tangents = vertexFormat.tangent ? new Float32Array(size) : undefined; var bitangents = vertexFormat.bitangent ? new Float32Array(size) : undefined; var textureCoordinates = vertexFormat.st ? new Float32Array((size / 3) * 2) : undefined; var positionIndex = 0; var normalIndex = 0; var bitangentIndex = 0; var tangentIndex = 0; var stIndex = 0; // add lower and upper points one after the other, lower // points being even and upper points being odd var normal = scratchNormal$1; var tangent = scratchTangent; var bitangent = scratchBitangent; var recomputeNormal = true; length /= 3; var i; var s = 0; var ds = 1 / (length - numCorners - 1); for (i = 0; i < length; ++i) { var i3 = i * 3; var topPosition = Cartesian3.fromArray( topPositions, i3, scratchCartesian3Position1$1 ); var bottomPosition = Cartesian3.fromArray( bottomPositions, i3, scratchCartesian3Position2$1 ); if (vertexFormat.position) { // insert the lower point positions[positionIndex++] = bottomPosition.x; positions[positionIndex++] = bottomPosition.y; positions[positionIndex++] = bottomPosition.z; // insert the upper point positions[positionIndex++] = topPosition.x; positions[positionIndex++] = topPosition.y; positions[positionIndex++] = topPosition.z; } if (vertexFormat.st) { textureCoordinates[stIndex++] = s; textureCoordinates[stIndex++] = 0.0; textureCoordinates[stIndex++] = s; textureCoordinates[stIndex++] = 1.0; } if (vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent) { var nextTop = Cartesian3.clone( Cartesian3.ZERO, scratchCartesian3Position5 ); var groundPosition = Cartesian3.subtract( topPosition, ellipsoid.geodeticSurfaceNormal( topPosition, scratchCartesian3Position2$1 ), scratchCartesian3Position2$1 ); if (i + 1 < length) { nextTop = Cartesian3.fromArray( topPositions, i3 + 3, scratchCartesian3Position5 ); } if (recomputeNormal) { var scalednextPosition = Cartesian3.subtract( nextTop, topPosition, scratchCartesian3Position4 ); var scaledGroundPosition = Cartesian3.subtract( groundPosition, topPosition, scratchCartesian3Position1$1 ); normal = Cartesian3.normalize( Cartesian3.cross(scaledGroundPosition, scalednextPosition, normal), normal ); recomputeNormal = false; } if ( Cartesian3.equalsEpsilon(topPosition, nextTop, CesiumMath.EPSILON10) ) { recomputeNormal = true; } else { s += ds; if (vertexFormat.tangent) { tangent = Cartesian3.normalize( Cartesian3.subtract(nextTop, topPosition, tangent), tangent ); } if (vertexFormat.bitangent) { bitangent = Cartesian3.normalize( Cartesian3.cross(normal, tangent, bitangent), bitangent ); } } if (vertexFormat.normal) { normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; normals[normalIndex++] = normal.x; normals[normalIndex++] = normal.y; normals[normalIndex++] = normal.z; } if (vertexFormat.tangent) { tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; tangents[tangentIndex++] = tangent.x; tangents[tangentIndex++] = tangent.y; tangents[tangentIndex++] = tangent.z; } if (vertexFormat.bitangent) { bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; bitangents[bitangentIndex++] = bitangent.x; bitangents[bitangentIndex++] = bitangent.y; bitangents[bitangentIndex++] = bitangent.z; } } } var attributes = new GeometryAttributes(); if (vertexFormat.position) { attributes.position = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }); } if (vertexFormat.normal) { attributes.normal = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: normals, }); } if (vertexFormat.tangent) { attributes.tangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: tangents, }); } if (vertexFormat.bitangent) { attributes.bitangent = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, values: bitangents, }); } if (vertexFormat.st) { attributes.st = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, values: textureCoordinates, }); } // prepare the side walls, two triangles for each wall // // A (i+1) B (i+3) E // +--------+-------+ // | / | /| triangles: A C B // | / | / | B C D // | / | / | // | / | / | // | / | / | // | / | / | // +--------+-------+ // C (i) D (i+2) F // var numVertices = size / 3; size -= 6 * (numCorners + 1); var indices = IndexDatatype$1.createTypedArray(numVertices, size); var edgeIndex = 0; for (i = 0; i < numVertices - 2; i += 2) { var LL = i; var LR = i + 2; var pl = Cartesian3.fromArray( positions, LL * 3, scratchCartesian3Position1$1 ); var pr = Cartesian3.fromArray( positions, LR * 3, scratchCartesian3Position2$1 ); if (Cartesian3.equalsEpsilon(pl, pr, CesiumMath.EPSILON10)) { continue; } var UL = i + 1; var UR = i + 3; indices[edgeIndex++] = UL; indices[edgeIndex++] = LL; indices[edgeIndex++] = UR; indices[edgeIndex++] = UR; indices[edgeIndex++] = LL; indices[edgeIndex++] = LR; } return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.TRIANGLES, boundingSphere: new BoundingSphere.fromVertices(positions), }); }; var scratchCartesian3Position1 = new Cartesian3(); var scratchCartesian3Position2 = new Cartesian3(); /** * A description of a wall outline. A wall is defined by a series of points, * which extrude down to the ground. Optionally, they can extrude downwards to a specified height. * * @alias WallOutlineGeometry * @constructor * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall. * @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.maximumHeights] An array parallel to positions that give the maximum height of the * wall at positions. If undefined, the height of each position in used. * @param {Number[]} [options.minimumHeights] An array parallel to positions that give the minimum height of the * wall at positions. If undefined, the height at each position is 0.0. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation * * @exception {DeveloperError} positions length must be greater than or equal to 2. * @exception {DeveloperError} positions and maximumHeights must have the same length. * @exception {DeveloperError} positions and minimumHeights must have the same length. * * @see WallGeometry#createGeometry * @see WallGeometry#fromConstantHeight * * @example * // create a wall outline that spans from ground level to 10000 meters * var wall = new Cesium.WallOutlineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArrayHeights([ * 19.0, 47.0, 10000.0, * 19.0, 48.0, 10000.0, * 20.0, 48.0, 10000.0, * 20.0, 47.0, 10000.0, * 19.0, 47.0, 10000.0 * ]) * }); * var geometry = Cesium.WallOutlineGeometry.createGeometry(wall); */ function WallOutlineGeometry(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var wallPositions = options.positions; var maximumHeights = options.maximumHeights; var minimumHeights = options.minimumHeights; //>>includeStart('debug', pragmas.debug); if (!defined(wallPositions)) { throw new DeveloperError("options.positions is required."); } if ( defined(maximumHeights) && maximumHeights.length !== wallPositions.length ) { throw new DeveloperError( "options.positions and options.maximumHeights must have the same length." ); } if ( defined(minimumHeights) && minimumHeights.length !== wallPositions.length ) { throw new DeveloperError( "options.positions and options.minimumHeights must have the same length." ); } //>>includeEnd('debug'); var granularity = defaultValue( options.granularity, CesiumMath.RADIANS_PER_DEGREE ); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._positions = wallPositions; this._minimumHeights = minimumHeights; this._maximumHeights = maximumHeights; this._granularity = granularity; this._ellipsoid = Ellipsoid.clone(ellipsoid); this._workerName = "createWallOutlineGeometry"; var numComponents = 1 + wallPositions.length * Cartesian3.packedLength + 2; if (defined(minimumHeights)) { numComponents += minimumHeights.length; } if (defined(maximumHeights)) { numComponents += maximumHeights.length; } /** * The number of elements used to pack the object into an array. * @type {Number} */ this.packedLength = numComponents + Ellipsoid.packedLength + 1; } /** * Stores the provided instance into the provided array. * * @param {WallOutlineGeometry} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ WallOutlineGeometry.pack = function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var positions = value._positions; var length = positions.length; array[startingIndex++] = length; for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { Cartesian3.pack(positions[i], array, startingIndex); } var minimumHeights = value._minimumHeights; length = defined(minimumHeights) ? minimumHeights.length : 0; array[startingIndex++] = length; if (defined(minimumHeights)) { for (i = 0; i < length; ++i) { array[startingIndex++] = minimumHeights[i]; } } var maximumHeights = value._maximumHeights; length = defined(maximumHeights) ? maximumHeights.length : 0; array[startingIndex++] = length; if (defined(maximumHeights)) { for (i = 0; i < length; ++i) { array[startingIndex++] = maximumHeights[i]; } } Ellipsoid.pack(value._ellipsoid, array, startingIndex); startingIndex += Ellipsoid.packedLength; array[startingIndex] = value._granularity; return array; }; var scratchEllipsoid$1 = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchOptions = { positions: undefined, minimumHeights: undefined, maximumHeights: undefined, ellipsoid: scratchEllipsoid$1, granularity: undefined, }; /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {WallOutlineGeometry} [result] The object into which to store the result. * @returns {WallOutlineGeometry} The modified result parameter or a new WallOutlineGeometry instance if one was not provided. */ WallOutlineGeometry.unpack = function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); var i; var length = array[startingIndex++]; var positions = new Array(length); for (i = 0; i < length; ++i, startingIndex += Cartesian3.packedLength) { positions[i] = Cartesian3.unpack(array, startingIndex); } length = array[startingIndex++]; var minimumHeights; if (length > 0) { minimumHeights = new Array(length); for (i = 0; i < length; ++i) { minimumHeights[i] = array[startingIndex++]; } } length = array[startingIndex++]; var maximumHeights; if (length > 0) { maximumHeights = new Array(length); for (i = 0; i < length; ++i) { maximumHeights[i] = array[startingIndex++]; } } var ellipsoid = Ellipsoid.unpack(array, startingIndex, scratchEllipsoid$1); startingIndex += Ellipsoid.packedLength; var granularity = array[startingIndex]; if (!defined(result)) { scratchOptions.positions = positions; scratchOptions.minimumHeights = minimumHeights; scratchOptions.maximumHeights = maximumHeights; scratchOptions.granularity = granularity; return new WallOutlineGeometry(scratchOptions); } result._positions = positions; result._minimumHeights = minimumHeights; result._maximumHeights = maximumHeights; result._ellipsoid = Ellipsoid.clone(ellipsoid, result._ellipsoid); result._granularity = granularity; return result; }; /** * A description of a walloutline. A wall is defined by a series of points, * which extrude down to the ground. Optionally, they can extrude downwards to a specified height. * * @param {Object} options Object with the following properties: * @param {Cartesian3[]} options.positions An array of Cartesian objects, which are the points of the wall. * @param {Number} [options.maximumHeight] A constant that defines the maximum height of the * wall at positions. If undefined, the height of each position in used. * @param {Number} [options.minimumHeight] A constant that defines the minimum height of the * wall at positions. If undefined, the height at each position is 0.0. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid for coordinate manipulation * @returns {WallOutlineGeometry} * * * @example * // create a wall that spans from 10000 meters to 20000 meters * var wall = Cesium.WallOutlineGeometry.fromConstantHeights({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 19.0, 47.0, * 19.0, 48.0, * 20.0, 48.0, * 20.0, 47.0, * 19.0, 47.0, * ]), * minimumHeight : 20000.0, * maximumHeight : 10000.0 * }); * var geometry = Cesium.WallOutlineGeometry.createGeometry(wall); * * @see WallOutlineGeometry#createGeometry */ WallOutlineGeometry.fromConstantHeights = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var positions = options.positions; //>>includeStart('debug', pragmas.debug); if (!defined(positions)) { throw new DeveloperError("options.positions is required."); } //>>includeEnd('debug'); var minHeights; var maxHeights; var min = options.minimumHeight; var max = options.maximumHeight; var doMin = defined(min); var doMax = defined(max); if (doMin || doMax) { var length = positions.length; minHeights = doMin ? new Array(length) : undefined; maxHeights = doMax ? new Array(length) : undefined; for (var i = 0; i < length; ++i) { if (doMin) { minHeights[i] = min; } if (doMax) { maxHeights[i] = max; } } } var newOptions = { positions: positions, maximumHeights: maxHeights, minimumHeights: minHeights, ellipsoid: options.ellipsoid, }; return new WallOutlineGeometry(newOptions); }; /** * Computes the geometric representation of a wall outline, including its vertices, indices, and a bounding sphere. * * @param {WallOutlineGeometry} wallGeometry A description of the wall outline. * @returns {Geometry|undefined} The computed vertices and indices. */ WallOutlineGeometry.createGeometry = function (wallGeometry) { var wallPositions = wallGeometry._positions; var minimumHeights = wallGeometry._minimumHeights; var maximumHeights = wallGeometry._maximumHeights; var granularity = wallGeometry._granularity; var ellipsoid = wallGeometry._ellipsoid; var pos = WallGeometryLibrary.computePositions( ellipsoid, wallPositions, maximumHeights, minimumHeights, granularity, false ); if (!defined(pos)) { return; } var bottomPositions = pos.bottomPositions; var topPositions = pos.topPositions; var length = topPositions.length; var size = length * 2; var positions = new Float64Array(size); var positionIndex = 0; // add lower and upper points one after the other, lower // points being even and upper points being odd length /= 3; var i; for (i = 0; i < length; ++i) { var i3 = i * 3; var topPosition = Cartesian3.fromArray( topPositions, i3, scratchCartesian3Position1 ); var bottomPosition = Cartesian3.fromArray( bottomPositions, i3, scratchCartesian3Position2 ); // insert the lower point positions[positionIndex++] = bottomPosition.x; positions[positionIndex++] = bottomPosition.y; positions[positionIndex++] = bottomPosition.z; // insert the upper point positions[positionIndex++] = topPosition.x; positions[positionIndex++] = topPosition.y; positions[positionIndex++] = topPosition.z; } var attributes = new GeometryAttributes({ position: new GeometryAttribute({ componentDatatype: ComponentDatatype$1.DOUBLE, componentsPerAttribute: 3, values: positions, }), }); var numVertices = size / 3; size = 2 * numVertices - 4 + numVertices; var indices = IndexDatatype$1.createTypedArray(numVertices, size); var edgeIndex = 0; for (i = 0; i < numVertices - 2; i += 2) { var LL = i; var LR = i + 2; var pl = Cartesian3.fromArray( positions, LL * 3, scratchCartesian3Position1 ); var pr = Cartesian3.fromArray( positions, LR * 3, scratchCartesian3Position2 ); if (Cartesian3.equalsEpsilon(pl, pr, CesiumMath.EPSILON10)) { continue; } var UL = i + 1; var UR = i + 3; indices[edgeIndex++] = UL; indices[edgeIndex++] = LL; indices[edgeIndex++] = UL; indices[edgeIndex++] = UR; indices[edgeIndex++] = LL; indices[edgeIndex++] = LR; } indices[edgeIndex++] = numVertices - 2; indices[edgeIndex++] = numVertices - 1; return new Geometry({ attributes: attributes, indices: indices, primitiveType: PrimitiveType$1.LINES, boundingSphere: new BoundingSphere.fromVertices(positions), }); }; /** * A spline that linearly interpolates over an array of weight values used by morph targets. * * @alias WeightSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. * The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Number[]} options.weights The array of floating-point control weights given. The weights are ordered such * that all weights for the targets are given in chronological order and order in which they appear in * the glTF from which the morph targets come. This means for 2 targets, weights = [w(0,0), w(0,1), w(1,0), w(1,1) ...] * where i and j in w(i,j) are the time indices and target indices, respectively. * * @exception {DeveloperError} weights.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be a factor of weights.length. * * * @example * var times = [ 0.0, 1.5, 3.0, 4.5, 6.0 ]; * var weights = [0.0, 1.0, 0.25, 0.75, 0.5, 0.5, 0.75, 0.25, 1.0, 0.0]; //Two targets * var spline = new Cesium.WeightSpline({ * times : times, * weights : weights * }); * * var p0 = spline.evaluate(times[0]); * * @see LinearSpline * @see HermiteSpline * @see CatmullRomSpline * @see QuaternionSpline */ function WeightSpline(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var weights = options.weights; var times = options.times; //>>includeStart('debug', pragmas.debug); Check.defined("weights", weights); Check.defined("times", times); Check.typeOf.number.greaterThanOrEquals("weights.length", weights.length, 3); if (weights.length % times.length !== 0) { throw new DeveloperError( "times.length must be a factor of weights.length." ); } //>>includeEnd('debug'); this._times = times; this._weights = weights; this._count = weights.length / times.length; this._lastTimeIndex = 0; } Object.defineProperties(WeightSpline.prototype, { /** * An array of times for the control weights. * * @memberof WeightSpline.prototype * * @type {Number[]} * @readonly */ times: { get: function () { return this._times; }, }, /** * An array of floating-point array control weights. * * @memberof WeightSpline.prototype * * @type {Number[]} * @readonly */ weights: { get: function () { return this._weights; }, }, }); /** * Finds an index i in times such that the parameter * time is in the interval [times[i], times[i + 1]]. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ WeightSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; /** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */ WeightSpline.prototype.wrapTime = Spline.prototype.wrapTime; /** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */ WeightSpline.prototype.clampTime = Spline.prototype.clampTime; /** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Number[]} [result] The object onto which to store the result. * @returns {Number[]} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range [t0, tn], where t0 * is the first element in the array times and tn is the last element * in the array times. */ WeightSpline.prototype.evaluate = function (time, result) { var weights = this.weights; var times = this.times; var i = (this._lastTimeIndex = this.findTimeInterval( time, this._lastTimeIndex )); var u = (time - times[i]) / (times[i + 1] - times[i]); if (!defined(result)) { result = new Array(this._count); } for (var j = 0; j < this._count; j++) { var index = i * this._count + j; result[j] = weights[index] * (1.0 - u) + weights[index + this._count] * u; } return result; }; /** * Create a shallow copy of an array from begin to end. * * @param {Array} array The array to fill. * @param {Number} [begin=0] The index to start at. * @param {Number} [end=array.length] The index to end at which is not included. * * @returns {Array} The resulting array. * @private */ function arraySlice(array, begin, end) { //>>includeStart('debug', pragmas.debug); Check.defined("array", array); if (defined(begin)) { Check.typeOf.number("begin", begin); } if (defined(end)) { Check.typeOf.number("end", end); } //>>includeEnd('debug'); if (typeof array.slice === "function") { return array.slice(begin, end); } var copy = Array.prototype.slice.call(array, begin, end); var typedArrayTypes = FeatureDetection.typedArrayTypes; var length = typedArrayTypes.length; for (var i = 0; i < length; ++i) { if (array instanceof typedArrayTypes[i]) { copy = new typedArrayTypes[i](copy); break; } } return copy; } var implementation$1; if (typeof cancelAnimationFrame !== "undefined") { implementation$1 = cancelAnimationFrame; } (function () { // look for vendor prefixed function if (!defined(implementation$1) && typeof window !== "undefined") { var vendors = ["webkit", "moz", "ms", "o"]; var i = 0; var len = vendors.length; while (i < len && !defined(implementation$1)) { implementation$1 = window[vendors[i] + "CancelAnimationFrame"]; if (!defined(implementation$1)) { implementation$1 = window[vendors[i] + "CancelRequestAnimationFrame"]; } ++i; } } // otherwise, assume requestAnimationFrame is based on setTimeout, so use clearTimeout if (!defined(implementation$1)) { implementation$1 = clearTimeout; } })(); /** * A browser-independent function to cancel an animation frame requested using {@link requestAnimationFrame}. * * @function cancelAnimationFrame * * @param {Number} requestID The value returned by {@link requestAnimationFrame}. * * @see {@link http://www.w3.org/TR/animation-timing/#the-WindowAnimationTiming-interface|The WindowAnimationTiming interface} */ function cancelAnimationFramePolyfill(requestID) { // we need this extra wrapper function because the native cancelAnimationFrame // functions must be invoked on the global scope (window), which is not the case // if invoked as Cesium.cancelAnimationFrame(requestID) implementation$1(requestID); } /** * Creates a Globally unique identifier (GUID) string. A GUID is 128 bits long, and can guarantee uniqueness across space and time. * * @function * * @returns {String} * * * @example * this.guid = Cesium.createGuid(); * * @see {@link http://www.ietf.org/rfc/rfc4122.txt|RFC 4122 A Universally Unique IDentifier (UUID) URN Namespace} */ function createGuid() { // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { var r = (Math.random() * 16) | 0; var v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); } /** * Creates a {@link CesiumTerrainProvider} instance for the {@link https://cesium.com/content/#cesium-world-terrain|Cesium World Terrain}. * * @function * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.requestVertexNormals=false] Flag that indicates if the client should request additional lighting information from the server if available. * @param {Boolean} [options.requestWaterMask=false] Flag that indicates if the client should request per tile water masks from the server if available. * @returns {CesiumTerrainProvider} * * @see Ion * * @example * // Create Cesium World Terrain with default settings * var viewer = new Cesium.Viewer('cesiumContainer', { * terrainProvider : Cesium.createWorldTerrain(); * }); * * @example * // Create Cesium World Terrain with water and normals. * var viewer = new Cesium.Viewer('cesiumContainer', { * terrainProvider : Cesium.createWorldTerrain({ * requestWaterMask : true, * requestVertexNormals : true * }); * }); * */ function createWorldTerrain(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); return new CesiumTerrainProvider({ url: IonResource.fromAssetId(1), requestVertexNormals: defaultValue(options.requestVertexNormals, false), requestWaterMask: defaultValue(options.requestWaterMask, false), }); } var compressedMagic = 0x7468dead; var compressedMagicSwap = 0xadde6874; /** * Decodes data that is received from the Google Earth Enterprise server. * * @param {ArrayBuffer} key The key used during decoding. * @param {ArrayBuffer} data The data to be decoded. * * @private */ function decodeGoogleEarthEnterpriseData(key, data) { if (decodeGoogleEarthEnterpriseData.passThroughDataForTesting) { return data; } //>>includeStart('debug', pragmas.debug); Check.typeOf.object("key", key); Check.typeOf.object("data", data); //>>includeEnd('debug'); var keyLength = key.byteLength; if (keyLength === 0 || keyLength % 4 !== 0) { throw new RuntimeError( "The length of key must be greater than 0 and a multiple of 4." ); } var dataView = new DataView(data); var magic = dataView.getUint32(0, true); if (magic === compressedMagic || magic === compressedMagicSwap) { // Occasionally packets don't come back encoded, so just return return data; } var keyView = new DataView(key); var dp = 0; var dpend = data.byteLength; var dpend64 = dpend - (dpend % 8); var kpend = keyLength; var kp; var off = 8; // This algorithm is intentionally asymmetric to make it more difficult to // guess. Security through obscurity. :-( // while we have a full uint64 (8 bytes) left to do // assumes buffer is 64bit aligned (or processor doesn't care) while (dp < dpend64) { // rotate the key each time through by using the offets 16,0,8,16,0,8,... off = (off + 8) % 24; kp = off; // run through one key length xor'ing one uint64 at a time // then drop out to rotate the key for the next bit while (dp < dpend64 && kp < kpend) { dataView.setUint32( dp, dataView.getUint32(dp, true) ^ keyView.getUint32(kp, true), true ); dataView.setUint32( dp + 4, dataView.getUint32(dp + 4, true) ^ keyView.getUint32(kp + 4, true), true ); dp += 8; kp += 24; } } // now the remaining 1 to 7 bytes if (dp < dpend) { if (kp >= kpend) { // rotate the key one last time (if necessary) off = (off + 8) % 24; kp = off; } while (dp < dpend) { dataView.setUint8(dp, dataView.getUint8(dp) ^ keyView.getUint8(kp)); dp++; kp++; } } } decodeGoogleEarthEnterpriseData.passThroughDataForTesting = false; /** * Logs a deprecation message to the console. Use this function instead of * console.log directly since this does not log duplicate messages * unless it is called from multiple workers. * * @function deprecationWarning * * @param {String} identifier The unique identifier for this deprecated API. * @param {String} message The message to log to the console. * * @example * // Deprecated function or class * function Foo() { * deprecationWarning('Foo', 'Foo was deprecated in Cesium 1.01. It will be removed in 1.03. Use newFoo instead.'); * // ... * } * * // Deprecated function * Bar.prototype.func = function() { * deprecationWarning('Bar.func', 'Bar.func() was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newFunc() instead.'); * // ... * }; * * // Deprecated property * Object.defineProperties(Bar.prototype, { * prop : { * get : function() { * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); * // ... * }, * set : function(value) { * deprecationWarning('Bar.prop', 'Bar.prop was deprecated in Cesium 1.01. It will be removed in 1.03. Use Bar.newProp instead.'); * // ... * } * } * }); * * @private */ function deprecationWarning(identifier, message) { //>>includeStart('debug', pragmas.debug); if (!defined(identifier) || !defined(message)) { throw new DeveloperError("identifier and message are required."); } //>>includeEnd('debug'); oneTimeWarning(identifier, message); } /** * Given a URI, returns the last segment of the URI, removing any path or query information. * @function getFilenameFromUri * * @param {String} uri The Uri. * @returns {String} The last segment of the Uri. * * @example * //fileName will be"simple.czml"; * var fileName = Cesium.getFilenameFromUri('/Gallery/simple.czml?value=true&example=false'); */ function getFilenameFromUri(uri) { //>>includeStart('debug', pragmas.debug); if (!defined(uri)) { throw new DeveloperError("uri is required."); } //>>includeEnd('debug'); var uriObject = new URI(uri); uriObject.normalize(); var path = uriObject.path; var index = path.lastIndexOf("/"); if (index !== -1) { path = path.substr(index + 1); } return path; } /** * @private */ function getMagic(uint8Array, byteOffset) { byteOffset = defaultValue(byteOffset, 0); return getStringFromTypedArray( uint8Array, byteOffset, Math.min(4, uint8Array.length) ); } var transcodeTaskProcessor = new TaskProcessor("transcodeCRNToDXT"); /** * Asynchronously loads and parses the given URL to a CRN file or parses the raw binary data of a CRN file. * Returns a promise that will resolve to an object containing the image buffer, width, height and format once loaded, * or reject if the URL failed to load or failed to parse the data. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. * * @function loadCRN * * @param {Resource|String|ArrayBuffer} resourceOrUrlOrBuffer The URL of the binary data or an ArrayBuffer. * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Unsupported compressed format. * * @example * // load a single URL asynchronously * Cesium.loadCRN('some/url').then(function(textureData) { * var width = textureData.width; * var height = textureData.height; * var format = textureData.internalFormat; * var arrayBufferView = textureData.bufferView; * // use the data to create a texture * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link https://github.com/BinomialLLC/crunch|crunch DXTc texture compression and transcoding library} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ function loadCRN(resourceOrUrlOrBuffer) { //>>includeStart('debug', pragmas.debug); if (!defined(resourceOrUrlOrBuffer)) { throw new DeveloperError("resourceOrUrlOrBuffer is required."); } //>>includeEnd('debug'); var loadPromise; if ( resourceOrUrlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(resourceOrUrlOrBuffer) ) { loadPromise = when.resolve(resourceOrUrlOrBuffer); } else { var resource = Resource.createIfNeeded(resourceOrUrlOrBuffer); loadPromise = resource.fetchArrayBuffer(); } if (!defined(loadPromise)) { return undefined; } return loadPromise .then(function (data) { if (!defined(data)) { return; } var transferrableObjects = []; if (data instanceof ArrayBuffer) { transferrableObjects.push(data); } else if ( data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ) { transferrableObjects.push(data.buffer); } else { // data is a view of an array buffer. need to copy so it is transferrable to web worker data = data.slice(0, data.length); transferrableObjects.push(data.buffer); } return transcodeTaskProcessor.scheduleTask(data, transferrableObjects); }) .then(function (compressedTextureBuffer) { return CompressedTextureBuffer.clone(compressedTextureBuffer); }); } /** * @private */ function loadImageFromTypedArray(options) { var uint8Array = options.uint8Array; var format = options.format; var request = options.request; var flipY = defaultValue(options.flipY, false); //>>includeStart('debug', pragmas.debug); Check.typeOf.object("uint8Array", uint8Array); Check.typeOf.string("format", format); //>>includeEnd('debug'); var blob = new Blob([uint8Array], { type: format, }); var blobUrl; return Resource.supportsImageBitmapOptions() .then(function (result) { if (result) { return when( Resource.createImageBitmapFromBlob(blob, { flipY: flipY, premultiplyAlpha: false, }) ); } blobUrl = window.URL.createObjectURL(blob); var resource = new Resource({ url: blobUrl, request: request, }); return resource.fetchImage({ flipY: flipY, }); }) .then(function (result) { if (defined(blobUrl)) { window.URL.revokeObjectURL(blobUrl); } return result; }) .otherwise(function (error) { if (defined(blobUrl)) { window.URL.revokeObjectURL(blobUrl); } return when.reject(error); }); } /** * Asynchronously loads and parses the given URL to a KTX file or parses the raw binary data of a KTX file. * Returns a promise that will resolve to an object containing the image buffer, width, height and format once loaded, * or reject if the URL failed to load or failed to parse the data. The data is loaded * using XMLHttpRequest, which means that in order to make requests to another origin, * the server must have Cross-Origin Resource Sharing (CORS) headers enabled. *

* The following are part of the KTX format specification but are not supported: *

    *
  • Big-endian files
  • *
  • Metadata
  • *
  • 3D textures
  • *
  • Texture Arrays
  • *
  • Cubemaps
  • *
  • Mipmaps
  • *
*

* * @function loadKTX * * @param {Resource|String|ArrayBuffer} resourceOrUrlOrBuffer The URL of the binary data or an ArrayBuffer. * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Invalid KTX file. * @exception {RuntimeError} File is the wrong endianness. * @exception {RuntimeError} glInternalFormat is not a valid format. * @exception {RuntimeError} glType must be zero when the texture is compressed. * @exception {RuntimeError} The type size for compressed textures must be 1. * @exception {RuntimeError} glFormat must be zero when the texture is compressed. * @exception {RuntimeError} Generating mipmaps for a compressed texture is unsupported. * @exception {RuntimeError} The base internal format must be the same as the format for uncompressed textures. * @exception {RuntimeError} 3D textures are not supported. * @exception {RuntimeError} Texture arrays are not supported. * @exception {RuntimeError} Cubemaps are not supported. * * @example * // load a single URL asynchronously * Cesium.loadKTX('some/url').then(function(ktxData) { * var width = ktxData.width; * var height = ktxData.height; * var format = ktxData.internalFormat; * var arrayBufferView = ktxData.bufferView; * // use the data to create a texture * }).otherwise(function(error) { * // an error occurred * }); * * @see {@link https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/|KTX file format} * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing} * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A} */ function loadKTX(resourceOrUrlOrBuffer) { //>>includeStart('debug', pragmas.debug); Check.defined("resourceOrUrlOrBuffer", resourceOrUrlOrBuffer); //>>includeEnd('debug'); var loadPromise; if ( resourceOrUrlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(resourceOrUrlOrBuffer) ) { loadPromise = when.resolve(resourceOrUrlOrBuffer); } else { var resource = Resource.createIfNeeded(resourceOrUrlOrBuffer); loadPromise = resource.fetchArrayBuffer(); } if (!defined(loadPromise)) { return undefined; } return loadPromise.then(function (data) { if (defined(data)) { return parseKTX(data); } }); } var fileIdentifier = [ 0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a, ]; var endiannessTest = 0x04030201; var faceOrder = [ "positiveX", "negativeX", "positiveY", "negativeY", "positiveZ", "negativeZ", ]; var sizeOfUint32$7 = 4; function parseKTX(data) { var byteBuffer = new Uint8Array(data); var isKTX = true; var i; for (i = 0; i < fileIdentifier.length; ++i) { if (fileIdentifier[i] !== byteBuffer[i]) { isKTX = false; break; } } if (!isKTX) { throw new RuntimeError("Invalid KTX file."); } var view; var byteOffset; if (defined(data.buffer)) { view = new DataView(data.buffer); byteOffset = data.byteOffset; } else { view = new DataView(data); byteOffset = 0; } byteOffset += 12; // skip identifier var endianness = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; if (endianness !== endiannessTest) { throw new RuntimeError("File is the wrong endianness."); } var glType = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var glTypeSize = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var glFormat = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var glInternalFormat = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var glBaseInternalFormat = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var pixelWidth = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var pixelHeight = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var pixelDepth = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var numberOfArrayElements = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var numberOfFaces = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var numberOfMipmapLevels = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var bytesOfKeyValueByteSize = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; // skip metadata byteOffset += bytesOfKeyValueByteSize; var imageSize = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$7; var texture; if (defined(data.buffer)) { texture = new Uint8Array(data.buffer, byteOffset, imageSize); } else { texture = new Uint8Array(data, byteOffset, imageSize); } // Some tools use a sized internal format. // See table 2: https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml if (glInternalFormat === WebGLConstants$1.RGB8) { glInternalFormat = PixelFormat$1.RGB; } else if (glInternalFormat === WebGLConstants$1.RGBA8) { glInternalFormat = PixelFormat$1.RGBA; } if (!PixelFormat$1.validate(glInternalFormat)) { throw new RuntimeError("glInternalFormat is not a valid format."); } if (PixelFormat$1.isCompressedFormat(glInternalFormat)) { if (glType !== 0) { throw new RuntimeError( "glType must be zero when the texture is compressed." ); } if (glTypeSize !== 1) { throw new RuntimeError( "The type size for compressed textures must be 1." ); } if (glFormat !== 0) { throw new RuntimeError( "glFormat must be zero when the texture is compressed." ); } } else if (glType !== WebGLConstants$1.UNSIGNED_BYTE) { throw new RuntimeError("Only unsigned byte buffers are supported."); } else if (glBaseInternalFormat !== glFormat) { throw new RuntimeError( "The base internal format must be the same as the format for uncompressed textures." ); } if (pixelDepth !== 0) { throw new RuntimeError("3D textures are unsupported."); } if (numberOfArrayElements !== 0) { throw new RuntimeError("Texture arrays are unsupported."); } var offset = texture.byteOffset; var mipmaps = new Array(numberOfMipmapLevels); for (i = 0; i < numberOfMipmapLevels; ++i) { var level = (mipmaps[i] = {}); for (var j = 0; j < numberOfFaces; ++j) { var width = pixelWidth >> i; var height = pixelHeight >> i; var levelSize = PixelFormat$1.isCompressedFormat(glInternalFormat) ? PixelFormat$1.compressedTextureSizeInBytes( glInternalFormat, width, height ) : PixelFormat$1.textureSizeInBytes( glInternalFormat, glType, width, height ); var levelBuffer = new Uint8Array(texture.buffer, offset, levelSize); level[faceOrder[j]] = new CompressedTextureBuffer( glInternalFormat, width, height, levelBuffer ); offset += levelSize; } offset += 3 - ((offset + 3) % 4) + 4; } var result = mipmaps; if (numberOfFaces === 1) { for (i = 0; i < numberOfMipmapLevels; ++i) { result[i] = result[i][faceOrder[0]]; } } if (numberOfMipmapLevels === 1) { result = result[0]; } return result; } var leftScratchArray = []; var rightScratchArray = []; function merge(array, compare, userDefinedObject, start, middle, end) { var leftLength = middle - start + 1; var rightLength = end - middle; var left = leftScratchArray; var right = rightScratchArray; var i; var j; for (i = 0; i < leftLength; ++i) { left[i] = array[start + i]; } for (j = 0; j < rightLength; ++j) { right[j] = array[middle + j + 1]; } i = 0; j = 0; for (var k = start; k <= end; ++k) { var leftElement = left[i]; var rightElement = right[j]; if ( i < leftLength && (j >= rightLength || compare(leftElement, rightElement, userDefinedObject) <= 0) ) { array[k] = leftElement; ++i; } else if (j < rightLength) { array[k] = rightElement; ++j; } } } function sort$1(array, compare, userDefinedObject, start, end) { if (start >= end) { return; } var middle = Math.floor((start + end) * 0.5); sort$1(array, compare, userDefinedObject, start, middle); sort$1(array, compare, userDefinedObject, middle + 1, end); merge(array, compare, userDefinedObject, start, middle, end); } /** * A stable merge sort. * * @function mergeSort * @param {Array} array The array to sort. * @param {mergeSortComparator} comparator The function to use to compare elements in the array. * @param {*} [userDefinedObject] Any item to pass as the third parameter to comparator. * * @example * // Assume array contains BoundingSpheres in world coordinates. * // Sort them in ascending order of distance from the camera. * var position = camera.positionWC; * Cesium.mergeSort(array, function(a, b, position) { * return Cesium.BoundingSphere.distanceSquaredTo(b, position) - Cesium.BoundingSphere.distanceSquaredTo(a, position); * }, position); */ function mergeSort(array, comparator, userDefinedObject) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required."); } if (!defined(comparator)) { throw new DeveloperError("comparator is required."); } //>>includeEnd('debug'); var length = array.length; var scratchLength = Math.ceil(length * 0.5); // preallocate space in scratch arrays leftScratchArray.length = scratchLength; rightScratchArray.length = scratchLength; sort$1(array, comparator, userDefinedObject, 0, length - 1); // trim scratch arrays leftScratchArray.length = 0; rightScratchArray.length = 0; } var coords = new Cartesian3(); /** * Determines if a point is inside a triangle. * * @function pointInsideTriangle * * @param {Cartesian2|Cartesian3} point The point to test. * @param {Cartesian2|Cartesian3} p0 The first point of the triangle. * @param {Cartesian2|Cartesian3} p1 The second point of the triangle. * @param {Cartesian2|Cartesian3} p2 The third point of the triangle. * @returns {Boolean} true if the point is inside the triangle; otherwise, false. * * @example * // Returns true * var p = new Cesium.Cartesian2(0.25, 0.25); * var b = Cesium.pointInsideTriangle(p, * new Cesium.Cartesian2(0.0, 0.0), * new Cesium.Cartesian2(1.0, 0.0), * new Cesium.Cartesian2(0.0, 1.0)); */ function pointInsideTriangle(point, p0, p1, p2) { barycentricCoordinates(point, p0, p1, p2, coords); return coords.x > 0.0 && coords.y > 0.0 && coords.z > 0; } var implementation; if (typeof requestAnimationFrame !== "undefined") { implementation = requestAnimationFrame; } (function () { // look for vendor prefixed function if (!defined(implementation) && typeof window !== "undefined") { var vendors = ["webkit", "moz", "ms", "o"]; var i = 0; var len = vendors.length; while (i < len && !defined(implementation)) { implementation = window[vendors[i] + "RequestAnimationFrame"]; ++i; } } // build an implementation based on setTimeout if (!defined(implementation)) { var msPerFrame = 1000.0 / 60.0; var lastFrameTime = 0; implementation = function (callback) { var currentTime = getTimestamp$1(); // schedule the callback to target 60fps, 16.7ms per frame, // accounting for the time taken by the callback var delay = Math.max(msPerFrame - (currentTime - lastFrameTime), 0); lastFrameTime = currentTime + delay; return setTimeout(function () { callback(lastFrameTime); }, delay); }; } })(); /** * A browser-independent function to request a new animation frame. This is used to create * an application's draw loop as shown in the example below. * * @function requestAnimationFrame * * @param {requestAnimationFrameCallback} callback The function to call when the next frame should be drawn. * @returns {Number} An ID that can be passed to {@link cancelAnimationFrame} to cancel the request. * * * @example * // Create a draw loop using requestAnimationFrame. The * // tick callback function is called for every animation frame. * function tick() { * scene.render(); * Cesium.requestAnimationFrame(tick); * } * tick(); * * @see {@link https://www.w3.org/TR/html51/webappapis.html#animation-frames|The Web API Animation Frames interface} */ function requestAnimationFramePolyFill(callback) { // we need this extra wrapper function because the native requestAnimationFrame // functions must be invoked on the global scope (window), which is not the case // if invoked as Cesium.requestAnimationFrame(callback) return implementation(callback); } /** * Initiates a terrain height query for an array of {@link Cartographic} positions by * requesting tiles from a terrain provider, sampling, and interpolating. The interpolation * matches the triangles used to render the terrain at the specified level. The query * happens asynchronously, so this function returns a promise that is resolved when * the query completes. Each point height is modified in place. If a height can not be * determined because no terrain data is available for the specified level at that location, * or another error occurs, the height is set to undefined. As is typical of the * {@link Cartographic} type, the supplied height is a height above the reference ellipsoid * (such as {@link Ellipsoid.WGS84}) rather than an altitude above mean sea level. In other * words, it will not necessarily be 0.0 if sampled in the ocean. This function needs the * terrain level of detail as input, if you need to get the altitude of the terrain as precisely * as possible (i.e. with maximum level of detail) use {@link sampleTerrainMostDetailed}. * * @function sampleTerrain * * @param {TerrainProvider} terrainProvider The terrain provider from which to query heights. * @param {Number} level The terrain level-of-detail from which to query terrain heights. * @param {Cartographic[]} positions The positions to update with terrain heights. * @returns {Promise.} A promise that resolves to the provided list of positions when terrain the query has completed. * * @see sampleTerrainMostDetailed * * @example * // Query the terrain height of two Cartographic positions * var terrainProvider = Cesium.createWorldTerrain(); * var positions = [ * Cesium.Cartographic.fromDegrees(86.925145, 27.988257), * Cesium.Cartographic.fromDegrees(87.0, 28.0) * ]; * var promise = Cesium.sampleTerrain(terrainProvider, 11, positions); * Cesium.when(promise, function(updatedPositions) { * // positions[0].height and positions[1].height have been updated. * // updatedPositions is just a reference to positions. * }); */ function sampleTerrain(terrainProvider, level, positions) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("terrainProvider", terrainProvider); Check.typeOf.number("level", level); Check.defined("positions", positions); //>>includeEnd('debug'); return terrainProvider.readyPromise.then(function () { return doSampling(terrainProvider, level, positions); }); } function doSampling(terrainProvider, level, positions) { var tilingScheme = terrainProvider.tilingScheme; var i; // Sort points into a set of tiles var tileRequests = []; // Result will be an Array as it's easier to work with var tileRequestSet = {}; // A unique set for (i = 0; i < positions.length; ++i) { var xy = tilingScheme.positionToTileXY(positions[i], level); var key = xy.toString(); if (!tileRequestSet.hasOwnProperty(key)) { // When tile is requested for the first time var value = { x: xy.x, y: xy.y, level: level, tilingScheme: tilingScheme, terrainProvider: terrainProvider, positions: [], }; tileRequestSet[key] = value; tileRequests.push(value); } // Now append to array of points for the tile tileRequestSet[key].positions.push(positions[i]); } // Send request for each required tile var tilePromises = []; for (i = 0; i < tileRequests.length; ++i) { var tileRequest = tileRequests[i]; var requestPromise = tileRequest.terrainProvider.requestTileGeometry( tileRequest.x, tileRequest.y, tileRequest.level ); var tilePromise = requestPromise .then(createInterpolateFunction(tileRequest)) .otherwise(createMarkFailedFunction(tileRequest)); tilePromises.push(tilePromise); } return when.all(tilePromises, function () { return positions; }); } /** * Calls {@link TerrainData#interpolateHeight} on a given {@link TerrainData} for a given {@link Cartographic} and * will assign the height property if the return value is not undefined. * * If the return value is false; it's suggesting that you should call {@link TerrainData#createMesh} first. * @param {Cartographic} position The position to interpolate for and assign the height value to * @param {TerrainData} terrainData * @param {Rectangle} rectangle * @returns {Boolean} If the height was actually interpolated and assigned * @private */ function interpolateAndAssignHeight(position, terrainData, rectangle) { var height = terrainData.interpolateHeight( rectangle, position.longitude, position.latitude ); if (height === undefined) { // if height comes back as undefined, it may implicitly mean the terrain data // requires us to call TerrainData.createMesh() first (ArcGIS requires this in particular) // so we'll return false and do that next! return false; } position.height = height; return true; } function createInterpolateFunction(tileRequest) { var tilePositions = tileRequest.positions; var rectangle = tileRequest.tilingScheme.tileXYToRectangle( tileRequest.x, tileRequest.y, tileRequest.level ); return function (terrainData) { var isMeshRequired = false; for (var i = 0; i < tilePositions.length; ++i) { var position = tilePositions[i]; var isHeightAssigned = interpolateAndAssignHeight( position, terrainData, rectangle ); // we've found a position which returned undefined - hinting to us // that we probably need to create a mesh for this terrain data. // so break out of this loop and create the mesh - then we'll interpolate all the heights again if (!isHeightAssigned) { isMeshRequired = true; break; } } if (!isMeshRequired) { // all position heights were interpolated - we don't need the mesh return when.resolve(); } // create the mesh - and interpolate all the positions again return terrainData .createMesh({ tilingScheme: tileRequest.tilingScheme, x: tileRequest.x, y: tileRequest.y, level: tileRequest.level, // interpolateHeight will divide away the exaggeration - so passing in 1 is fine; it doesn't really matter exaggeration: 1, // don't throttle this mesh creation because we've asked to sample these points; // so sample them! We don't care how many tiles that is! throttle: false, }) .then(function () { // mesh has been created - so go through every position (maybe again) // and re-interpolate the heights - presumably using the mesh this time for (var i = 0; i < tilePositions.length; ++i) { var position = tilePositions[i]; // if it doesn't work this time - that's fine, we tried. interpolateAndAssignHeight(position, terrainData, rectangle); } }); }; } function createMarkFailedFunction(tileRequest) { var tilePositions = tileRequest.positions; return function () { for (var i = 0; i < tilePositions.length; ++i) { var position = tilePositions[i]; position.height = undefined; } }; } var scratchCartesian2$4 = new Cartesian2(); /** * Initiates a sampleTerrain() request at the maximum available tile level for a terrain dataset. * * @function sampleTerrainMostDetailed * * @param {TerrainProvider} terrainProvider The terrain provider from which to query heights. * @param {Cartographic[]} positions The positions to update with terrain heights. * @returns {Promise.} A promise that resolves to the provided list of positions when terrain the query has completed. This * promise will reject if the terrain provider's `availability` property is undefined. * * @example * // Query the terrain height of two Cartographic positions * var terrainProvider = Cesium.createWorldTerrain(); * var positions = [ * Cesium.Cartographic.fromDegrees(86.925145, 27.988257), * Cesium.Cartographic.fromDegrees(87.0, 28.0) * ]; * var promise = Cesium.sampleTerrainMostDetailed(terrainProvider, positions); * Cesium.when(promise, function(updatedPositions) { * // positions[0].height and positions[1].height have been updated. * // updatedPositions is just a reference to positions. * }); */ function sampleTerrainMostDetailed(terrainProvider, positions) { //>>includeStart('debug', pragmas.debug); if (!defined(terrainProvider)) { throw new DeveloperError("terrainProvider is required."); } if (!defined(positions)) { throw new DeveloperError("positions is required."); } //>>includeEnd('debug'); return terrainProvider.readyPromise.then(function () { var byLevel = []; var maxLevels = []; var availability = terrainProvider.availability; //>>includeStart('debug', pragmas.debug); if (!defined(availability)) { throw new DeveloperError( "sampleTerrainMostDetailed requires a terrain provider that has tile availability." ); } //>>includeEnd('debug'); var promises = []; for (var i = 0; i < positions.length; ++i) { var position = positions[i]; var maxLevel = availability.computeMaximumLevelAtPosition(position); maxLevels[i] = maxLevel; if (maxLevel === 0) { // This is a special case where we have a parent terrain and we are requesting // heights from an area that isn't covered by the top level terrain at all. // This will essentially trigger the loading of the parent terrains root tile terrainProvider.tilingScheme.positionToTileXY( position, 1, scratchCartesian2$4 ); var promise = terrainProvider.loadTileDataAvailability( scratchCartesian2$4.x, scratchCartesian2$4.y, 1 ); if (defined(promise)) { promises.push(promise); } } var atLevel = byLevel[maxLevel]; if (!defined(atLevel)) { byLevel[maxLevel] = atLevel = []; } atLevel.push(position); } return when .all(promises) .then(function () { return when.all( byLevel.map(function (positionsAtLevel, index) { if (defined(positionsAtLevel)) { return sampleTerrain(terrainProvider, index, positionsAtLevel); } }) ); }) .then(function () { var changedPositions = []; for (var i = 0; i < positions.length; ++i) { var position = positions[i]; var maxLevel = availability.computeMaximumLevelAtPosition(position); if (maxLevel !== maxLevels[i]) { // Now that we loaded the max availability, a higher level has become available changedPositions.push(position); } } if (changedPositions.length > 0) { return sampleTerrainMostDetailed(terrainProvider, changedPositions); } }) .then(function () { return positions; }); }); } /** * Subdivides an array into a number of smaller, equal sized arrays. * * @function subdivideArray * * @param {Array} array The array to divide. * @param {Number} numberOfArrays The number of arrays to divide the provided array into. * * @exception {DeveloperError} numberOfArrays must be greater than 0. */ function subdivideArray(array, numberOfArrays) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required."); } if (!defined(numberOfArrays) || numberOfArrays < 1) { throw new DeveloperError("numberOfArrays must be greater than 0."); } //>>includeEnd('debug'); var result = []; var len = array.length; var i = 0; while (i < len) { var size = Math.ceil((len - i) / numberOfArrays--); result.push(array.slice(i, i + size)); i += size; } return result; } function webGLConstantToGlslType(webGLValue) { switch (webGLValue) { case WebGLConstants$1.FLOAT: return "float"; case WebGLConstants$1.FLOAT_VEC2: return "vec2"; case WebGLConstants$1.FLOAT_VEC3: return "vec3"; case WebGLConstants$1.FLOAT_VEC4: return "vec4"; case WebGLConstants$1.FLOAT_MAT2: return "mat2"; case WebGLConstants$1.FLOAT_MAT3: return "mat3"; case WebGLConstants$1.FLOAT_MAT4: return "mat4"; case WebGLConstants$1.SAMPLER_2D: return "sampler2D"; case WebGLConstants$1.BOOL: return "bool"; } } /** * Wraps a function on the provided objects with another function called in the * object's context so that the new function is always called immediately * before the old one. * * @private */ function wrapFunction(obj, oldFunction, newFunction) { //>>includeStart('debug', pragmas.debug); if (typeof oldFunction !== "function") { throw new DeveloperError("oldFunction is required to be a function."); } if (typeof newFunction !== "function") { throw new DeveloperError("oldFunction is required to be a function."); } //>>includeEnd('debug'); return function () { newFunction.apply(obj, arguments); oldFunction.apply(obj, arguments); }; } /** * A {@link Property} whose value does not change with respect to simulation time. * * @alias ConstantProperty * @constructor * * @param {*} [value] The property value. * * @see ConstantPositionProperty */ function ConstantProperty(value) { this._value = undefined; this._hasClone = false; this._hasEquals = false; this._definitionChanged = new Event(); this.setValue(value); } Object.defineProperties(ConstantProperty.prototype, { /** * Gets a value indicating if this property is constant. * This property always returns true. * @memberof ConstantProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { value: true, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value. * @memberof ConstantProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, }); /** * Gets the value of the property. * * @param {JulianDate} [time] The time for which to retrieve the value. This parameter is unused since the value does not change with respect to time. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ ConstantProperty.prototype.getValue = function (time, result) { return this._hasClone ? this._value.clone(result) : this._value; }; /** * Sets the value of the property. * * @param {*} value The property value. */ ConstantProperty.prototype.setValue = function (value) { var oldValue = this._value; if (oldValue !== value) { var isDefined = defined(value); var hasClone = isDefined && typeof value.clone === "function"; var hasEquals = isDefined && typeof value.equals === "function"; var changed = !hasEquals || !value.equals(oldValue); if (changed) { this._hasClone = hasClone; this._hasEquals = hasEquals; this._value = !hasClone ? value : value.clone(this._value); this._definitionChanged.raiseEvent(this); } } }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ ConstantProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof ConstantProperty && // ((!this._hasEquals && this._value === other._value) || // (this._hasEquals && this._value.equals(other._value)))) ); }; /** * Gets this property's value. * * @returns {*} This property's value. */ ConstantProperty.prototype.valueOf = function () { return this._value; }; /** * Creates a string representing this property's value. * * @returns {String} A string representing the property's value. */ ConstantProperty.prototype.toString = function () { return String(this._value); }; function createProperty( name, privateName, subscriptionName, configurable, createPropertyCallback ) { return { configurable: configurable, get: function () { return this[privateName]; }, set: function (value) { var oldValue = this[privateName]; var subscription = this[subscriptionName]; if (defined(subscription)) { subscription(); this[subscriptionName] = undefined; } var hasValue = value !== undefined; if ( hasValue && (!defined(value) || !defined(value.getValue)) && defined(createPropertyCallback) ) { value = createPropertyCallback(value); } if (oldValue !== value) { this[privateName] = value; this._definitionChanged.raiseEvent(this, name, value, oldValue); } if (defined(value) && defined(value.definitionChanged)) { this[subscriptionName] = value.definitionChanged.addEventListener( function () { this._definitionChanged.raiseEvent(this, name, value, value); }, this ); } }, }; } function createConstantProperty$1(value) { return new ConstantProperty(value); } /** * Used to consistently define all DataSources graphics objects. * This is broken into two functions because the Chrome profiler does a better * job of optimizing lookups if it notices that the string is constant throughout the function. * @private */ function createPropertyDescriptor(name, configurable, createPropertyCallback) { //Safari 8.0.3 has a JavaScript bug that causes it to confuse two variables and treat them as the same. //The two extra toString calls work around the issue. return createProperty( name, "_" + name.toString(), "_" + name.toString() + "Subscription", defaultValue(configurable, false), defaultValue(createPropertyCallback, createConstantProperty$1) ); } /** * @typedef {Object} BillboardGraphics.ConstructorOptions * * Initialization options for the BillboardGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the billboard. * @property {Property | string | HTMLCanvasElement} [image] A Property specifying the Image, URI, or Canvas to use for the billboard. * @property {Property | number} [scale=1.0] A numeric Property specifying the scale to apply to the image size. * @property {Property | Cartesian2} [pixelOffset=Cartesian2.ZERO] A {@link Cartesian2} Property specifying the pixel offset. * @property {Property | Cartesian3} [eyeOffset=Cartesian3.ZERO] A {@link Cartesian3} Property specifying the eye offset. * @property {Property | HorizontalOrigin} [horizontalOrigin=HorizontalOrigin.CENTER] A Property specifying the {@link HorizontalOrigin}. * @property {Property | VerticalOrigin} [verticalOrigin=VerticalOrigin.CENTER] A Property specifying the {@link VerticalOrigin}. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | Color} [color=Color.WHITE] A Property specifying the tint {@link Color} of the image. * @property {Property | number} [rotation=0] A numeric Property specifying the rotation about the alignedAxis. * @property {Property | Cartesian3} [alignedAxis=Cartesian3.ZERO] A {@link Cartesian3} Property specifying the unit vector axis of rotation. * @property {Property | boolean} [sizeInMeters] A boolean Property specifying whether this billboard's size should be measured in meters. * @property {Property | number} [width] A numeric Property specifying the width of the billboard in pixels, overriding the native size. * @property {Property | number} [height] A numeric Property specifying the height of the billboard in pixels, overriding the native size. * @property {Property | NearFarScalar} [scaleByDistance] A {@link NearFarScalar} Property used to scale the point based on distance from the camera. * @property {Property | NearFarScalar} [translucencyByDistance] A {@link NearFarScalar} Property used to set translucency based on distance from the camera. * @property {Property | NearFarScalar} [pixelOffsetScaleByDistance] A {@link NearFarScalar} Property used to set pixelOffset based on distance from the camera. * @property {Property | BoundingRectangle} [imageSubRegion] A Property specifying a {@link BoundingRectangle} that defines a sub-region of the image to use for the billboard, rather than the entire image, measured in pixels from the bottom-left. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this billboard will be displayed. * @property {Property | number} [disableDepthTestDistance] A Property specifying the distance from the camera at which to disable the depth test to. */ /** * Describes a two dimensional icon located at the position of the containing {@link Entity}. *

*

*
* Example billboards *
*

* * @alias BillboardGraphics * @constructor * * @param {BillboardGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Billboards.html|Cesium Sandcastle Billboard Demo} */ function BillboardGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._image = undefined; this._imageSubscription = undefined; this._scale = undefined; this._scaleSubscription = undefined; this._pixelOffset = undefined; this._pixelOffsetSubscription = undefined; this._eyeOffset = undefined; this._eyeOffsetSubscription = undefined; this._horizontalOrigin = undefined; this._horizontalOriginSubscription = undefined; this._verticalOrigin = undefined; this._verticalOriginSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._color = undefined; this._colorSubscription = undefined; this._rotation = undefined; this._rotationSubscription = undefined; this._alignedAxis = undefined; this._alignedAxisSubscription = undefined; this._sizeInMeters = undefined; this._sizeInMetersSubscription = undefined; this._width = undefined; this._widthSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; this._scaleByDistance = undefined; this._scaleByDistanceSubscription = undefined; this._translucencyByDistance = undefined; this._translucencyByDistanceSubscription = undefined; this._pixelOffsetScaleByDistance = undefined; this._pixelOffsetScaleByDistanceSubscription = undefined; this._imageSubRegion = undefined; this._imageSubRegionSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._disableDepthTestDistance = undefined; this._disableDepthTestDistanceSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(BillboardGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof BillboardGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the billboard. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the Image, URI, or Canvas to use for the billboard. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ image: createPropertyDescriptor("image"), /** * Gets or sets the numeric Property specifying the uniform scale to apply to the image. * A scale greater than 1.0 enlarges the billboard while a scale less than 1.0 shrinks it. *

*

*
* From left to right in the above image, the scales are 0.5, 1.0, and 2.0. *
*

* @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default 1.0 */ scale: createPropertyDescriptor("scale"), /** * Gets or sets the {@link Cartesian2} Property specifying the billboard's pixel offset in screen space * from the origin of this billboard. This is commonly used to align multiple billboards and labels at * the same position, e.g., an image and text. The screen space origin is the top, left corner of the * canvas; x increases from left to right, and y increases from top to bottom. *

*

* * * *
default
b.pixeloffset = new Cartesian2(50, 25);
* The billboard's origin is indicated by the yellow point. *
*

* @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default Cartesian2.ZERO */ pixelOffset: createPropertyDescriptor("pixelOffset"), /** * Gets or sets the {@link Cartesian3} Property specifying the billboard's offset in eye coordinates. * Eye coordinates is a left-handed coordinate system, where x points towards the viewer's * right, y points up, and z points into the screen. *

* An eye offset is commonly used to arrange multiple billboards or objects at the same position, e.g., to * arrange a billboard above its corresponding 3D model. *

* Below, the billboard is positioned at the center of the Earth but an eye offset makes it always * appear on top of the Earth regardless of the viewer's or Earth's orientation. *

*

* * * *
* b.eyeOffset = new Cartesian3(0.0, 8000000.0, 0.0); *
*

* @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default Cartesian3.ZERO */ eyeOffset: createPropertyDescriptor("eyeOffset"), /** * Gets or sets the Property specifying the {@link HorizontalOrigin}. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default HorizontalOrigin.CENTER */ horizontalOrigin: createPropertyDescriptor("horizontalOrigin"), /** * Gets or sets the Property specifying the {@link VerticalOrigin}. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default VerticalOrigin.CENTER */ verticalOrigin: createPropertyDescriptor("verticalOrigin"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the Property specifying the {@link Color} that is multiplied with the image. * This has two common use cases. First, the same white texture may be used by many different billboards, * each with a different color, to create colored billboards. Second, the color's alpha component can be * used to make the billboard translucent as shown below. An alpha of 0.0 makes the billboard * transparent, and 1.0 makes the billboard opaque. *

*

* * * *
default
alpha : 0.5
*
*

* @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), /** * Gets or sets the numeric Property specifying the rotation of the image * counter clockwise from the alignedAxis. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default 0 */ rotation: createPropertyDescriptor("rotation"), /** * Gets or sets the {@link Cartesian3} Property specifying the unit vector axis of rotation * in the fixed frame. When set to Cartesian3.ZERO the rotation is from the top of the screen. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default Cartesian3.ZERO */ alignedAxis: createPropertyDescriptor("alignedAxis"), /** * Gets or sets the boolean Property specifying if this billboard's size will be measured in meters. * @memberof BillboardGraphics.prototype * @type {Property|undefined} * @default false */ sizeInMeters: createPropertyDescriptor("sizeInMeters"), /** * Gets or sets the numeric Property specifying the width of the billboard in pixels. * When undefined, the native width is used. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ width: createPropertyDescriptor("width"), /** * Gets or sets the numeric Property specifying the height of the billboard in pixels. * When undefined, the native height is used. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ height: createPropertyDescriptor("height"), /** * Gets or sets {@link NearFarScalar} Property specifying the scale of the billboard based on the distance from the camera. * A billboard's scale will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's scale remains clamped to the nearest bound. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ scaleByDistance: createPropertyDescriptor("scaleByDistance"), /** * Gets or sets {@link NearFarScalar} Property specifying the translucency of the billboard based on the distance from the camera. * A billboard's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's translucency remains clamped to the nearest bound. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ translucencyByDistance: createPropertyDescriptor("translucencyByDistance"), /** * Gets or sets {@link NearFarScalar} Property specifying the pixel offset of the billboard based on the distance from the camera. * A billboard's pixel offset will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's pixel offset remains clamped to the nearest bound. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ pixelOffsetScaleByDistance: createPropertyDescriptor( "pixelOffsetScaleByDistance" ), /** * Gets or sets the Property specifying a {@link BoundingRectangle} that defines a * sub-region of the image to use for the billboard, rather than the entire image, * measured in pixels from the bottom-left. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ imageSubRegion: createPropertyDescriptor("imageSubRegion"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this billboard will be displayed. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof BillboardGraphics.prototype * @type {Property|undefined} */ disableDepthTestDistance: createPropertyDescriptor( "disableDepthTestDistance" ), }); /** * Duplicates this instance. * * @param {BillboardGraphics} [result] The object onto which to store the result. * @returns {BillboardGraphics} The modified result parameter or a new instance if one was not provided. */ BillboardGraphics.prototype.clone = function (result) { if (!defined(result)) { return new BillboardGraphics(this); } result.show = this._show; result.image = this._image; result.scale = this._scale; result.pixelOffset = this._pixelOffset; result.eyeOffset = this._eyeOffset; result.horizontalOrigin = this._horizontalOrigin; result.verticalOrigin = this._verticalOrigin; result.heightReference = this._heightReference; result.color = this._color; result.rotation = this._rotation; result.alignedAxis = this._alignedAxis; result.sizeInMeters = this._sizeInMeters; result.width = this._width; result.height = this._height; result.scaleByDistance = this._scaleByDistance; result.translucencyByDistance = this._translucencyByDistance; result.pixelOffsetScaleByDistance = this._pixelOffsetScaleByDistance; result.imageSubRegion = this._imageSubRegion; result.distanceDisplayCondition = this._distanceDisplayCondition; result.disableDepthTestDistance = this._disableDepthTestDistance; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {BillboardGraphics} source The object to be merged into this object. */ BillboardGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this._show, source.show); this.image = defaultValue(this._image, source.image); this.scale = defaultValue(this._scale, source.scale); this.pixelOffset = defaultValue(this._pixelOffset, source.pixelOffset); this.eyeOffset = defaultValue(this._eyeOffset, source.eyeOffset); this.horizontalOrigin = defaultValue( this._horizontalOrigin, source.horizontalOrigin ); this.verticalOrigin = defaultValue( this._verticalOrigin, source.verticalOrigin ); this.heightReference = defaultValue( this._heightReference, source.heightReference ); this.color = defaultValue(this._color, source.color); this.rotation = defaultValue(this._rotation, source.rotation); this.alignedAxis = defaultValue(this._alignedAxis, source.alignedAxis); this.sizeInMeters = defaultValue(this._sizeInMeters, source.sizeInMeters); this.width = defaultValue(this._width, source.width); this.height = defaultValue(this._height, source.height); this.scaleByDistance = defaultValue( this._scaleByDistance, source.scaleByDistance ); this.translucencyByDistance = defaultValue( this._translucencyByDistance, source.translucencyByDistance ); this.pixelOffsetScaleByDistance = defaultValue( this._pixelOffsetScaleByDistance, source.pixelOffsetScaleByDistance ); this.imageSubRegion = defaultValue( this._imageSubRegion, source.imageSubRegion ); this.distanceDisplayCondition = defaultValue( this._distanceDisplayCondition, source.distanceDisplayCondition ); this.disableDepthTestDistance = defaultValue( this._disableDepthTestDistance, source.disableDepthTestDistance ); }; /** * Represents the position relative to the terrain. * * @enum {Number} */ var HeightReference = { /** * The position is absolute. * @type {Number} * @constant */ NONE: 0, /** * The position is clamped to the terrain. * @type {Number} * @constant */ CLAMP_TO_GROUND: 1, /** * The position height is the height above the terrain. * @type {Number} * @constant */ RELATIVE_TO_GROUND: 2, }; var HeightReference$1 = Object.freeze(HeightReference); /** * The horizontal location of an origin relative to an object, e.g., a {@link Billboard} * or {@link Label}. For example, setting the horizontal origin to LEFT * or RIGHT will display a billboard to the left or right (in screen space) * of the anchor position. *

*
*
*
* * @enum {Number} * * @see Billboard#horizontalOrigin * @see Label#horizontalOrigin */ var HorizontalOrigin = { /** * The origin is at the horizontal center of the object. * * @type {Number} * @constant */ CENTER: 0, /** * The origin is on the left side of the object. * * @type {Number} * @constant */ LEFT: 1, /** * The origin is on the right side of the object. * * @type {Number} * @constant */ RIGHT: -1, }; var HorizontalOrigin$1 = Object.freeze(HorizontalOrigin); /** * The vertical location of an origin relative to an object, e.g., a {@link Billboard} * or {@link Label}. For example, setting the vertical origin to TOP * or BOTTOM will display a billboard above or below (in screen space) * the anchor position. *

*
*
*
* * @enum {Number} * * @see Billboard#verticalOrigin * @see Label#verticalOrigin */ var VerticalOrigin = { /** * The origin is at the vertical center between BASELINE and TOP. * * @type {Number} * @constant */ CENTER: 0, /** * The origin is at the bottom of the object. * * @type {Number} * @constant */ BOTTOM: 1, /** * If the object contains text, the origin is at the baseline of the text, else the origin is at the bottom of the object. * * @type {Number} * @constant */ BASELINE: 2, /** * The origin is at the top of the object. * * @type {Number} * @constant */ TOP: -1, }; var VerticalOrigin$1 = Object.freeze(VerticalOrigin); /** * The state of a BoundingSphere computation being performed by a {@link Visualizer}. * @enum {Number} * @private */ var BoundingSphereState = { /** * The BoundingSphere has been computed. * @type BoundingSphereState * @constant */ DONE: 0, /** * The BoundingSphere is still being computed. * @type BoundingSphereState * @constant */ PENDING: 1, /** * The BoundingSphere does not exist. * @type BoundingSphereState * @constant */ FAILED: 2, }; var BoundingSphereState$1 = Object.freeze(BoundingSphereState); /** * The interface for all properties, which represent a value that can optionally vary over time. * This type defines an interface and cannot be instantiated directly. * * @alias Property * @constructor * @abstract * * @see CompositeProperty * @see ConstantProperty * @see SampledProperty * @see TimeIntervalCollectionProperty * @see MaterialProperty * @see PositionProperty * @see ReferenceProperty */ function Property() { DeveloperError.throwInstantiationError(); } Object.defineProperties(Property.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof Property.prototype * * @type {Boolean} * @readonly */ isConstant: { get: DeveloperError.throwInstantiationError, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof Property.prototype * * @type {Event} * @readonly */ definitionChanged: { get: DeveloperError.throwInstantiationError, }, }); /** * Gets the value of the property at the provided time. * @function * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ Property.prototype.getValue = DeveloperError.throwInstantiationError; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * @function * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ Property.prototype.equals = DeveloperError.throwInstantiationError; /** * @private */ Property.equals = function (left, right) { return left === right || (defined(left) && left.equals(right)); }; /** * @private */ Property.arrayEquals = function (left, right) { if (left === right) { return true; } if (!defined(left) || !defined(right) || left.length !== right.length) { return false; } var length = left.length; for (var i = 0; i < length; i++) { if (!Property.equals(left[i], right[i])) { return false; } } return true; }; /** * @private */ Property.isConstant = function (property) { return !defined(property) || property.isConstant; }; /** * @private */ Property.getValueOrUndefined = function (property, time, result) { return defined(property) ? property.getValue(time, result) : undefined; }; /** * @private */ Property.getValueOrDefault = function (property, time, valueDefault, result) { return defined(property) ? defaultValue(property.getValue(time, result), valueDefault) : valueDefault; }; /** * @private */ Property.getValueOrClonedDefault = function ( property, time, valueDefault, result ) { var value; if (defined(property)) { value = property.getValue(time, result); } if (!defined(value)) { value = valueDefault.clone(value); } return value; }; var defaultColor$8 = Color.WHITE; var defaultEyeOffset$1 = Cartesian3.ZERO; var defaultHeightReference$2 = HeightReference$1.NONE; var defaultPixelOffset$1 = Cartesian2.ZERO; var defaultScale$2 = 1.0; var defaultRotation = 0.0; var defaultAlignedAxis = Cartesian3.ZERO; var defaultHorizontalOrigin$1 = HorizontalOrigin$1.CENTER; var defaultVerticalOrigin$1 = VerticalOrigin$1.CENTER; var defaultSizeInMeters = false; var positionScratch$7 = new Cartesian3(); var colorScratch$6 = new Color(); var eyeOffsetScratch$1 = new Cartesian3(); var pixelOffsetScratch$1 = new Cartesian2(); var scaleByDistanceScratch$2 = new NearFarScalar(); var translucencyByDistanceScratch$2 = new NearFarScalar(); var pixelOffsetScaleByDistanceScratch$1 = new NearFarScalar(); var boundingRectangleScratch = new BoundingRectangle(); var distanceDisplayConditionScratch$8 = new DistanceDisplayCondition(); function EntityData$3(entity) { this.entity = entity; this.billboard = undefined; this.textureValue = undefined; } /** * A {@link Visualizer} which maps {@link Entity#billboard} to a {@link Billboard}. * @alias BillboardVisualizer * @constructor * * @param {EntityCluster} entityCluster The entity cluster to manage the collection of billboards and optionally cluster with other entities. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function BillboardVisualizer(entityCluster, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(entityCluster)) { throw new DeveloperError("entityCluster is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( BillboardVisualizer.prototype._onCollectionChanged, this ); this._cluster = entityCluster; this._entityCollection = entityCollection; this._items = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ BillboardVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var items = this._items.values; var cluster = this._cluster; for (var i = 0, len = items.length; i < len; i++) { var item = items[i]; var entity = item.entity; var billboardGraphics = entity._billboard; var textureValue; var billboard = item.billboard; var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(billboardGraphics._show, time, true); var position; if (show) { position = Property.getValueOrUndefined( entity._position, time, positionScratch$7 ); textureValue = Property.getValueOrUndefined( billboardGraphics._image, time ); show = defined(position) && defined(textureValue); } if (!show) { //don't bother creating or updating anything else returnPrimitive$2(item, entity, cluster); continue; } if (!Property.isConstant(entity._position)) { cluster._clusterDirty = true; } if (!defined(billboard)) { billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = undefined; item.billboard = billboard; } billboard.show = show; if (!defined(billboard.image) || item.textureValue !== textureValue) { billboard.image = textureValue; item.textureValue = textureValue; } billboard.position = position; billboard.color = Property.getValueOrDefault( billboardGraphics._color, time, defaultColor$8, colorScratch$6 ); billboard.eyeOffset = Property.getValueOrDefault( billboardGraphics._eyeOffset, time, defaultEyeOffset$1, eyeOffsetScratch$1 ); billboard.heightReference = Property.getValueOrDefault( billboardGraphics._heightReference, time, defaultHeightReference$2 ); billboard.pixelOffset = Property.getValueOrDefault( billboardGraphics._pixelOffset, time, defaultPixelOffset$1, pixelOffsetScratch$1 ); billboard.scale = Property.getValueOrDefault( billboardGraphics._scale, time, defaultScale$2 ); billboard.rotation = Property.getValueOrDefault( billboardGraphics._rotation, time, defaultRotation ); billboard.alignedAxis = Property.getValueOrDefault( billboardGraphics._alignedAxis, time, defaultAlignedAxis ); billboard.horizontalOrigin = Property.getValueOrDefault( billboardGraphics._horizontalOrigin, time, defaultHorizontalOrigin$1 ); billboard.verticalOrigin = Property.getValueOrDefault( billboardGraphics._verticalOrigin, time, defaultVerticalOrigin$1 ); billboard.width = Property.getValueOrUndefined( billboardGraphics._width, time ); billboard.height = Property.getValueOrUndefined( billboardGraphics._height, time ); billboard.scaleByDistance = Property.getValueOrUndefined( billboardGraphics._scaleByDistance, time, scaleByDistanceScratch$2 ); billboard.translucencyByDistance = Property.getValueOrUndefined( billboardGraphics._translucencyByDistance, time, translucencyByDistanceScratch$2 ); billboard.pixelOffsetScaleByDistance = Property.getValueOrUndefined( billboardGraphics._pixelOffsetScaleByDistance, time, pixelOffsetScaleByDistanceScratch$1 ); billboard.sizeInMeters = Property.getValueOrDefault( billboardGraphics._sizeInMeters, time, defaultSizeInMeters ); billboard.distanceDisplayCondition = Property.getValueOrUndefined( billboardGraphics._distanceDisplayCondition, time, distanceDisplayConditionScratch$8 ); billboard.disableDepthTestDistance = Property.getValueOrUndefined( billboardGraphics._disableDepthTestDistance, time ); var subRegion = Property.getValueOrUndefined( billboardGraphics._imageSubRegion, time, boundingRectangleScratch ); if (defined(subRegion)) { billboard.setImageSubRegion(billboard._imageId, subRegion); } } return true; }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ BillboardVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var item = this._items.get(entity.id); if (!defined(item) || !defined(item.billboard)) { return BoundingSphereState$1.FAILED; } var billboard = item.billboard; if (billboard.heightReference === HeightReference$1.NONE) { result.center = Cartesian3.clone(billboard.position, result.center); } else { if (!defined(billboard._clampedPosition)) { return BoundingSphereState$1.PENDING; } result.center = Cartesian3.clone(billboard._clampedPosition, result.center); } result.radius = 0; return BoundingSphereState$1.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ BillboardVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ BillboardVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( BillboardVisualizer.prototype._onCollectionChanged, this ); var entities = this._entityCollection.values; for (var i = 0; i < entities.length; i++) { this._cluster.removeBillboard(entities[i]); } return destroyObject(this); }; BillboardVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var items = this._items; var cluster = this._cluster; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._billboard) && defined(entity._position)) { items.set(entity.id, new EntityData$3(entity)); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._billboard) && defined(entity._position)) { if (!items.contains(entity.id)) { items.set(entity.id, new EntityData$3(entity)); } } else { returnPrimitive$2(items.get(entity.id), entity, cluster); items.remove(entity.id); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; returnPrimitive$2(items.get(entity.id), entity, cluster); items.remove(entity.id); } }; function returnPrimitive$2(item, entity, cluster) { if (defined(item)) { item.billboard = undefined; cluster.removeBillboard(entity); } } //This file is automatically rebuilt by the Cesium build process. var AllMaterialAppearanceFS = "varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec3 v_tangentEC;\n\ varying vec3 v_bitangentEC;\n\ varying vec2 v_st;\n\ \n\ void main()\n\ {\n\ vec3 positionToEyeEC = -v_positionEC;\n\ mat3 tangentToEyeMatrix = czm_tangentToEyeSpaceMatrix(v_normalEC, v_tangentEC, v_bitangentEC);\n\ \n\ vec3 normalEC = normalize(v_normalEC);\n\ #ifdef FACE_FORWARD\n\ normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n\ #endif\n\ \n\ czm_materialInput materialInput;\n\ materialInput.normalEC = normalEC;\n\ materialInput.tangentToEyeMatrix = tangentToEyeMatrix;\n\ materialInput.positionToEyeEC = positionToEyeEC;\n\ materialInput.st = v_st;\n\ czm_material material = czm_getMaterial(materialInput);\n\ \n\ #ifdef FLAT\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #else\n\ gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var AllMaterialAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 normal;\n\ attribute vec3 tangent;\n\ attribute vec3 bitangent;\n\ attribute vec2 st;\n\ attribute float batchId;\n\ \n\ varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec3 v_tangentEC;\n\ varying vec3 v_bitangentEC;\n\ varying vec2 v_st;\n\ \n\ void main()\n\ {\n\ vec4 p = czm_computePosition();\n\ \n\ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates\n\ v_normalEC = czm_normal * normal; // normal in eye coordinates\n\ v_tangentEC = czm_normal * tangent; // tangent in eye coordinates\n\ v_bitangentEC = czm_normal * bitangent; // bitangent in eye coordinates\n\ v_st = st;\n\ \n\ gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var BasicMaterialAppearanceFS = "varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ \n\ void main()\n\ {\n\ vec3 positionToEyeEC = -v_positionEC;\n\ \n\ vec3 normalEC = normalize(v_normalEC);\n\ #ifdef FACE_FORWARD\n\ normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n\ #endif\n\ \n\ czm_materialInput materialInput;\n\ materialInput.normalEC = normalEC;\n\ materialInput.positionToEyeEC = positionToEyeEC;\n\ czm_material material = czm_getMaterial(materialInput);\n\ \n\ #ifdef FLAT\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #else\n\ gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var BasicMaterialAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 normal;\n\ attribute float batchId;\n\ \n\ varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ \n\ void main()\n\ {\n\ vec4 p = czm_computePosition();\n\ \n\ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates\n\ v_normalEC = czm_normal * normal; // normal in eye coordinates\n\ \n\ gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var TexturedMaterialAppearanceFS = "varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec2 v_st;\n\ \n\ void main()\n\ {\n\ vec3 positionToEyeEC = -v_positionEC;\n\ \n\ vec3 normalEC = normalize(v_normalEC);\n\ #ifdef FACE_FORWARD\n\ normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n\ #endif\n\ \n\ czm_materialInput materialInput;\n\ materialInput.normalEC = normalEC;\n\ materialInput.positionToEyeEC = positionToEyeEC;\n\ materialInput.st = v_st;\n\ czm_material material = czm_getMaterial(materialInput);\n\ \n\ #ifdef FLAT\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #else\n\ gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var TexturedMaterialAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 normal;\n\ attribute vec2 st;\n\ attribute float batchId;\n\ \n\ varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec2 v_st;\n\ \n\ void main()\n\ {\n\ vec4 p = czm_computePosition();\n\ \n\ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates\n\ v_normalEC = czm_normal * normal; // normal in eye coordinates\n\ v_st = st;\n\ \n\ gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ }\n\ "; /** * Determines how two pixels' values are combined. * * @enum {Number} */ var BlendEquation = { /** * Pixel values are added componentwise. This is used in additive blending for translucency. * * @type {Number} * @constant */ ADD: WebGLConstants$1.FUNC_ADD, /** * Pixel values are subtracted componentwise (source - destination). This is used in alpha blending for translucency. * * @type {Number} * @constant */ SUBTRACT: WebGLConstants$1.FUNC_SUBTRACT, /** * Pixel values are subtracted componentwise (destination - source). * * @type {Number} * @constant */ REVERSE_SUBTRACT: WebGLConstants$1.FUNC_REVERSE_SUBTRACT, /** * Pixel values are given to the minimum function (min(source, destination)). * * This equation operates on each pixel color component. * * @type {Number} * @constant */ MIN: WebGLConstants$1.MIN, /** * Pixel values are given to the maximum function (max(source, destination)). * * This equation operates on each pixel color component. * * @type {Number} * @constant */ MAX: WebGLConstants$1.MAX, }; var BlendEquation$1 = Object.freeze(BlendEquation); /** * Determines how blending factors are computed. * * @enum {Number} */ var BlendFunction = { /** * The blend factor is zero. * * @type {Number} * @constant */ ZERO: WebGLConstants$1.ZERO, /** * The blend factor is one. * * @type {Number} * @constant */ ONE: WebGLConstants$1.ONE, /** * The blend factor is the source color. * * @type {Number} * @constant */ SOURCE_COLOR: WebGLConstants$1.SRC_COLOR, /** * The blend factor is one minus the source color. * * @type {Number} * @constant */ ONE_MINUS_SOURCE_COLOR: WebGLConstants$1.ONE_MINUS_SRC_COLOR, /** * The blend factor is the destination color. * * @type {Number} * @constant */ DESTINATION_COLOR: WebGLConstants$1.DST_COLOR, /** * The blend factor is one minus the destination color. * * @type {Number} * @constant */ ONE_MINUS_DESTINATION_COLOR: WebGLConstants$1.ONE_MINUS_DST_COLOR, /** * The blend factor is the source alpha. * * @type {Number} * @constant */ SOURCE_ALPHA: WebGLConstants$1.SRC_ALPHA, /** * The blend factor is one minus the source alpha. * * @type {Number} * @constant */ ONE_MINUS_SOURCE_ALPHA: WebGLConstants$1.ONE_MINUS_SRC_ALPHA, /** * The blend factor is the destination alpha. * * @type {Number} * @constant */ DESTINATION_ALPHA: WebGLConstants$1.DST_ALPHA, /** * The blend factor is one minus the destination alpha. * * @type {Number} * @constant */ ONE_MINUS_DESTINATION_ALPHA: WebGLConstants$1.ONE_MINUS_DST_ALPHA, /** * The blend factor is the constant color. * * @type {Number} * @constant */ CONSTANT_COLOR: WebGLConstants$1.CONSTANT_COLOR, /** * The blend factor is one minus the constant color. * * @type {Number} * @constant */ ONE_MINUS_CONSTANT_COLOR: WebGLConstants$1.ONE_MINUS_CONSTANT_COLOR, /** * The blend factor is the constant alpha. * * @type {Number} * @constant */ CONSTANT_ALPHA: WebGLConstants$1.CONSTANT_ALPHA, /** * The blend factor is one minus the constant alpha. * * @type {Number} * @constant */ ONE_MINUS_CONSTANT_ALPHA: WebGLConstants$1.ONE_MINUS_CONSTANT_ALPHA, /** * The blend factor is the saturated source alpha. * * @type {Number} * @constant */ SOURCE_ALPHA_SATURATE: WebGLConstants$1.SRC_ALPHA_SATURATE, }; var BlendFunction$1 = Object.freeze(BlendFunction); /** * The blending state combines {@link BlendEquation} and {@link BlendFunction} and the * enabled flag to define the full blending state for combining source and * destination fragments when rendering. *

* This is a helper when using custom render states with {@link Appearance#renderState}. *

* * @namespace */ var BlendingState = { /** * Blending is disabled. * * @type {Object} * @constant */ DISABLED: Object.freeze({ enabled: false, }), /** * Blending is enabled using alpha blending, source(source.alpha) + destination(1 - source.alpha). * * @type {Object} * @constant */ ALPHA_BLEND: Object.freeze({ enabled: true, equationRgb: BlendEquation$1.ADD, equationAlpha: BlendEquation$1.ADD, functionSourceRgb: BlendFunction$1.SOURCE_ALPHA, functionSourceAlpha: BlendFunction$1.ONE, functionDestinationRgb: BlendFunction$1.ONE_MINUS_SOURCE_ALPHA, functionDestinationAlpha: BlendFunction$1.ONE_MINUS_SOURCE_ALPHA, }), /** * Blending is enabled using alpha blending with premultiplied alpha, source + destination(1 - source.alpha). * * @type {Object} * @constant */ PRE_MULTIPLIED_ALPHA_BLEND: Object.freeze({ enabled: true, equationRgb: BlendEquation$1.ADD, equationAlpha: BlendEquation$1.ADD, functionSourceRgb: BlendFunction$1.ONE, functionSourceAlpha: BlendFunction$1.ONE, functionDestinationRgb: BlendFunction$1.ONE_MINUS_SOURCE_ALPHA, functionDestinationAlpha: BlendFunction$1.ONE_MINUS_SOURCE_ALPHA, }), /** * Blending is enabled using additive blending, source(source.alpha) + destination. * * @type {Object} * @constant */ ADDITIVE_BLEND: Object.freeze({ enabled: true, equationRgb: BlendEquation$1.ADD, equationAlpha: BlendEquation$1.ADD, functionSourceRgb: BlendFunction$1.SOURCE_ALPHA, functionSourceAlpha: BlendFunction$1.ONE, functionDestinationRgb: BlendFunction$1.ONE, functionDestinationAlpha: BlendFunction$1.ONE, }), }; var BlendingState$1 = Object.freeze(BlendingState); /** * Determines which triangles, if any, are culled. * * @enum {Number} */ var CullFace = { /** * Front-facing triangles are culled. * * @type {Number} * @constant */ FRONT: WebGLConstants$1.FRONT, /** * Back-facing triangles are culled. * * @type {Number} * @constant */ BACK: WebGLConstants$1.BACK, /** * Both front-facing and back-facing triangles are culled. * * @type {Number} * @constant */ FRONT_AND_BACK: WebGLConstants$1.FRONT_AND_BACK, }; var CullFace$1 = Object.freeze(CullFace); /** * An appearance defines the full GLSL vertex and fragment shaders and the * render state used to draw a {@link Primitive}. All appearances implement * this base Appearance interface. * * @alias Appearance * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link Appearance#renderState} has alpha blending enabled. * @param {Boolean} [options.closed=false] When true, the geometry is expected to be closed so {@link Appearance#renderState} has backface culling enabled. * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color. * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {Object} [options.renderState] Optional render state to override the default render state. * * @see MaterialAppearance * @see EllipsoidSurfaceAppearance * @see PerInstanceColorAppearance * @see DebugAppearance * @see PolylineColorAppearance * @see PolylineMaterialAppearance * * @demo {@link https://sandcastle.cesium.com/index.html?src=Geometry%20and%20Appearances.html|Geometry and Appearances Demo} */ function Appearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The material used to determine the fragment color. Unlike other {@link Appearance} * properties, this is not read-only, so an appearance's material can change on the fly. * * @type Material * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} */ this.material = options.material; /** * When true, the geometry is expected to appear translucent. * * @type {Boolean} * * @default true */ this.translucent = defaultValue(options.translucent, true); this._vertexShaderSource = options.vertexShaderSource; this._fragmentShaderSource = options.fragmentShaderSource; this._renderState = options.renderState; this._closed = defaultValue(options.closed, false); } Object.defineProperties(Appearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof Appearance.prototype * * @type {String} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * The GLSL source code for the fragment shader. The full fragment shader * source is built procedurally taking into account the {@link Appearance#material}. * Use {@link Appearance#getFragmentShaderSource} to get the full source. * * @memberof Appearance.prototype * * @type {String} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. * * @memberof Appearance.prototype * * @type {Object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed. * * @memberof Appearance.prototype * * @type {Boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, }); /** * Procedurally creates the full GLSL fragment shader source for this appearance * taking into account {@link Appearance#fragmentShaderSource} and {@link Appearance#material}. * * @returns {String} The full GLSL fragment shader source. */ Appearance.prototype.getFragmentShaderSource = function () { var parts = []; if (this.flat) { parts.push("#define FLAT"); } if (this.faceForward) { parts.push("#define FACE_FORWARD"); } if (defined(this.material)) { parts.push(this.material.shaderSource); } parts.push(this.fragmentShaderSource); return parts.join("\n"); }; /** * Determines if the geometry is translucent based on {@link Appearance#translucent} and {@link Material#isTranslucent}. * * @returns {Boolean} true if the appearance is translucent. */ Appearance.prototype.isTranslucent = function () { return ( (defined(this.material) && this.material.isTranslucent()) || (!defined(this.material) && this.translucent) ); }; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @returns {Object} The render state. */ Appearance.prototype.getRenderState = function () { var translucent = this.isTranslucent(); var rs = clone$1(this.renderState, false); if (translucent) { rs.depthMask = false; rs.blending = BlendingState$1.ALPHA_BLEND; } else { rs.depthMask = true; } return rs; }; /** * @private */ Appearance.getDefaultRenderState = function (translucent, closed, existing) { var rs = { depthTest: { enabled: true, }, }; if (translucent) { rs.depthMask = false; rs.blending = BlendingState$1.ALPHA_BLEND; } if (closed) { rs.cull = { enabled: true, face: CullFace$1.BACK, }; } if (defined(existing)) { rs = combine$2(existing, rs, true); } return rs; }; /** * @private */ var ContextLimits = { _maximumCombinedTextureImageUnits: 0, _maximumCubeMapSize: 0, _maximumFragmentUniformVectors: 0, _maximumTextureImageUnits: 0, _maximumRenderbufferSize: 0, _maximumTextureSize: 0, _maximumVaryingVectors: 0, _maximumVertexAttributes: 0, _maximumVertexTextureImageUnits: 0, _maximumVertexUniformVectors: 0, _minimumAliasedLineWidth: 0, _maximumAliasedLineWidth: 0, _minimumAliasedPointSize: 0, _maximumAliasedPointSize: 0, _maximumViewportWidth: 0, _maximumViewportHeight: 0, _maximumTextureFilterAnisotropy: 0, _maximumDrawBuffers: 0, _maximumColorAttachments: 0, _highpFloatSupported: false, _highpIntSupported: false, }; Object.defineProperties(ContextLimits, { /** * The maximum number of texture units that can be used from the vertex and fragment * shader with this WebGL implementation. The minimum is eight. If both shaders access the * same texture unit, this counts as two texture units. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_COMBINED_TEXTURE_IMAGE_UNITS. */ maximumCombinedTextureImageUnits: { get: function () { return ContextLimits._maximumCombinedTextureImageUnits; }, }, /** * The approximate maximum cube mape width and height supported by this WebGL implementation. * The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_CUBE_MAP_TEXTURE_SIZE. */ maximumCubeMapSize: { get: function () { return ContextLimits._maximumCubeMapSize; }, }, /** * The maximum number of vec4, ivec4, and bvec4 * uniforms that can be used by a fragment shader with this WebGL implementation. The minimum is 16. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_FRAGMENT_UNIFORM_VECTORS. */ maximumFragmentUniformVectors: { get: function () { return ContextLimits._maximumFragmentUniformVectors; }, }, /** * The maximum number of texture units that can be used from the fragment shader with this WebGL implementation. The minimum is eight. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_TEXTURE_IMAGE_UNITS. */ maximumTextureImageUnits: { get: function () { return ContextLimits._maximumTextureImageUnits; }, }, /** * The maximum renderbuffer width and height supported by this WebGL implementation. * The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_RENDERBUFFER_SIZE. */ maximumRenderbufferSize: { get: function () { return ContextLimits._maximumRenderbufferSize; }, }, /** * The approximate maximum texture width and height supported by this WebGL implementation. * The minimum is 64, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_TEXTURE_SIZE. */ maximumTextureSize: { get: function () { return ContextLimits._maximumTextureSize; }, }, /** * The maximum number of vec4 varying variables supported by this WebGL implementation. * The minimum is eight. Matrices and arrays count as multiple vec4s. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VARYING_VECTORS. */ maximumVaryingVectors: { get: function () { return ContextLimits._maximumVaryingVectors; }, }, /** * The maximum number of vec4 vertex attributes supported by this WebGL implementation. The minimum is eight. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_ATTRIBS. */ maximumVertexAttributes: { get: function () { return ContextLimits._maximumVertexAttributes; }, }, /** * The maximum number of texture units that can be used from the vertex shader with this WebGL implementation. * The minimum is zero, which means the GL does not support vertex texture fetch. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_TEXTURE_IMAGE_UNITS. */ maximumVertexTextureImageUnits: { get: function () { return ContextLimits._maximumVertexTextureImageUnits; }, }, /** * The maximum number of vec4, ivec4, and bvec4 * uniforms that can be used by a vertex shader with this WebGL implementation. The minimum is 16. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_UNIFORM_VECTORS. */ maximumVertexUniformVectors: { get: function () { return ContextLimits._maximumVertexUniformVectors; }, }, /** * The minimum aliased line width, in pixels, supported by this WebGL implementation. It will be at most one. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_LINE_WIDTH_RANGE. */ minimumAliasedLineWidth: { get: function () { return ContextLimits._minimumAliasedLineWidth; }, }, /** * The maximum aliased line width, in pixels, supported by this WebGL implementation. It will be at least one. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_LINE_WIDTH_RANGE. */ maximumAliasedLineWidth: { get: function () { return ContextLimits._maximumAliasedLineWidth; }, }, /** * The minimum aliased point size, in pixels, supported by this WebGL implementation. It will be at most one. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_POINT_SIZE_RANGE. */ minimumAliasedPointSize: { get: function () { return ContextLimits._minimumAliasedPointSize; }, }, /** * The maximum aliased point size, in pixels, supported by this WebGL implementation. It will be at least one. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_POINT_SIZE_RANGE. */ maximumAliasedPointSize: { get: function () { return ContextLimits._maximumAliasedPointSize; }, }, /** * The maximum supported width of the viewport. It will be at least as large as the visible width of the associated canvas. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VIEWPORT_DIMS. */ maximumViewportWidth: { get: function () { return ContextLimits._maximumViewportWidth; }, }, /** * The maximum supported height of the viewport. It will be at least as large as the visible height of the associated canvas. * @memberof ContextLimits * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VIEWPORT_DIMS. */ maximumViewportHeight: { get: function () { return ContextLimits._maximumViewportHeight; }, }, /** * The maximum degree of anisotropy for texture filtering * @memberof ContextLimits * @type {Number} */ maximumTextureFilterAnisotropy: { get: function () { return ContextLimits._maximumTextureFilterAnisotropy; }, }, /** * The maximum number of simultaneous outputs that may be written in a fragment shader. * @memberof ContextLimits * @type {Number} */ maximumDrawBuffers: { get: function () { return ContextLimits._maximumDrawBuffers; }, }, /** * The maximum number of color attachments supported. * @memberof ContextLimits * @type {Number} */ maximumColorAttachments: { get: function () { return ContextLimits._maximumColorAttachments; }, }, /** * High precision float supported (highp) in fragment shaders. * @memberof ContextLimits * @type {Boolean} */ highpFloatSupported: { get: function () { return ContextLimits._highpFloatSupported; }, }, /** * High precision int supported (highp) in fragment shaders. * @memberof ContextLimits * @type {Boolean} */ highpIntSupported: { get: function () { return ContextLimits._highpIntSupported; }, }, }); /** * @private */ function CubeMapFace( context, texture, textureTarget, targetFace, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ) { this._context = context; this._texture = texture; this._textureTarget = textureTarget; this._targetFace = targetFace; this._pixelDatatype = pixelDatatype; this._internalFormat = internalFormat; this._pixelFormat = pixelFormat; this._size = size; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; this._initialized = initialized; } Object.defineProperties(CubeMapFace.prototype, { pixelFormat: { get: function () { return this._pixelFormat; }, }, pixelDatatype: { get: function () { return this._pixelDatatype; }, }, _target: { get: function () { return this._targetFace; }, }, }); /** * Copies texels from the source to the cubemap's face. * * @param {Object} source The source ImageData, HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, or an object with a width, height, and typed array as shown in the example. * @param {Number} [xOffset=0] An offset in the x direction in the cubemap where copying begins. * @param {Number} [yOffset=0] An offset in the y direction in the cubemap where copying begins. * * @exception {DeveloperError} xOffset must be greater than or equal to zero. * @exception {DeveloperError} yOffset must be greater than or equal to zero. * @exception {DeveloperError} xOffset + source.width must be less than or equal to width. * @exception {DeveloperError} yOffset + source.height must be less than or equal to height. * @exception {DeveloperError} This CubeMap was destroyed, i.e., destroy() was called. * * @example * // Create a cubemap with 1x1 faces, and make the +x face red. * var cubeMap = new CubeMap({ * context : context * width : 1, * height : 1 * }); * cubeMap.positiveX.copyFrom({ * width : 1, * height : 1, * arrayBufferView : new Uint8Array([255, 0, 0, 255]) * }); */ CubeMapFace.prototype.copyFrom = function (source, xOffset, yOffset) { xOffset = defaultValue(xOffset, 0); yOffset = defaultValue(yOffset, 0); //>>includeStart('debug', pragmas.debug); Check.defined("source", source); Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0); Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0); if (xOffset + source.width > this._size) { throw new DeveloperError( "xOffset + source.width must be less than or equal to width." ); } if (yOffset + source.height > this._size) { throw new DeveloperError( "yOffset + source.height must be less than or equal to height." ); } //>>includeEnd('debug'); var gl = this._context._gl; var target = this._textureTarget; var targetFace = this._targetFace; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); var width = source.width; var height = source.height; var arrayBufferView = source.arrayBufferView; var size = this._size; var pixelFormat = this._pixelFormat; var internalFormat = this._internalFormat; var pixelDatatype = this._pixelDatatype; var preMultiplyAlpha = this._preMultiplyAlpha; var flipY = this._flipY; var unpackAlignment = 4; if (defined(arrayBufferView)) { unpackAlignment = PixelFormat$1.alignmentInBytes( pixelFormat, pixelDatatype, width ); } gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); var uploaded = false; if (!this._initialized) { if (xOffset === 0 && yOffset === 0 && width === size && height === size) { // initialize the entire texture if (defined(arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, size, size ); } gl.texImage2D( targetFace, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, this._context), arrayBufferView ); } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); gl.texImage2D( targetFace, 0, internalFormat, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, this._context), source ); } uploaded = true; } else { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); // initialize the entire texture to zero var bufferView = PixelFormat$1.createTypedArray( pixelFormat, pixelDatatype, size, size ); gl.texImage2D( targetFace, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, this._context), bufferView ); } this._initialized = true; } if (!uploaded) { if (defined(arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, width, height ); } gl.texSubImage2D( targetFace, 0, xOffset, yOffset, width, height, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, this._context), arrayBufferView ); } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement gl.texSubImage2D( targetFace, 0, xOffset, yOffset, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, this._context), source ); } } gl.bindTexture(target, null); }; /** * Copies texels from the framebuffer to the cubemap's face. * * @param {Number} [xOffset=0] An offset in the x direction in the cubemap where copying begins. * @param {Number} [yOffset=0] An offset in the y direction in the cubemap where copying begins. * @param {Number} [framebufferXOffset=0] An offset in the x direction in the framebuffer where copying begins from. * @param {Number} [framebufferYOffset=0] An offset in the y direction in the framebuffer where copying begins from. * @param {Number} [width=CubeMap's width] The width of the subimage to copy. * @param {Number} [height=CubeMap's height] The height of the subimage to copy. * * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT. * @exception {DeveloperError} This CubeMap was destroyed, i.e., destroy() was called. * @exception {DeveloperError} xOffset must be greater than or equal to zero. * @exception {DeveloperError} yOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero. * @exception {DeveloperError} xOffset + source.width must be less than or equal to width. * @exception {DeveloperError} yOffset + source.height must be less than or equal to height. * @exception {DeveloperError} This CubeMap was destroyed, i.e., destroy() was called. * * @example * // Copy the framebuffer contents to the +x cube map face. * cubeMap.positiveX.copyFromFramebuffer(); */ CubeMapFace.prototype.copyFromFramebuffer = function ( xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height ) { xOffset = defaultValue(xOffset, 0); yOffset = defaultValue(yOffset, 0); framebufferXOffset = defaultValue(framebufferXOffset, 0); framebufferYOffset = defaultValue(framebufferYOffset, 0); width = defaultValue(width, this._size); height = defaultValue(height, this._size); //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0); Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0); Check.typeOf.number.greaterThanOrEquals( "framebufferXOffset", framebufferXOffset, 0 ); Check.typeOf.number.greaterThanOrEquals( "framebufferYOffset", framebufferYOffset, 0 ); if (xOffset + width > this._size) { throw new DeveloperError( "xOffset + source.width must be less than or equal to width." ); } if (yOffset + height > this._size) { throw new DeveloperError( "yOffset + source.height must be less than or equal to height." ); } if (this._pixelDatatype === PixelDatatype$1.FLOAT) { throw new DeveloperError( "Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT." ); } if (this._pixelDatatype === PixelDatatype$1.HALF_FLOAT) { throw new DeveloperError( "Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT." ); } //>>includeEnd('debug'); var gl = this._context._gl; var target = this._textureTarget; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.copyTexSubImage2D( this._targetFace, 0, xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height ); gl.bindTexture(target, null); this._initialized = true; }; /** * @private */ var MipmapHint = { DONT_CARE: WebGLConstants$1.DONT_CARE, FASTEST: WebGLConstants$1.FASTEST, NICEST: WebGLConstants$1.NICEST, validate: function (mipmapHint) { return ( mipmapHint === MipmapHint.DONT_CARE || mipmapHint === MipmapHint.FASTEST || mipmapHint === MipmapHint.NICEST ); }, }; var MipmapHint$1 = Object.freeze(MipmapHint); /** * Enumerates all possible filters used when magnifying WebGL textures. * * @enum {Number} * * @see TextureMinificationFilter */ var TextureMagnificationFilter = { /** * Samples the texture by returning the closest pixel. * * @type {Number} * @constant */ NEAREST: WebGLConstants$1.NEAREST, /** * Samples the texture through bi-linear interpolation of the four nearest pixels. This produces smoother results than NEAREST filtering. * * @type {Number} * @constant */ LINEAR: WebGLConstants$1.LINEAR, }; /** * Validates the given textureMinificationFilter with respect to the possible enum values. * @param textureMagnificationFilter * @returns {Boolean} true if textureMagnificationFilter is valid. * * @private */ TextureMagnificationFilter.validate = function (textureMagnificationFilter) { return ( textureMagnificationFilter === TextureMagnificationFilter.NEAREST || textureMagnificationFilter === TextureMagnificationFilter.LINEAR ); }; var TextureMagnificationFilter$1 = Object.freeze(TextureMagnificationFilter); /** * Enumerates all possible filters used when minifying WebGL textures. * * @enum {Number} * * @see TextureMagnificationFilter */ var TextureMinificationFilter = { /** * Samples the texture by returning the closest pixel. * * @type {Number} * @constant */ NEAREST: WebGLConstants$1.NEAREST, /** * Samples the texture through bi-linear interpolation of the four nearest pixels. This produces smoother results than NEAREST filtering. * * @type {Number} * @constant */ LINEAR: WebGLConstants$1.LINEAR, /** * Selects the nearest mip level and applies nearest sampling within that level. *

* Requires that the texture has a mipmap. The mip level is chosen by the view angle and screen-space size of the texture. *

* * @type {Number} * @constant */ NEAREST_MIPMAP_NEAREST: WebGLConstants$1.NEAREST_MIPMAP_NEAREST, /** * Selects the nearest mip level and applies linear sampling within that level. *

* Requires that the texture has a mipmap. The mip level is chosen by the view angle and screen-space size of the texture. *

* * @type {Number} * @constant */ LINEAR_MIPMAP_NEAREST: WebGLConstants$1.LINEAR_MIPMAP_NEAREST, /** * Read texture values with nearest sampling from two adjacent mip levels and linearly interpolate the results. *

* This option provides a good balance of visual quality and speed when sampling from a mipmapped texture. *

*

* Requires that the texture has a mipmap. The mip level is chosen by the view angle and screen-space size of the texture. *

* * @type {Number} * @constant */ NEAREST_MIPMAP_LINEAR: WebGLConstants$1.NEAREST_MIPMAP_LINEAR, /** * Read texture values with linear sampling from two adjacent mip levels and linearly interpolate the results. *

* This option provides a good balance of visual quality and speed when sampling from a mipmapped texture. *

*

* Requires that the texture has a mipmap. The mip level is chosen by the view angle and screen-space size of the texture. *

* @type {Number} * @constant */ LINEAR_MIPMAP_LINEAR: WebGLConstants$1.LINEAR_MIPMAP_LINEAR, }; /** * Validates the given textureMinificationFilter with respect to the possible enum values. * * @private * * @param textureMinificationFilter * @returns {Boolean} true if textureMinificationFilter is valid. */ TextureMinificationFilter.validate = function (textureMinificationFilter) { return ( textureMinificationFilter === TextureMinificationFilter.NEAREST || textureMinificationFilter === TextureMinificationFilter.LINEAR || textureMinificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST || textureMinificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST || textureMinificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR || textureMinificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR ); }; var TextureMinificationFilter$1 = Object.freeze(TextureMinificationFilter); /** * @private */ var TextureWrap = { CLAMP_TO_EDGE: WebGLConstants$1.CLAMP_TO_EDGE, REPEAT: WebGLConstants$1.REPEAT, MIRRORED_REPEAT: WebGLConstants$1.MIRRORED_REPEAT, validate: function (textureWrap) { return ( textureWrap === TextureWrap.CLAMP_TO_EDGE || textureWrap === TextureWrap.REPEAT || textureWrap === TextureWrap.MIRRORED_REPEAT ); }, }; var TextureWrap$1 = Object.freeze(TextureWrap); /** * @private */ function Sampler(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var wrapS = defaultValue(options.wrapS, TextureWrap$1.CLAMP_TO_EDGE); var wrapT = defaultValue(options.wrapT, TextureWrap$1.CLAMP_TO_EDGE); var minificationFilter = defaultValue( options.minificationFilter, TextureMinificationFilter$1.LINEAR ); var magnificationFilter = defaultValue( options.magnificationFilter, TextureMagnificationFilter$1.LINEAR ); var maximumAnisotropy = defined(options.maximumAnisotropy) ? options.maximumAnisotropy : 1.0; //>>includeStart('debug', pragmas.debug); if (!TextureWrap$1.validate(wrapS)) { throw new DeveloperError("Invalid sampler.wrapS."); } if (!TextureWrap$1.validate(wrapT)) { throw new DeveloperError("Invalid sampler.wrapT."); } if (!TextureMinificationFilter$1.validate(minificationFilter)) { throw new DeveloperError("Invalid sampler.minificationFilter."); } if (!TextureMagnificationFilter$1.validate(magnificationFilter)) { throw new DeveloperError("Invalid sampler.magnificationFilter."); } Check.typeOf.number.greaterThanOrEquals( "maximumAnisotropy", maximumAnisotropy, 1.0 ); //>>includeEnd('debug'); this._wrapS = wrapS; this._wrapT = wrapT; this._minificationFilter = minificationFilter; this._magnificationFilter = magnificationFilter; this._maximumAnisotropy = maximumAnisotropy; } Object.defineProperties(Sampler.prototype, { wrapS: { get: function () { return this._wrapS; }, }, wrapT: { get: function () { return this._wrapT; }, }, minificationFilter: { get: function () { return this._minificationFilter; }, }, magnificationFilter: { get: function () { return this._magnificationFilter; }, }, maximumAnisotropy: { get: function () { return this._maximumAnisotropy; }, }, }); Sampler.equals = function (left, right) { return ( left === right || (defined(left) && defined(right) && left._wrapS === right._wrapS && left._wrapT === right._wrapT && left._minificationFilter === right._minificationFilter && left._magnificationFilter === right._magnificationFilter && left._maximumAnisotropy === right._maximumAnisotropy) ); }; Sampler.NEAREST = Object.freeze( new Sampler({ wrapS: TextureWrap$1.CLAMP_TO_EDGE, wrapT: TextureWrap$1.CLAMP_TO_EDGE, minificationFilter: TextureMinificationFilter$1.NEAREST, magnificationFilter: TextureMagnificationFilter$1.NEAREST, }) ); /** * @private */ function CubeMap(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); var context = options.context; var source = options.source; var width; var height; if (defined(source)) { var faces = [ source.positiveX, source.negativeX, source.positiveY, source.negativeY, source.positiveZ, source.negativeZ, ]; //>>includeStart('debug', pragmas.debug); if ( !faces[0] || !faces[1] || !faces[2] || !faces[3] || !faces[4] || !faces[5] ) { throw new DeveloperError( "options.source requires positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ faces." ); } //>>includeEnd('debug'); width = faces[0].width; height = faces[0].height; //>>includeStart('debug', pragmas.debug); for (var i = 1; i < 6; ++i) { if ( Number(faces[i].width) !== width || Number(faces[i].height) !== height ) { throw new DeveloperError( "Each face in options.source must have the same width and height." ); } } //>>includeEnd('debug'); } else { width = options.width; height = options.height; } var size = width; var pixelDatatype = defaultValue( options.pixelDatatype, PixelDatatype$1.UNSIGNED_BYTE ); var pixelFormat = defaultValue(options.pixelFormat, PixelFormat$1.RGBA); var internalFormat = PixelFormat$1.toInternalFormat( pixelFormat, pixelDatatype, context ); //>>includeStart('debug', pragmas.debug); if (!defined(width) || !defined(height)) { throw new DeveloperError( "options requires a source field to create an initialized cube map or width and height fields to create a blank cube map." ); } if (width !== height) { throw new DeveloperError("Width must equal height."); } if (size <= 0) { throw new DeveloperError("Width and height must be greater than zero."); } if (size > ContextLimits.maximumCubeMapSize) { throw new DeveloperError( "Width and height must be less than or equal to the maximum cube map size (" + ContextLimits.maximumCubeMapSize + "). Check maximumCubeMapSize." ); } if (!PixelFormat$1.validate(pixelFormat)) { throw new DeveloperError("Invalid options.pixelFormat."); } if (PixelFormat$1.isDepthFormat(pixelFormat)) { throw new DeveloperError( "options.pixelFormat cannot be DEPTH_COMPONENT or DEPTH_STENCIL." ); } if (!PixelDatatype$1.validate(pixelDatatype)) { throw new DeveloperError("Invalid options.pixelDatatype."); } if (pixelDatatype === PixelDatatype$1.FLOAT && !context.floatingPointTexture) { throw new DeveloperError( "When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension." ); } if ( pixelDatatype === PixelDatatype$1.HALF_FLOAT && !context.halfFloatingPointTexture ) { throw new DeveloperError( "When options.pixelDatatype is HALF_FLOAT, this WebGL implementation must support the OES_texture_half_float extension." ); } //>>includeEnd('debug'); var sizeInBytes = PixelFormat$1.textureSizeInBytes(pixelFormat, pixelDatatype, size, size) * 6; // Use premultiplied alpha for opaque textures should perform better on Chrome: // http://media.tojicode.com/webglCamp4/#20 var preMultiplyAlpha = options.preMultiplyAlpha || pixelFormat === PixelFormat$1.RGB || pixelFormat === PixelFormat$1.LUMINANCE; var flipY = defaultValue(options.flipY, true); var gl = context._gl; var textureTarget = gl.TEXTURE_CUBE_MAP; var texture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(textureTarget, texture); function createFace(target, sourceFace, preMultiplyAlpha, flipY) { var arrayBufferView = sourceFace.arrayBufferView; if (!defined(arrayBufferView)) { arrayBufferView = sourceFace.bufferView; } var unpackAlignment = 4; if (defined(arrayBufferView)) { unpackAlignment = PixelFormat$1.alignmentInBytes( pixelFormat, pixelDatatype, width ); } gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); if (defined(arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, size, size ); } gl.texImage2D( target, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), arrayBufferView ); } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement gl.texImage2D( target, 0, internalFormat, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), sourceFace ); } } if (defined(source)) { createFace( gl.TEXTURE_CUBE_MAP_POSITIVE_X, source.positiveX, preMultiplyAlpha, flipY ); createFace( gl.TEXTURE_CUBE_MAP_NEGATIVE_X, source.negativeX, preMultiplyAlpha, flipY ); createFace( gl.TEXTURE_CUBE_MAP_POSITIVE_Y, source.positiveY, preMultiplyAlpha, flipY ); createFace( gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, source.negativeY, preMultiplyAlpha, flipY ); createFace( gl.TEXTURE_CUBE_MAP_POSITIVE_Z, source.positiveZ, preMultiplyAlpha, flipY ); createFace( gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, source.negativeZ, preMultiplyAlpha, flipY ); } else { gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); gl.texImage2D( gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); gl.texImage2D( gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); gl.texImage2D( gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, internalFormat, size, size, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); } gl.bindTexture(textureTarget, null); this._context = context; this._textureFilterAnisotropic = context._textureFilterAnisotropic; this._textureTarget = textureTarget; this._texture = texture; this._pixelFormat = pixelFormat; this._pixelDatatype = pixelDatatype; this._size = size; this._hasMipmap = false; this._sizeInBytes = sizeInBytes; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; this._sampler = undefined; var initialized = defined(source); this._positiveX = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_X, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this._negativeX = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this._positiveY = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this._negativeY = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this._positiveZ = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this._negativeZ = new CubeMapFace( context, texture, textureTarget, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, internalFormat, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY, initialized ); this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); } Object.defineProperties(CubeMap.prototype, { positiveX: { get: function () { return this._positiveX; }, }, negativeX: { get: function () { return this._negativeX; }, }, positiveY: { get: function () { return this._positiveY; }, }, negativeY: { get: function () { return this._negativeY; }, }, positiveZ: { get: function () { return this._positiveZ; }, }, negativeZ: { get: function () { return this._negativeZ; }, }, sampler: { get: function () { return this._sampler; }, set: function (sampler) { var minificationFilter = sampler.minificationFilter; var magnificationFilter = sampler.magnificationFilter; var mipmap = minificationFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST || minificationFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_LINEAR || minificationFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_NEAREST || minificationFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_LINEAR; var context = this._context; var pixelDatatype = this._pixelDatatype; // float textures only support nearest filtering unless the linear extensions are supported, so override the sampler's settings if ( (pixelDatatype === PixelDatatype$1.FLOAT && !context.textureFloatLinear) || (pixelDatatype === PixelDatatype$1.HALF_FLOAT && !context.textureHalfFloatLinear) ) { minificationFilter = mipmap ? TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST : TextureMinificationFilter$1.NEAREST; magnificationFilter = TextureMagnificationFilter$1.NEAREST; } var gl = context._gl; var target = this._textureTarget; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter); gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter); gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS); gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT); if (defined(this._textureFilterAnisotropic)) { gl.texParameteri( target, this._textureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, sampler.maximumAnisotropy ); } gl.bindTexture(target, null); this._sampler = sampler; }, }, pixelFormat: { get: function () { return this._pixelFormat; }, }, pixelDatatype: { get: function () { return this._pixelDatatype; }, }, width: { get: function () { return this._size; }, }, height: { get: function () { return this._size; }, }, sizeInBytes: { get: function () { if (this._hasMipmap) { return Math.floor((this._sizeInBytes * 4) / 3); } return this._sizeInBytes; }, }, preMultiplyAlpha: { get: function () { return this._preMultiplyAlpha; }, }, flipY: { get: function () { return this._flipY; }, }, _target: { get: function () { return this._textureTarget; }, }, }); /** * Generates a complete mipmap chain for each cubemap face. * * @param {MipmapHint} [hint=MipmapHint.DONT_CARE] A performance vs. quality hint. * * @exception {DeveloperError} hint is invalid. * @exception {DeveloperError} This CubeMap's width must be a power of two to call generateMipmap(). * @exception {DeveloperError} This CubeMap's height must be a power of two to call generateMipmap(). * @exception {DeveloperError} This CubeMap was destroyed, i.e., destroy() was called. * * @example * // Generate mipmaps, and then set the sampler so mipmaps are used for * // minification when the cube map is sampled. * cubeMap.generateMipmap(); * cubeMap.sampler = new Sampler({ * minificationFilter : Cesium.TextureMinificationFilter.NEAREST_MIPMAP_LINEAR * }); */ CubeMap.prototype.generateMipmap = function (hint) { hint = defaultValue(hint, MipmapHint$1.DONT_CARE); //>>includeStart('debug', pragmas.debug); if (this._size > 1 && !CesiumMath.isPowerOfTwo(this._size)) { throw new DeveloperError( "width and height must be a power of two to call generateMipmap()." ); } if (!MipmapHint$1.validate(hint)) { throw new DeveloperError("hint is invalid."); } //>>includeEnd('debug'); this._hasMipmap = true; var gl = this._context._gl; var target = this._textureTarget; gl.hint(gl.GENERATE_MIPMAP_HINT, hint); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.generateMipmap(target); gl.bindTexture(target, null); }; CubeMap.prototype.isDestroyed = function () { return false; }; CubeMap.prototype.destroy = function () { this._context._gl.deleteTexture(this._texture); this._positiveX = destroyObject(this._positiveX); this._negativeX = destroyObject(this._negativeX); this._positiveY = destroyObject(this._positiveY); this._negativeY = destroyObject(this._negativeY); this._positiveZ = destroyObject(this._positiveZ); this._negativeZ = destroyObject(this._negativeZ); return destroyObject(this); }; /** * @private */ function Texture(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); var context = options.context; var width = options.width; var height = options.height; var source = options.source; if (defined(source)) { if (!defined(width)) { width = defaultValue(source.videoWidth, source.width); } if (!defined(height)) { height = defaultValue(source.videoHeight, source.height); } } var pixelFormat = defaultValue(options.pixelFormat, PixelFormat$1.RGBA); var pixelDatatype = defaultValue( options.pixelDatatype, PixelDatatype$1.UNSIGNED_BYTE ); var internalFormat = PixelFormat$1.toInternalFormat( pixelFormat, pixelDatatype, context ); var isCompressed = PixelFormat$1.isCompressedFormat(internalFormat); //>>includeStart('debug', pragmas.debug); if (!defined(width) || !defined(height)) { throw new DeveloperError( "options requires a source field to create an initialized texture or width and height fields to create a blank texture." ); } Check.typeOf.number.greaterThan("width", width, 0); if (width > ContextLimits.maximumTextureSize) { throw new DeveloperError( "Width must be less than or equal to the maximum texture size (" + ContextLimits.maximumTextureSize + "). Check maximumTextureSize." ); } Check.typeOf.number.greaterThan("height", height, 0); if (height > ContextLimits.maximumTextureSize) { throw new DeveloperError( "Height must be less than or equal to the maximum texture size (" + ContextLimits.maximumTextureSize + "). Check maximumTextureSize." ); } if (!PixelFormat$1.validate(pixelFormat)) { throw new DeveloperError("Invalid options.pixelFormat."); } if (!isCompressed && !PixelDatatype$1.validate(pixelDatatype)) { throw new DeveloperError("Invalid options.pixelDatatype."); } if ( pixelFormat === PixelFormat$1.DEPTH_COMPONENT && pixelDatatype !== PixelDatatype$1.UNSIGNED_SHORT && pixelDatatype !== PixelDatatype$1.UNSIGNED_INT ) { throw new DeveloperError( "When options.pixelFormat is DEPTH_COMPONENT, options.pixelDatatype must be UNSIGNED_SHORT or UNSIGNED_INT." ); } if ( pixelFormat === PixelFormat$1.DEPTH_STENCIL && pixelDatatype !== PixelDatatype$1.UNSIGNED_INT_24_8 ) { throw new DeveloperError( "When options.pixelFormat is DEPTH_STENCIL, options.pixelDatatype must be UNSIGNED_INT_24_8." ); } if (pixelDatatype === PixelDatatype$1.FLOAT && !context.floatingPointTexture) { throw new DeveloperError( "When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension. Check context.floatingPointTexture." ); } if ( pixelDatatype === PixelDatatype$1.HALF_FLOAT && !context.halfFloatingPointTexture ) { throw new DeveloperError( "When options.pixelDatatype is HALF_FLOAT, this WebGL implementation must support the OES_texture_half_float extension. Check context.halfFloatingPointTexture." ); } if (PixelFormat$1.isDepthFormat(pixelFormat)) { if (defined(source)) { throw new DeveloperError( "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, source cannot be provided." ); } if (!context.depthTexture) { throw new DeveloperError( "When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, this WebGL implementation must support WEBGL_depth_texture. Check context.depthTexture." ); } } if (isCompressed) { if (!defined(source) || !defined(source.arrayBufferView)) { throw new DeveloperError( "When options.pixelFormat is compressed, options.source.arrayBufferView must be defined." ); } if (PixelFormat$1.isDXTFormat(internalFormat) && !context.s3tc) { throw new DeveloperError( "When options.pixelFormat is S3TC compressed, this WebGL implementation must support the WEBGL_texture_compression_s3tc extension. Check context.s3tc." ); } else if (PixelFormat$1.isPVRTCFormat(internalFormat) && !context.pvrtc) { throw new DeveloperError( "When options.pixelFormat is PVRTC compressed, this WebGL implementation must support the WEBGL_texture_compression_pvrtc extension. Check context.pvrtc." ); } else if (PixelFormat$1.isETC1Format(internalFormat) && !context.etc1) { throw new DeveloperError( "When options.pixelFormat is ETC1 compressed, this WebGL implementation must support the WEBGL_texture_compression_etc1 extension. Check context.etc1." ); } if ( PixelFormat$1.compressedTextureSizeInBytes( internalFormat, width, height ) !== source.arrayBufferView.byteLength ) { throw new DeveloperError( "The byte length of the array buffer is invalid for the compressed texture with the given width and height." ); } } //>>includeEnd('debug'); // Use premultiplied alpha for opaque textures should perform better on Chrome: // http://media.tojicode.com/webglCamp4/#20 var preMultiplyAlpha = options.preMultiplyAlpha || pixelFormat === PixelFormat$1.RGB || pixelFormat === PixelFormat$1.LUMINANCE; var flipY = defaultValue(options.flipY, true); var initialized = true; var gl = context._gl; var textureTarget = gl.TEXTURE_2D; var texture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(textureTarget, texture); var unpackAlignment = 4; if (defined(source) && defined(source.arrayBufferView) && !isCompressed) { unpackAlignment = PixelFormat$1.alignmentInBytes( pixelFormat, pixelDatatype, width ); } gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); if (defined(source)) { if (defined(source.arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); // Source: typed array var arrayBufferView = source.arrayBufferView; if (isCompressed) { gl.compressedTexImage2D( textureTarget, 0, internalFormat, width, height, 0, arrayBufferView ); } else { if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, width, height ); } gl.texImage2D( textureTarget, 0, internalFormat, width, height, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), arrayBufferView ); if (defined(source.mipLevels)) { var mipWidth = width; var mipHeight = height; for (var i = 0; i < source.mipLevels.length; ++i) { mipWidth = Math.floor(mipWidth / 2) | 0; if (mipWidth < 1) { mipWidth = 1; } mipHeight = Math.floor(mipHeight / 2) | 0; if (mipHeight < 1) { mipHeight = 1; } gl.texImage2D( textureTarget, i + 1, internalFormat, mipWidth, mipHeight, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), source.mipLevels[i] ); } } } } else if (defined(source.framebuffer)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); // Source: framebuffer if (source.framebuffer !== context.defaultFramebuffer) { source.framebuffer._bind(); } gl.copyTexImage2D( textureTarget, 0, internalFormat, source.xOffset, source.yOffset, width, height, 0 ); if (source.framebuffer !== context.defaultFramebuffer) { source.framebuffer._unBind(); } } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); // Source: ImageData, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement gl.texImage2D( textureTarget, 0, internalFormat, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), source ); } } else { gl.texImage2D( textureTarget, 0, internalFormat, width, height, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), null ); initialized = false; } gl.bindTexture(textureTarget, null); var sizeInBytes; if (isCompressed) { sizeInBytes = PixelFormat$1.compressedTextureSizeInBytes( pixelFormat, width, height ); } else { sizeInBytes = PixelFormat$1.textureSizeInBytes( pixelFormat, pixelDatatype, width, height ); } this._id = createGuid(); this._context = context; this._textureFilterAnisotropic = context._textureFilterAnisotropic; this._textureTarget = textureTarget; this._texture = texture; this._internalFormat = internalFormat; this._pixelFormat = pixelFormat; this._pixelDatatype = pixelDatatype; this._width = width; this._height = height; this._dimensions = new Cartesian2(width, height); this._hasMipmap = false; this._sizeInBytes = sizeInBytes; this._preMultiplyAlpha = preMultiplyAlpha; this._flipY = flipY; this._initialized = initialized; this._sampler = undefined; this.sampler = defined(options.sampler) ? options.sampler : new Sampler(); } /** * This function is identical to using the Texture constructor except that it can be * replaced with a mock/spy in tests. * @private */ Texture.create = function (options) { return new Texture(options); }; /** * Creates a texture, and copies a subimage of the framebuffer to it. When called without arguments, * the texture is the same width and height as the framebuffer and contains its contents. * * @param {Object} options Object with the following properties: * @param {Context} options.context The context in which the Texture gets created. * @param {PixelFormat} [options.pixelFormat=PixelFormat.RGB] The texture's internal pixel format. * @param {Number} [options.framebufferXOffset=0] An offset in the x direction in the framebuffer where copying begins from. * @param {Number} [options.framebufferYOffset=0] An offset in the y direction in the framebuffer where copying begins from. * @param {Number} [options.width=canvas.clientWidth] The width of the texture in texels. * @param {Number} [options.height=canvas.clientHeight] The height of the texture in texels. * @param {Framebuffer} [options.framebuffer=defaultFramebuffer] The framebuffer from which to create the texture. If this * parameter is not specified, the default framebuffer is used. * @returns {Texture} A texture with contents from the framebuffer. * * @exception {DeveloperError} Invalid pixelFormat. * @exception {DeveloperError} pixelFormat cannot be DEPTH_COMPONENT, DEPTH_STENCIL or a compressed format. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferXOffset + width must be less than or equal to canvas.clientWidth. * @exception {DeveloperError} framebufferYOffset + height must be less than or equal to canvas.clientHeight. * * * @example * // Create a texture with the contents of the framebuffer. * var t = Texture.fromFramebuffer({ * context : context * }); * * @see Sampler * * @private */ Texture.fromFramebuffer = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); var context = options.context; var gl = context._gl; var pixelFormat = defaultValue(options.pixelFormat, PixelFormat$1.RGB); var framebufferXOffset = defaultValue(options.framebufferXOffset, 0); var framebufferYOffset = defaultValue(options.framebufferYOffset, 0); var width = defaultValue(options.width, gl.drawingBufferWidth); var height = defaultValue(options.height, gl.drawingBufferHeight); var framebuffer = options.framebuffer; //>>includeStart('debug', pragmas.debug); if (!PixelFormat$1.validate(pixelFormat)) { throw new DeveloperError("Invalid pixelFormat."); } if ( PixelFormat$1.isDepthFormat(pixelFormat) || PixelFormat$1.isCompressedFormat(pixelFormat) ) { throw new DeveloperError( "pixelFormat cannot be DEPTH_COMPONENT, DEPTH_STENCIL or a compressed format." ); } Check.defined("options.context", options.context); Check.typeOf.number.greaterThanOrEquals( "framebufferXOffset", framebufferXOffset, 0 ); Check.typeOf.number.greaterThanOrEquals( "framebufferYOffset", framebufferYOffset, 0 ); if (framebufferXOffset + width > gl.drawingBufferWidth) { throw new DeveloperError( "framebufferXOffset + width must be less than or equal to drawingBufferWidth" ); } if (framebufferYOffset + height > gl.drawingBufferHeight) { throw new DeveloperError( "framebufferYOffset + height must be less than or equal to drawingBufferHeight." ); } //>>includeEnd('debug'); var texture = new Texture({ context: context, width: width, height: height, pixelFormat: pixelFormat, source: { framebuffer: defined(framebuffer) ? framebuffer : context.defaultFramebuffer, xOffset: framebufferXOffset, yOffset: framebufferYOffset, width: width, height: height, }, }); return texture; }; Object.defineProperties(Texture.prototype, { /** * A unique id for the texture * @memberof Texture.prototype * @type {String} * @readonly * @private */ id: { get: function () { return this._id; }, }, /** * The sampler to use when sampling this texture. * Create a sampler by calling {@link Sampler}. If this * parameter is not specified, a default sampler is used. The default sampler clamps texture * coordinates in both directions, uses linear filtering for both magnification and minification, * and uses a maximum anisotropy of 1.0. * @memberof Texture.prototype * @type {Object} */ sampler: { get: function () { return this._sampler; }, set: function (sampler) { var minificationFilter = sampler.minificationFilter; var magnificationFilter = sampler.magnificationFilter; var context = this._context; var pixelFormat = this._pixelFormat; var pixelDatatype = this._pixelDatatype; var mipmap = minificationFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST || minificationFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_LINEAR || minificationFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_NEAREST || minificationFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_LINEAR; // float textures only support nearest filtering unless the linear extensions are supported, so override the sampler's settings if ( (pixelDatatype === PixelDatatype$1.FLOAT && !context.textureFloatLinear) || (pixelDatatype === PixelDatatype$1.HALF_FLOAT && !context.textureHalfFloatLinear) ) { minificationFilter = mipmap ? TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST : TextureMinificationFilter$1.NEAREST; magnificationFilter = TextureMagnificationFilter$1.NEAREST; } // WebGL 2 depth texture only support nearest filtering. See section 3.8.13 OpenGL ES 3 spec if (context.webgl2) { if (PixelFormat$1.isDepthFormat(pixelFormat)) { minificationFilter = TextureMinificationFilter$1.NEAREST; magnificationFilter = TextureMagnificationFilter$1.NEAREST; } } var gl = context._gl; var target = this._textureTarget; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, minificationFilter); gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magnificationFilter); gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS); gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT); if (defined(this._textureFilterAnisotropic)) { gl.texParameteri( target, this._textureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, sampler.maximumAnisotropy ); } gl.bindTexture(target, null); this._sampler = sampler; }, }, pixelFormat: { get: function () { return this._pixelFormat; }, }, pixelDatatype: { get: function () { return this._pixelDatatype; }, }, dimensions: { get: function () { return this._dimensions; }, }, preMultiplyAlpha: { get: function () { return this._preMultiplyAlpha; }, }, flipY: { get: function () { return this._flipY; }, }, width: { get: function () { return this._width; }, }, height: { get: function () { return this._height; }, }, sizeInBytes: { get: function () { if (this._hasMipmap) { return Math.floor((this._sizeInBytes * 4) / 3); } return this._sizeInBytes; }, }, _target: { get: function () { return this._textureTarget; }, }, }); /** * Copy new image data into this texture, from a source {@link ImageData}, {@link HTMLImageElement}, {@link HTMLCanvasElement}, or {@link HTMLVideoElement}. * or an object with width, height, and arrayBufferView properties. * * @param {Object} source The source {@link ImageData}, {@link HTMLImageElement}, {@link HTMLCanvasElement}, or {@link HTMLVideoElement}, * or an object with width, height, and arrayBufferView properties. * @param {Number} [xOffset=0] The offset in the x direction within the texture to copy into. * @param {Number} [yOffset=0] The offset in the y direction within the texture to copy into. * * @exception {DeveloperError} Cannot call copyFrom when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL. * @exception {DeveloperError} Cannot call copyFrom with a compressed texture pixel format. * @exception {DeveloperError} xOffset must be greater than or equal to zero. * @exception {DeveloperError} yOffset must be greater than or equal to zero. * @exception {DeveloperError} xOffset + source.width must be less than or equal to width. * @exception {DeveloperError} yOffset + source.height must be less than or equal to height. * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called. * * @example * texture.copyFrom({ * width : 1, * height : 1, * arrayBufferView : new Uint8Array([255, 0, 0, 255]) * }); */ Texture.prototype.copyFrom = function (source, xOffset, yOffset) { xOffset = defaultValue(xOffset, 0); yOffset = defaultValue(yOffset, 0); //>>includeStart('debug', pragmas.debug); Check.defined("source", source); if (PixelFormat$1.isDepthFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call copyFrom when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL." ); } if (PixelFormat$1.isCompressedFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call copyFrom with a compressed texture pixel format." ); } Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0); Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0); Check.typeOf.number.lessThanOrEquals( "xOffset + source.width", xOffset + source.width, this._width ); Check.typeOf.number.lessThanOrEquals( "yOffset + source.height", yOffset + source.height, this._height ); //>>includeEnd('debug'); var context = this._context; var gl = context._gl; var target = this._textureTarget; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); var width = source.width; var height = source.height; var arrayBufferView = source.arrayBufferView; var textureWidth = this._width; var textureHeight = this._height; var internalFormat = this._internalFormat; var pixelFormat = this._pixelFormat; var pixelDatatype = this._pixelDatatype; var preMultiplyAlpha = this._preMultiplyAlpha; var flipY = this._flipY; var unpackAlignment = 4; if (defined(arrayBufferView)) { unpackAlignment = PixelFormat$1.alignmentInBytes( pixelFormat, pixelDatatype, width ); } gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); var uploaded = false; if (!this._initialized) { if ( xOffset === 0 && yOffset === 0 && width === textureWidth && height === textureHeight ) { // initialize the entire texture if (defined(arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, textureWidth, textureHeight ); } gl.texImage2D( target, 0, internalFormat, textureWidth, textureHeight, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), arrayBufferView ); } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); gl.texImage2D( target, 0, internalFormat, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), source ); } uploaded = true; } else { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); // initialize the entire texture to zero var bufferView = PixelFormat$1.createTypedArray( pixelFormat, pixelDatatype, textureWidth, textureHeight ); gl.texImage2D( target, 0, internalFormat, textureWidth, textureHeight, 0, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), bufferView ); } this._initialized = true; } if (!uploaded) { if (defined(arrayBufferView)) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); if (flipY) { arrayBufferView = PixelFormat$1.flipY( arrayBufferView, pixelFormat, pixelDatatype, width, height ); } gl.texSubImage2D( target, 0, xOffset, yOffset, width, height, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), arrayBufferView ); } else { // Only valid for DOM-Element uploads gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); gl.texSubImage2D( target, 0, xOffset, yOffset, pixelFormat, PixelDatatype$1.toWebGLConstant(pixelDatatype, context), source ); } } gl.bindTexture(target, null); }; /** * @param {Number} [xOffset=0] The offset in the x direction within the texture to copy into. * @param {Number} [yOffset=0] The offset in the y direction within the texture to copy into. * @param {Number} [framebufferXOffset=0] optional * @param {Number} [framebufferYOffset=0] optional * @param {Number} [width=width] optional * @param {Number} [height=height] optional * * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT. * @exception {DeveloperError} Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT. * @exception {DeveloperError} Cannot call copyFrom with a compressed texture pixel format. * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called. * @exception {DeveloperError} xOffset must be greater than or equal to zero. * @exception {DeveloperError} yOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero. * @exception {DeveloperError} xOffset + width must be less than or equal to width. * @exception {DeveloperError} yOffset + height must be less than or equal to height. */ Texture.prototype.copyFromFramebuffer = function ( xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height ) { xOffset = defaultValue(xOffset, 0); yOffset = defaultValue(yOffset, 0); framebufferXOffset = defaultValue(framebufferXOffset, 0); framebufferYOffset = defaultValue(framebufferYOffset, 0); width = defaultValue(width, this._width); height = defaultValue(height, this._height); //>>includeStart('debug', pragmas.debug); if (PixelFormat$1.isDepthFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call copyFromFramebuffer when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL." ); } if (this._pixelDatatype === PixelDatatype$1.FLOAT) { throw new DeveloperError( "Cannot call copyFromFramebuffer when the texture pixel data type is FLOAT." ); } if (this._pixelDatatype === PixelDatatype$1.HALF_FLOAT) { throw new DeveloperError( "Cannot call copyFromFramebuffer when the texture pixel data type is HALF_FLOAT." ); } if (PixelFormat$1.isCompressedFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call copyFrom with a compressed texture pixel format." ); } Check.typeOf.number.greaterThanOrEquals("xOffset", xOffset, 0); Check.typeOf.number.greaterThanOrEquals("yOffset", yOffset, 0); Check.typeOf.number.greaterThanOrEquals( "framebufferXOffset", framebufferXOffset, 0 ); Check.typeOf.number.greaterThanOrEquals( "framebufferYOffset", framebufferYOffset, 0 ); Check.typeOf.number.lessThanOrEquals( "xOffset + width", xOffset + width, this._width ); Check.typeOf.number.lessThanOrEquals( "yOffset + height", yOffset + height, this._height ); //>>includeEnd('debug'); var gl = this._context._gl; var target = this._textureTarget; gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.copyTexSubImage2D( target, 0, xOffset, yOffset, framebufferXOffset, framebufferYOffset, width, height ); gl.bindTexture(target, null); this._initialized = true; }; /** * @param {MipmapHint} [hint=MipmapHint.DONT_CARE] optional. * * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL. * @exception {DeveloperError} Cannot call generateMipmap when the texture pixel format is a compressed format. * @exception {DeveloperError} hint is invalid. * @exception {DeveloperError} This texture's width must be a power of two to call generateMipmap(). * @exception {DeveloperError} This texture's height must be a power of two to call generateMipmap(). * @exception {DeveloperError} This texture was destroyed, i.e., destroy() was called. */ Texture.prototype.generateMipmap = function (hint) { hint = defaultValue(hint, MipmapHint$1.DONT_CARE); //>>includeStart('debug', pragmas.debug); if (PixelFormat$1.isDepthFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call generateMipmap when the texture pixel format is DEPTH_COMPONENT or DEPTH_STENCIL." ); } if (PixelFormat$1.isCompressedFormat(this._pixelFormat)) { throw new DeveloperError( "Cannot call generateMipmap with a compressed pixel format." ); } if (this._width > 1 && !CesiumMath.isPowerOfTwo(this._width)) { throw new DeveloperError( "width must be a power of two to call generateMipmap()." ); } if (this._height > 1 && !CesiumMath.isPowerOfTwo(this._height)) { throw new DeveloperError( "height must be a power of two to call generateMipmap()." ); } if (!MipmapHint$1.validate(hint)) { throw new DeveloperError("hint is invalid."); } //>>includeEnd('debug'); this._hasMipmap = true; var gl = this._context._gl; var target = this._textureTarget; gl.hint(gl.GENERATE_MIPMAP_HINT, hint); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(target, this._texture); gl.generateMipmap(target); gl.bindTexture(target, null); }; Texture.prototype.isDestroyed = function () { return false; }; Texture.prototype.destroy = function () { this._context._gl.deleteTexture(this._texture); return destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var AspectRampMaterial = "uniform sampler2D image;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ vec4 rampColor = texture2D(image, vec2(materialInput.aspect / (2.0 * czm_pi), 0.5));\n\ rampColor = czm_gammaCorrect(rampColor);\n\ material.diffuse = rampColor.rgb;\n\ material.alpha = rampColor.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var BumpMapMaterial = "uniform sampler2D image;\n\ uniform float strength;\n\ uniform vec2 repeat;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ \n\ vec2 centerPixel = fract(repeat * st);\n\ float centerBump = texture2D(image, centerPixel).channel;\n\ \n\ float imageWidth = float(imageDimensions.x);\n\ vec2 rightPixel = fract(repeat * (st + vec2(1.0 / imageWidth, 0.0)));\n\ float rightBump = texture2D(image, rightPixel).channel;\n\ \n\ float imageHeight = float(imageDimensions.y);\n\ vec2 leftPixel = fract(repeat * (st + vec2(0.0, 1.0 / imageHeight)));\n\ float topBump = texture2D(image, leftPixel).channel;\n\ \n\ vec3 normalTangentSpace = normalize(vec3(centerBump - rightBump, centerBump - topBump, clamp(1.0 - strength, 0.1, 1.0)));\n\ vec3 normalEC = materialInput.tangentToEyeMatrix * normalTangentSpace;\n\ \n\ material.normal = normalEC;\n\ material.diffuse = vec3(0.01);\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var CheckerboardMaterial = "uniform vec4 lightColor;\n\ uniform vec4 darkColor;\n\ uniform vec2 repeat;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ \n\ // From Stefan Gustavson's Procedural Textures in GLSL in OpenGL Insights\n\ float b = mod(floor(repeat.s * st.s) + floor(repeat.t * st.t), 2.0); // 0.0 or 1.0\n\ \n\ // Find the distance from the closest separator (region between two colors)\n\ float scaledWidth = fract(repeat.s * st.s);\n\ scaledWidth = abs(scaledWidth - floor(scaledWidth + 0.5));\n\ float scaledHeight = fract(repeat.t * st.t);\n\ scaledHeight = abs(scaledHeight - floor(scaledHeight + 0.5));\n\ float value = min(scaledWidth, scaledHeight);\n\ \n\ vec4 currentColor = mix(lightColor, darkColor, b);\n\ vec4 color = czm_antialias(lightColor, darkColor, currentColor, value, 0.03);\n\ \n\ color = czm_gammaCorrect(color);\n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var DotMaterial = "uniform vec4 lightColor;\n\ uniform vec4 darkColor;\n\ uniform vec2 repeat;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ // From Stefan Gustavson's Procedural Textures in GLSL in OpenGL Insights\n\ float b = smoothstep(0.3, 0.32, length(fract(repeat * materialInput.st) - 0.5)); // 0.0 or 1.0\n\ \n\ vec4 color = mix(lightColor, darkColor, b);\n\ color = czm_gammaCorrect(color);\n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var ElevationBandMaterial = "uniform sampler2D heights;\n\ uniform sampler2D colors;\n\ \n\ // This material expects heights to be sorted from lowest to highest.\n\ \n\ float getHeight(int idx, float invTexSize)\n\ {\n\ vec2 uv = vec2((float(idx) + 0.5) * invTexSize, 0.5);\n\ #ifdef OES_texture_float\n\ return texture2D(heights, uv).x;\n\ #else\n\ return czm_unpackFloat(texture2D(heights, uv));\n\ #endif\n\ }\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ float height = materialInput.height;\n\ float invTexSize = 1.0 / float(heightsDimensions.x);\n\ \n\ float minHeight = getHeight(0, invTexSize);\n\ float maxHeight = getHeight(heightsDimensions.x - 1, invTexSize);\n\ \n\ // early-out when outside the height range\n\ if (height < minHeight || height > maxHeight) {\n\ material.diffuse = vec3(0.0);\n\ material.alpha = 0.0;\n\ return material;\n\ }\n\ \n\ // Binary search to find heights above and below.\n\ int idxBelow = 0;\n\ int idxAbove = heightsDimensions.x;\n\ float heightBelow = minHeight;\n\ float heightAbove = maxHeight;\n\ \n\ // while loop not allowed, so use for loop with max iterations.\n\ // maxIterations of 16 supports a texture size up to 65536 (2^16).\n\ const int maxIterations = 16;\n\ for (int i = 0; i < maxIterations; i++) {\n\ if (idxBelow >= idxAbove - 1) {\n\ break;\n\ }\n\ \n\ int idxMid = (idxBelow + idxAbove) / 2;\n\ float heightTex = getHeight(idxMid, invTexSize);\n\ \n\ if (height > heightTex) {\n\ idxBelow = idxMid;\n\ heightBelow = heightTex;\n\ } else {\n\ idxAbove = idxMid;\n\ heightAbove = heightTex;\n\ }\n\ }\n\ \n\ float lerper = heightBelow == heightAbove ? 1.0 : (height - heightBelow) / (heightAbove - heightBelow);\n\ vec2 colorUv = vec2(invTexSize * (float(idxBelow) + 0.5 + lerper), 0.5);\n\ vec4 color = texture2D(colors, colorUv);\n\ \n\ // undo preumultiplied alpha\n\ if (color.a > 0.0) \n\ {\n\ color.rgb /= color.a;\n\ }\n\ \n\ color.rgb = czm_gammaCorrect(color.rgb);\n\ \n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var ElevationContourMaterial = "#ifdef GL_OES_standard_derivatives\n\ #extension GL_OES_standard_derivatives : enable\n\ #endif\n\ \n\ uniform vec4 color;\n\ uniform float spacing;\n\ uniform float width;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ float distanceToContour = mod(materialInput.height, spacing);\n\ \n\ #ifdef GL_OES_standard_derivatives\n\ float dxc = abs(dFdx(materialInput.height));\n\ float dyc = abs(dFdy(materialInput.height));\n\ float dF = max(dxc, dyc) * czm_pixelRatio * width;\n\ float alpha = (distanceToContour < dF) ? 1.0 : 0.0;\n\ #else\n\ float alpha = (distanceToContour < (czm_pixelRatio * width)) ? 1.0 : 0.0;\n\ #endif\n\ \n\ vec4 outColor = czm_gammaCorrect(vec4(color.rgb, alpha * color.a));\n\ material.diffuse = outColor.rgb;\n\ material.alpha = outColor.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var ElevationRampMaterial = "uniform sampler2D image;\n\ uniform float minimumHeight;\n\ uniform float maximumHeight;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ float scaledHeight = clamp((materialInput.height - minimumHeight) / (maximumHeight - minimumHeight), 0.0, 1.0);\n\ vec4 rampColor = texture2D(image, vec2(scaledHeight, 0.5));\n\ rampColor = czm_gammaCorrect(rampColor);\n\ material.diffuse = rampColor.rgb;\n\ material.alpha = rampColor.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var FadeMaterial = "uniform vec4 fadeInColor;\n\ uniform vec4 fadeOutColor;\n\ uniform float maximumDistance;\n\ uniform bool repeat;\n\ uniform vec2 fadeDirection;\n\ uniform vec2 time;\n\ \n\ float getTime(float t, float coord)\n\ {\n\ float scalar = 1.0 / maximumDistance;\n\ float q = distance(t, coord) * scalar;\n\ if (repeat)\n\ {\n\ float r = distance(t, coord + 1.0) * scalar;\n\ float s = distance(t, coord - 1.0) * scalar;\n\ q = min(min(r, s), q);\n\ }\n\ return clamp(q, 0.0, 1.0);\n\ }\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ float s = getTime(time.x, st.s) * fadeDirection.s;\n\ float t = getTime(time.y, st.t) * fadeDirection.t;\n\ \n\ float u = length(vec2(s, t));\n\ vec4 color = mix(fadeInColor, fadeOutColor, u);\n\ \n\ color = czm_gammaCorrect(color);\n\ material.emission = color.rgb;\n\ material.alpha = color.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var GridMaterial = "#ifdef GL_OES_standard_derivatives\n\ #extension GL_OES_standard_derivatives : enable\n\ #endif\n\ \n\ uniform vec4 color;\n\ uniform float cellAlpha;\n\ uniform vec2 lineCount;\n\ uniform vec2 lineThickness;\n\ uniform vec2 lineOffset;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ \n\ float scaledWidth = fract(lineCount.s * st.s - lineOffset.s);\n\ scaledWidth = abs(scaledWidth - floor(scaledWidth + 0.5));\n\ float scaledHeight = fract(lineCount.t * st.t - lineOffset.t);\n\ scaledHeight = abs(scaledHeight - floor(scaledHeight + 0.5));\n\ \n\ float value;\n\ #ifdef GL_OES_standard_derivatives\n\ // Fuzz Factor - Controls blurriness of lines\n\ const float fuzz = 1.2;\n\ vec2 thickness = (lineThickness * czm_pixelRatio) - 1.0;\n\ \n\ // From \"3D Engine Design for Virtual Globes\" by Cozzi and Ring, Listing 4.13.\n\ vec2 dx = abs(dFdx(st));\n\ vec2 dy = abs(dFdy(st));\n\ vec2 dF = vec2(max(dx.s, dy.s), max(dx.t, dy.t)) * lineCount;\n\ value = min(\n\ smoothstep(dF.s * thickness.s, dF.s * (fuzz + thickness.s), scaledWidth),\n\ smoothstep(dF.t * thickness.t, dF.t * (fuzz + thickness.t), scaledHeight));\n\ #else\n\ // Fuzz Factor - Controls blurriness of lines\n\ const float fuzz = 0.05;\n\ \n\ vec2 range = 0.5 - (lineThickness * 0.05);\n\ value = min(\n\ 1.0 - smoothstep(range.s, range.s + fuzz, scaledWidth),\n\ 1.0 - smoothstep(range.t, range.t + fuzz, scaledHeight));\n\ #endif\n\ \n\ // Edges taken from RimLightingMaterial.glsl\n\ // See http://www.fundza.com/rman_shaders/surface/fake_rim/fake_rim1.html\n\ float dRim = 1.0 - abs(dot(materialInput.normalEC, normalize(materialInput.positionToEyeEC)));\n\ float sRim = smoothstep(0.8, 1.0, dRim);\n\ value *= (1.0 - sRim);\n\ \n\ vec4 halfColor;\n\ halfColor.rgb = color.rgb * 0.5;\n\ halfColor.a = color.a * (1.0 - ((1.0 - cellAlpha) * value));\n\ halfColor = czm_gammaCorrect(halfColor);\n\ material.diffuse = halfColor.rgb;\n\ material.emission = halfColor.rgb;\n\ material.alpha = halfColor.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var NormalMapMaterial = "uniform sampler2D image;\n\ uniform float strength;\n\ uniform vec2 repeat;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec4 textureValue = texture2D(image, fract(repeat * materialInput.st));\n\ vec3 normalTangentSpace = textureValue.channels;\n\ normalTangentSpace.xy = normalTangentSpace.xy * 2.0 - 1.0;\n\ normalTangentSpace.z = clamp(1.0 - strength, 0.1, 1.0);\n\ normalTangentSpace = normalize(normalTangentSpace);\n\ vec3 normalEC = materialInput.tangentToEyeMatrix * normalTangentSpace;\n\ \n\ material.normal = normalEC;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineArrowMaterial = "#ifdef GL_OES_standard_derivatives\n\ #extension GL_OES_standard_derivatives : enable\n\ #endif\n\ \n\ uniform vec4 color;\n\ \n\ float getPointOnLine(vec2 p0, vec2 p1, float x)\n\ {\n\ float slope = (p0.y - p1.y) / (p0.x - p1.x);\n\ return slope * (x - p0.x) + p0.y;\n\ }\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ \n\ #ifdef GL_OES_standard_derivatives\n\ float base = 1.0 - abs(fwidth(st.s)) * 10.0 * czm_pixelRatio;\n\ #else\n\ float base = 0.975; // 2.5% of the line will be the arrow head\n\ #endif\n\ \n\ vec2 center = vec2(1.0, 0.5);\n\ float ptOnUpperLine = getPointOnLine(vec2(base, 1.0), center, st.s);\n\ float ptOnLowerLine = getPointOnLine(vec2(base, 0.0), center, st.s);\n\ \n\ float halfWidth = 0.15;\n\ float s = step(0.5 - halfWidth, st.t);\n\ s *= 1.0 - step(0.5 + halfWidth, st.t);\n\ s *= 1.0 - step(base, st.s);\n\ \n\ float t = step(base, materialInput.st.s);\n\ t *= 1.0 - step(ptOnUpperLine, st.t);\n\ t *= step(ptOnLowerLine, st.t);\n\ \n\ // Find the distance from the closest separator (region between two colors)\n\ float dist;\n\ if (st.s < base)\n\ {\n\ float d1 = abs(st.t - (0.5 - halfWidth));\n\ float d2 = abs(st.t - (0.5 + halfWidth));\n\ dist = min(d1, d2);\n\ }\n\ else\n\ {\n\ float d1 = czm_infinity;\n\ if (st.t < 0.5 - halfWidth && st.t > 0.5 + halfWidth)\n\ {\n\ d1 = abs(st.s - base);\n\ }\n\ float d2 = abs(st.t - ptOnUpperLine);\n\ float d3 = abs(st.t - ptOnLowerLine);\n\ dist = min(min(d1, d2), d3);\n\ }\n\ \n\ vec4 outsideColor = vec4(0.0);\n\ vec4 currentColor = mix(outsideColor, color, clamp(s + t, 0.0, 1.0));\n\ vec4 outColor = czm_antialias(outsideColor, color, currentColor, dist);\n\ \n\ outColor = czm_gammaCorrect(outColor);\n\ material.diffuse = outColor.rgb;\n\ material.alpha = outColor.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineDashMaterial = "uniform vec4 color;\n\ uniform vec4 gapColor;\n\ uniform float dashLength;\n\ uniform float dashPattern;\n\ varying float v_polylineAngle;\n\ \n\ const float maskLength = 16.0;\n\ \n\ mat2 rotate(float rad) {\n\ float c = cos(rad);\n\ float s = sin(rad);\n\ return mat2(\n\ c, s,\n\ -s, c\n\ );\n\ }\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy;\n\ \n\ // Get the relative position within the dash from 0 to 1\n\ float dashPosition = fract(pos.x / (dashLength * czm_pixelRatio));\n\ // Figure out the mask index.\n\ float maskIndex = floor(dashPosition * maskLength);\n\ // Test the bit mask.\n\ float maskTest = floor(dashPattern / pow(2.0, maskIndex));\n\ vec4 fragColor = (mod(maskTest, 2.0) < 1.0) ? gapColor : color;\n\ if (fragColor.a < 0.005) { // matches 0/255 and 1/255\n\ discard;\n\ }\n\ \n\ fragColor = czm_gammaCorrect(fragColor);\n\ material.emission = fragColor.rgb;\n\ material.alpha = fragColor.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineGlowMaterial = "uniform vec4 color;\n\ uniform float glowPower;\n\ uniform float taperPower;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ float glow = glowPower / abs(st.t - 0.5) - (glowPower / 0.5);\n\ \n\ if (taperPower <= 0.99999) {\n\ glow *= min(1.0, taperPower / (0.5 - st.s * 0.5) - (taperPower / 0.5));\n\ }\n\ \n\ vec4 fragColor;\n\ fragColor.rgb = max(vec3(glow - 1.0 + color.rgb), color.rgb);\n\ fragColor.a = clamp(0.0, 1.0, glow) * color.a;\n\ fragColor = czm_gammaCorrect(fragColor);\n\ \n\ material.emission = fragColor.rgb;\n\ material.alpha = fragColor.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineOutlineMaterial = "uniform vec4 color;\n\ uniform vec4 outlineColor;\n\ uniform float outlineWidth;\n\ \n\ varying float v_width;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ vec2 st = materialInput.st;\n\ float halfInteriorWidth = 0.5 * (v_width - outlineWidth) / v_width;\n\ float b = step(0.5 - halfInteriorWidth, st.t);\n\ b *= 1.0 - step(0.5 + halfInteriorWidth, st.t);\n\ \n\ // Find the distance from the closest separator (region between two colors)\n\ float d1 = abs(st.t - (0.5 - halfInteriorWidth));\n\ float d2 = abs(st.t - (0.5 + halfInteriorWidth));\n\ float dist = min(d1, d2);\n\ \n\ vec4 currentColor = mix(outlineColor, color, b);\n\ vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist);\n\ outColor = czm_gammaCorrect(outColor);\n\ \n\ material.diffuse = outColor.rgb;\n\ material.alpha = outColor.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var RimLightingMaterial = "uniform vec4 color;\n\ uniform vec4 rimColor;\n\ uniform float width;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ // See http://www.fundza.com/rman_shaders/surface/fake_rim/fake_rim1.html\n\ float d = 1.0 - dot(materialInput.normalEC, normalize(materialInput.positionToEyeEC));\n\ float s = smoothstep(1.0 - width, 1.0, d);\n\ \n\ vec4 outColor = czm_gammaCorrect(color);\n\ vec4 outRimColor = czm_gammaCorrect(rimColor);\n\ \n\ material.diffuse = outColor.rgb;\n\ material.emission = outRimColor.rgb * s;\n\ material.alpha = mix(outColor.a, outRimColor.a, s);\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var SlopeRampMaterial = "uniform sampler2D image;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ vec4 rampColor = texture2D(image, vec2(materialInput.slope / (czm_pi / 2.0), 0.5));\n\ rampColor = czm_gammaCorrect(rampColor);\n\ material.diffuse = rampColor.rgb;\n\ material.alpha = rampColor.a;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var StripeMaterial = "uniform vec4 evenColor;\n\ uniform vec4 oddColor;\n\ uniform float offset;\n\ uniform float repeat;\n\ uniform bool horizontal;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ // Based on the Stripes Fragment Shader in the Orange Book (11.1.2)\n\ float coord = mix(materialInput.st.s, materialInput.st.t, float(horizontal));\n\ float value = fract((coord - offset) * (repeat * 0.5));\n\ float dist = min(value, min(abs(value - 0.5), 1.0 - value));\n\ \n\ vec4 currentColor = mix(evenColor, oddColor, step(0.5, value));\n\ vec4 color = czm_antialias(evenColor, oddColor, currentColor, dist);\n\ color = czm_gammaCorrect(color);\n\ \n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ \n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var WaterMaterial = "// Thanks for the contribution Jonas\n\ // http://29a.ch/2012/7/19/webgl-terrain-rendering-water-fog\n\ \n\ uniform sampler2D specularMap;\n\ uniform sampler2D normalMap;\n\ uniform vec4 baseWaterColor;\n\ uniform vec4 blendColor;\n\ uniform float frequency;\n\ uniform float animationSpeed;\n\ uniform float amplitude;\n\ uniform float specularIntensity;\n\ uniform float fadeFactor;\n\ \n\ czm_material czm_getMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ \n\ float time = czm_frameNumber * animationSpeed;\n\ \n\ // fade is a function of the distance from the fragment and the frequency of the waves\n\ float fade = max(1.0, (length(materialInput.positionToEyeEC) / 10000000000.0) * frequency * fadeFactor);\n\ \n\ float specularMapValue = texture2D(specularMap, materialInput.st).r;\n\ \n\ // note: not using directional motion at this time, just set the angle to 0.0;\n\ vec4 noise = czm_getWaterNoise(normalMap, materialInput.st * frequency, time, 0.0);\n\ vec3 normalTangentSpace = noise.xyz * vec3(1.0, 1.0, (1.0 / amplitude));\n\ \n\ // fade out the normal perturbation as we move further from the water surface\n\ normalTangentSpace.xy /= fade;\n\ \n\ // attempt to fade out the normal perturbation as we approach non water areas (low specular map value)\n\ normalTangentSpace = mix(vec3(0.0, 0.0, 50.0), normalTangentSpace, specularMapValue);\n\ \n\ normalTangentSpace = normalize(normalTangentSpace);\n\ \n\ // get ratios for alignment of the new normal vector with a vector perpendicular to the tangent plane\n\ float tsPerturbationRatio = clamp(dot(normalTangentSpace, vec3(0.0, 0.0, 1.0)), 0.0, 1.0);\n\ \n\ // fade out water effect as specular map value decreases\n\ material.alpha = mix(blendColor.a, baseWaterColor.a, specularMapValue) * specularMapValue;\n\ \n\ // base color is a blend of the water and non-water color based on the value from the specular map\n\ // may need a uniform blend factor to better control this\n\ material.diffuse = mix(blendColor.rgb, baseWaterColor.rgb, specularMapValue);\n\ \n\ // diffuse highlights are based on how perturbed the normal is\n\ material.diffuse += (0.1 * tsPerturbationRatio);\n\ \n\ material.diffuse = material.diffuse;\n\ \n\ material.normal = normalize(materialInput.tangentToEyeMatrix * normalTangentSpace);\n\ \n\ material.specular = specularIntensity;\n\ material.shininess = 10.0;\n\ \n\ return material;\n\ }\n\ "; /** * A Material defines surface appearance through a combination of diffuse, specular, * normal, emission, and alpha components. These values are specified using a * JSON schema called Fabric which gets parsed and assembled into glsl shader code * behind-the-scenes. Check out the {@link https://github.com/CesiumGS/cesium/wiki/Fabric|wiki page} * for more details on Fabric. *

* * * Base material types and their uniforms: *
*
    *
  • Color
  • *
      *
    • color: rgba color object.
    • *
    *
  • Image
  • *
      *
    • image: path to image.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    *
  • DiffuseMap
  • *
      *
    • image: path to image.
    • *
    • channels: Three character string containing any combination of r, g, b, and a for selecting the desired image channels.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    *
  • AlphaMap
  • *
      *
    • image: path to image.
    • *
    • channel: One character string containing r, g, b, or a for selecting the desired image channel.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    *
  • SpecularMap
  • *
      *
    • image: path to image.
    • *
    • channel: One character string containing r, g, b, or a for selecting the desired image channel.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    *
  • EmissionMap
  • *
      *
    • image: path to image.
    • *
    • channels: Three character string containing any combination of r, g, b, and a for selecting the desired image channels.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    *
  • BumpMap
  • *
      *
    • image: path to image.
    • *
    • channel: One character string containing r, g, b, or a for selecting the desired image channel.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    • strength: Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.
    • *
    *
  • NormalMap
  • *
      *
    • image: path to image.
    • *
    • channels: Three character string containing any combination of r, g, b, and a for selecting the desired image channels.
    • *
    • repeat: Object with x and y values specifying the number of times to repeat the image.
    • *
    • strength: Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.
    • *
    *
  • Grid
  • *
      *
    • color: rgba color object for the whole material.
    • *
    • cellAlpha: Alpha value for the cells between grid lines. This will be combined with color.alpha.
    • *
    • lineCount: Object with x and y values specifying the number of columns and rows respectively.
    • *
    • lineThickness: Object with x and y values specifying the thickness of grid lines (in pixels where available).
    • *
    • lineOffset: Object with x and y values specifying the offset of grid lines (range is 0 to 1).
    • *
    *
  • Stripe
  • *
      *
    • horizontal: Boolean that determines if the stripes are horizontal or vertical.
    • *
    • evenColor: rgba color object for the stripe's first color.
    • *
    • oddColor: rgba color object for the stripe's second color.
    • *
    • offset: Number that controls at which point into the pattern to begin drawing; with 0.0 being the beginning of the even color, 1.0 the beginning of the odd color, 2.0 being the even color again, and any multiple or fractional values being in between.
    • *
    • repeat: Number that controls the total number of stripes, half light and half dark.
    • *
    *
  • Checkerboard
  • *
      *
    • lightColor: rgba color object for the checkerboard's light alternating color.
    • *
    • darkColor: rgba color object for the checkerboard's dark alternating color.
    • *
    • repeat: Object with x and y values specifying the number of columns and rows respectively.
    • *
    *
  • Dot
  • *
      *
    • lightColor: rgba color object for the dot color.
    • *
    • darkColor: rgba color object for the background color.
    • *
    • repeat: Object with x and y values specifying the number of columns and rows of dots respectively.
    • *
    *
  • Water
  • *
      *
    • baseWaterColor: rgba color object base color of the water.
    • *
    • blendColor: rgba color object used when blending from water to non-water areas.
    • *
    • specularMap: Single channel texture used to indicate areas of water.
    • *
    • normalMap: Normal map for water normal perturbation.
    • *
    • frequency: Number that controls the number of waves.
    • *
    • normalMap: Normal map for water normal perturbation.
    • *
    • animationSpeed: Number that controls the animations speed of the water.
    • *
    • amplitude: Number that controls the amplitude of water waves.
    • *
    • specularIntensity: Number that controls the intensity of specular reflections.
    • *
    *
  • RimLighting
  • *
      *
    • color: diffuse color and alpha.
    • *
    • rimColor: diffuse color and alpha of the rim.
    • *
    • width: Number that determines the rim's width.
    • *
    *
  • Fade
  • *
      *
    • fadeInColor: diffuse color and alpha at time
    • *
    • fadeOutColor: diffuse color and alpha at maximumDistance from time
    • *
    • maximumDistance: Number between 0.0 and 1.0 where the fadeInColor becomes the fadeOutColor. A value of 0.0 gives the entire material a color of fadeOutColor and a value of 1.0 gives the the entire material a color of fadeInColor
    • *
    • repeat: true if the fade should wrap around the texture coodinates.
    • *
    • fadeDirection: Object with x and y values specifying if the fade should be in the x and y directions.
    • *
    • time: Object with x and y values between 0.0 and 1.0 of the fadeInColor position
    • *
    *
  • PolylineArrow
  • *
      *
    • color: diffuse color and alpha.
    • *
    *
  • PolylineDash
  • *
      *
    • color: color for the line.
    • *
    • gapColor: color for the gaps in the line.
    • *
    • dashLength: Dash length in pixels.
    • *
    • dashPattern: The 16 bit stipple pattern for the line..
    • *
    *
  • PolylineGlow
  • *
      *
    • color: color and maximum alpha for the glow on the line.
    • *
    • glowPower: strength of the glow, as a percentage of the total line width (less than 1.0).
    • *
    • taperPower: strength of the tapering effect, as a percentage of the total line length. If 1.0 or higher, no taper effect is used.
    • *
    *
  • PolylineOutline
  • *
      *
    • color: diffuse color and alpha for the interior of the line.
    • *
    • outlineColor: diffuse color and alpha for the outline.
    • *
    • outlineWidth: width of the outline in pixels.
    • *
    *
  • ElevationContour
  • *
      *
    • color: color and alpha for the contour line.
    • *
    • spacing: spacing for contour lines in meters.
    • *
    • width: Number specifying the width of the grid lines in pixels.
    • *
    *
  • ElevationRamp
  • *
      *
    • image: color ramp image to use for coloring the terrain.
    • *
    • minimumHeight: minimum height for the ramp.
    • *
    • maximumHeight: maximum height for the ramp.
    • *
    *
  • SlopeRamp
  • *
      *
    • image: color ramp image to use for coloring the terrain by slope.
    • *
    *
  • AspectRamp
  • *
      *
    • image: color ramp image to use for color the terrain by aspect.
    • *
    *
  • ElevationBand
  • *
      *
    • heights: image of heights sorted from lowest to highest.
    • *
    • colors: image of colors at the corresponding heights.
    • *
    *
* *
* * @alias Material * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.strict=false] Throws errors for issues that would normally be ignored, including unused uniforms or materials. * @param {Boolean|Function} [options.translucent=true] When true or a function that returns true, the geometry * with this material is expected to appear translucent. * @param {TextureMinificationFilter} [options.minificationFilter=TextureMinificationFilter.LINEAR] The {@link TextureMinificationFilter} to apply to this material's textures. * @param {TextureMagnificationFilter} [options.magnificationFilter=TextureMagnificationFilter.LINEAR] The {@link TextureMagnificationFilter} to apply to this material's textures. * @param {Object} options.fabric The fabric JSON used to generate the material. * * @constructor * * @exception {DeveloperError} fabric: uniform has invalid type. * @exception {DeveloperError} fabric: uniforms and materials cannot share the same property. * @exception {DeveloperError} fabric: cannot have source and components in the same section. * @exception {DeveloperError} fabric: property name is not valid. It should be 'type', 'materials', 'uniforms', 'components', or 'source'. * @exception {DeveloperError} fabric: property name is not valid. It should be 'diffuse', 'specular', 'shininess', 'normal', 'emission', or 'alpha'. * @exception {DeveloperError} strict: shader source does not use string. * @exception {DeveloperError} strict: shader source does not use uniform. * @exception {DeveloperError} strict: shader source does not use material. * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric wiki page} for a more detailed options of Fabric. * * @demo {@link https://sandcastle.cesium.com/index.html?src=Materials.html|Cesium Sandcastle Materials Demo} * * @example * // Create a color material with fromType: * polygon.material = Cesium.Material.fromType('Color'); * polygon.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0); * * // Create the default material: * polygon.material = new Cesium.Material(); * * // Create a color material with full Fabric notation: * polygon.material = new Cesium.Material({ * fabric : { * type : 'Color', * uniforms : { * color : new Cesium.Color(1.0, 1.0, 0.0, 1.0) * } * } * }); */ function Material(options) { /** * The material type. Can be an existing type or a new type. If no type is specified in fabric, type is a GUID. * @type {String} * @default undefined */ this.type = undefined; /** * The glsl shader source for this material. * @type {String} * @default undefined */ this.shaderSource = undefined; /** * Maps sub-material names to Material objects. * @type {Object} * @default undefined */ this.materials = undefined; /** * Maps uniform names to their values. * @type {Object} * @default undefined */ this.uniforms = undefined; this._uniforms = undefined; /** * When true or a function that returns true, * the geometry is expected to appear translucent. * @type {Boolean|Function} * @default undefined */ this.translucent = undefined; this._minificationFilter = defaultValue( options.minificationFilter, TextureMinificationFilter$1.LINEAR ); this._magnificationFilter = defaultValue( options.magnificationFilter, TextureMagnificationFilter$1.LINEAR ); this._strict = undefined; this._template = undefined; this._count = undefined; this._texturePaths = {}; this._loadedImages = []; this._loadedCubeMaps = []; this._textures = {}; this._updateFunctions = []; this._defaultTexture = undefined; initializeMaterial(options, this); Object.defineProperties(this, { type: { value: this.type, writable: false, }, }); if (!defined(Material._uniformList[this.type])) { Material._uniformList[this.type] = Object.keys(this._uniforms); } } // Cached list of combined uniform names indexed by type. // Used to get the list of uniforms in the same order. Material._uniformList = {}; /** * Creates a new material using an existing material type. *

* Shorthand for: new Material({fabric : {type : type}}); * * @param {String} type The base material type. * @param {Object} [uniforms] Overrides for the default uniforms. * @returns {Material} New material object. * * @exception {DeveloperError} material with that type does not exist. * * @example * var material = Cesium.Material.fromType('Color', { * color : new Cesium.Color(1.0, 0.0, 0.0, 1.0) * }); */ Material.fromType = function (type, uniforms) { //>>includeStart('debug', pragmas.debug); if (!defined(Material._materialCache.getMaterial(type))) { throw new DeveloperError( "material with type '" + type + "' does not exist." ); } //>>includeEnd('debug'); var material = new Material({ fabric: { type: type, }, }); if (defined(uniforms)) { for (var name in uniforms) { if (uniforms.hasOwnProperty(name)) { material.uniforms[name] = uniforms[name]; } } } return material; }; /** * Gets whether or not this material is translucent. * @returns {Boolean} true if this material is translucent, false otherwise. */ Material.prototype.isTranslucent = function () { if (defined(this.translucent)) { if (typeof this.translucent === "function") { return this.translucent(); } return this.translucent; } var translucent = true; var funcs = this._translucentFunctions; var length = funcs.length; for (var i = 0; i < length; ++i) { var func = funcs[i]; if (typeof func === "function") { translucent = translucent && func(); } else { translucent = translucent && func; } if (!translucent) { break; } } return translucent; }; /** * @private */ Material.prototype.update = function (context) { var i; var uniformId; var loadedImages = this._loadedImages; var length = loadedImages.length; for (i = 0; i < length; ++i) { var loadedImage = loadedImages[i]; uniformId = loadedImage.id; var image = loadedImage.image; var sampler = new Sampler({ minificationFilter: this._minificationFilter, magnificationFilter: this._magnificationFilter, }); var texture; if (defined(image.internalFormat)) { texture = new Texture({ context: context, pixelFormat: image.internalFormat, width: image.width, height: image.height, source: { arrayBufferView: image.bufferView, }, sampler: sampler, }); } else { texture = new Texture({ context: context, source: image, sampler: sampler, }); } this._textures[uniformId] = texture; var uniformDimensionsName = uniformId + "Dimensions"; if (this.uniforms.hasOwnProperty(uniformDimensionsName)) { var uniformDimensions = this.uniforms[uniformDimensionsName]; uniformDimensions.x = texture._width; uniformDimensions.y = texture._height; } } loadedImages.length = 0; var loadedCubeMaps = this._loadedCubeMaps; length = loadedCubeMaps.length; for (i = 0; i < length; ++i) { var loadedCubeMap = loadedCubeMaps[i]; uniformId = loadedCubeMap.id; var images = loadedCubeMap.images; var cubeMap = new CubeMap({ context: context, source: { positiveX: images[0], negativeX: images[1], positiveY: images[2], negativeY: images[3], positiveZ: images[4], negativeZ: images[5], }, sampler: new Sampler({ minificationFilter: this._minificationFilter, magnificationFilter: this._magnificationFilter, }), }); this._textures[uniformId] = cubeMap; } loadedCubeMaps.length = 0; var updateFunctions = this._updateFunctions; length = updateFunctions.length; for (i = 0; i < length; ++i) { updateFunctions[i](this, context); } var subMaterials = this.materials; for (var name in subMaterials) { if (subMaterials.hasOwnProperty(name)) { subMaterials[name].update(context); } } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see Material#destroy */ Material.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * material = material && material.destroy(); * * @see Material#isDestroyed */ Material.prototype.destroy = function () { var textures = this._textures; for (var texture in textures) { if (textures.hasOwnProperty(texture)) { var instance = textures[texture]; if (instance !== this._defaultTexture) { instance.destroy(); } } } var materials = this.materials; for (var material in materials) { if (materials.hasOwnProperty(material)) { materials[material].destroy(); } } return destroyObject(this); }; function initializeMaterial(options, result) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); result._strict = defaultValue(options.strict, false); result._count = defaultValue(options.count, 0); result._template = clone$1( defaultValue(options.fabric, defaultValue.EMPTY_OBJECT) ); result._template.uniforms = clone$1( defaultValue(result._template.uniforms, defaultValue.EMPTY_OBJECT) ); result._template.materials = clone$1( defaultValue(result._template.materials, defaultValue.EMPTY_OBJECT) ); result.type = defined(result._template.type) ? result._template.type : createGuid(); result.shaderSource = ""; result.materials = {}; result.uniforms = {}; result._uniforms = {}; result._translucentFunctions = []; var translucent; // If the cache contains this material type, build the material template off of the stored template. var cachedMaterial = Material._materialCache.getMaterial(result.type); if (defined(cachedMaterial)) { var template = clone$1(cachedMaterial.fabric, true); result._template = combine$2(result._template, template, true); translucent = cachedMaterial.translucent; } // Make sure the template has no obvious errors. More error checking happens later. checkForTemplateErrors(result); // If the material has a new type, add it to the cache. if (!defined(cachedMaterial)) { Material._materialCache.addMaterial(result.type, result); } createMethodDefinition(result); createUniforms(result); createSubMaterials(result); var defaultTranslucent = result._translucentFunctions.length === 0 ? true : undefined; translucent = defaultValue(translucent, defaultTranslucent); translucent = defaultValue(options.translucent, translucent); if (defined(translucent)) { if (typeof translucent === "function") { var wrappedTranslucent = function () { return translucent(result); }; result._translucentFunctions.push(wrappedTranslucent); } else { result._translucentFunctions.push(translucent); } } } function checkForValidProperties(object, properties, result, throwNotFound) { if (defined(object)) { for (var property in object) { if (object.hasOwnProperty(property)) { var hasProperty = properties.indexOf(property) !== -1; if ( (throwNotFound && !hasProperty) || (!throwNotFound && hasProperty) ) { result(property, properties); } } } } } function invalidNameError(property, properties) { //>>includeStart('debug', pragmas.debug); var errorString = "fabric: property name '" + property + "' is not valid. It should be "; for (var i = 0; i < properties.length; i++) { var propertyName = "'" + properties[i] + "'"; errorString += i === properties.length - 1 ? "or " + propertyName + "." : propertyName + ", "; } throw new DeveloperError(errorString); //>>includeEnd('debug'); } function duplicateNameError(property, properties) { //>>includeStart('debug', pragmas.debug); var errorString = "fabric: uniforms and materials cannot share the same property '" + property + "'"; throw new DeveloperError(errorString); //>>includeEnd('debug'); } var templateProperties = [ "type", "materials", "uniforms", "components", "source", ]; var componentProperties = [ "diffuse", "specular", "shininess", "normal", "emission", "alpha", ]; function checkForTemplateErrors(material) { var template = material._template; var uniforms = template.uniforms; var materials = template.materials; var components = template.components; // Make sure source and components do not exist in the same template. //>>includeStart('debug', pragmas.debug); if (defined(components) && defined(template.source)) { throw new DeveloperError( "fabric: cannot have source and components in the same template." ); } //>>includeEnd('debug'); // Make sure all template and components properties are valid. checkForValidProperties(template, templateProperties, invalidNameError, true); checkForValidProperties( components, componentProperties, invalidNameError, true ); // Make sure uniforms and materials do not share any of the same names. var materialNames = []; for (var property in materials) { if (materials.hasOwnProperty(property)) { materialNames.push(property); } } checkForValidProperties(uniforms, materialNames, duplicateNameError, false); } function isMaterialFused(shaderComponent, material) { var materials = material._template.materials; for (var subMaterialId in materials) { if (materials.hasOwnProperty(subMaterialId)) { if (shaderComponent.indexOf(subMaterialId) > -1) { return true; } } } return false; } // Create the czm_getMaterial method body using source or components. function createMethodDefinition(material) { var components = material._template.components; var source = material._template.source; if (defined(source)) { material.shaderSource += source + "\n"; } else { material.shaderSource += "czm_material czm_getMaterial(czm_materialInput materialInput)\n{\n"; material.shaderSource += "czm_material material = czm_getDefaultMaterial(materialInput);\n"; if (defined(components)) { var isMultiMaterial = Object.keys(material._template.materials).length > 0; for (var component in components) { if (components.hasOwnProperty(component)) { if (component === "diffuse" || component === "emission") { var isFusion = isMultiMaterial && isMaterialFused(components[component], material); var componentSource = isFusion ? components[component] : "czm_gammaCorrect(" + components[component] + ")"; material.shaderSource += "material." + component + " = " + componentSource + "; \n"; } else if (component === "alpha") { material.shaderSource += "material.alpha = " + components.alpha + "; \n"; } else { material.shaderSource += "material." + component + " = " + components[component] + ";\n"; } } } } material.shaderSource += "return material;\n}\n"; } } var matrixMap = { mat2: Matrix2, mat3: Matrix3, mat4: Matrix4, }; var ktxRegex$2 = /\.ktx$/i; var crnRegex$2 = /\.crn$/i; function createTexture2DUpdateFunction(uniformId) { var oldUniformValue; return function (material, context) { var uniforms = material.uniforms; var uniformValue = uniforms[uniformId]; var uniformChanged = oldUniformValue !== uniformValue; oldUniformValue = uniformValue; var texture = material._textures[uniformId]; var uniformDimensionsName; var uniformDimensions; if (uniformValue instanceof HTMLVideoElement) { // HTMLVideoElement.readyState >=2 means we have enough data for the current frame. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState if (uniformValue.readyState >= 2) { if (uniformChanged && defined(texture)) { if (texture !== context.defaultTexture) { texture.destroy(); } texture = undefined; } if (!defined(texture) || texture === context.defaultTexture) { var sampler = new Sampler({ minificationFilter: material._minificationFilter, magnificationFilter: material._magnificationFilter, }); texture = new Texture({ context: context, source: uniformValue, sampler: sampler, }); material._textures[uniformId] = texture; return; } texture.copyFrom(uniformValue); } else if (!defined(texture)) { material._textures[uniformId] = context.defaultTexture; } return; } if (uniformValue instanceof Texture && uniformValue !== texture) { material._texturePaths[uniformId] = undefined; var tmp = material._textures[uniformId]; if (tmp !== material._defaultTexture) { tmp.destroy(); } material._textures[uniformId] = uniformValue; uniformDimensionsName = uniformId + "Dimensions"; if (uniforms.hasOwnProperty(uniformDimensionsName)) { uniformDimensions = uniforms[uniformDimensionsName]; uniformDimensions.x = uniformValue._width; uniformDimensions.y = uniformValue._height; } return; } if (!defined(texture)) { material._texturePaths[uniformId] = undefined; if (!defined(material._defaultTexture)) { material._defaultTexture = context.defaultTexture; } texture = material._textures[uniformId] = material._defaultTexture; uniformDimensionsName = uniformId + "Dimensions"; if (uniforms.hasOwnProperty(uniformDimensionsName)) { uniformDimensions = uniforms[uniformDimensionsName]; uniformDimensions.x = texture._width; uniformDimensions.y = texture._height; } } if (uniformValue === Material.DefaultImageId) { return; } // When using the entity layer, the Resource objects get recreated on getValue because // they are clonable. That's why we check the url property for Resources // because the instances aren't the same and we keep trying to load the same // image if it fails to load. var isResource = uniformValue instanceof Resource; if ( !defined(material._texturePaths[uniformId]) || (isResource && uniformValue.url !== material._texturePaths[uniformId].url) || (!isResource && uniformValue !== material._texturePaths[uniformId]) ) { if (typeof uniformValue === "string" || isResource) { var resource = isResource ? uniformValue : Resource.createIfNeeded(uniformValue); var promise; if (ktxRegex$2.test(resource.url)) { promise = loadKTX(resource); } else if (crnRegex$2.test(resource.url)) { promise = loadCRN(resource); } else { promise = resource.fetchImage(); } when(promise, function (image) { material._loadedImages.push({ id: uniformId, image: image, }); }); } else if ( uniformValue instanceof HTMLCanvasElement || uniformValue instanceof HTMLImageElement ) { material._loadedImages.push({ id: uniformId, image: uniformValue, }); } material._texturePaths[uniformId] = uniformValue; } }; } function createCubeMapUpdateFunction(uniformId) { return function (material, context) { var uniformValue = material.uniforms[uniformId]; if (uniformValue instanceof CubeMap) { var tmp = material._textures[uniformId]; if (tmp !== material._defaultTexture) { tmp.destroy(); } material._texturePaths[uniformId] = undefined; material._textures[uniformId] = uniformValue; return; } if (!defined(material._textures[uniformId])) { material._texturePaths[uniformId] = undefined; material._textures[uniformId] = context.defaultCubeMap; } if (uniformValue === Material.DefaultCubeMapId) { return; } var path = uniformValue.positiveX + uniformValue.negativeX + uniformValue.positiveY + uniformValue.negativeY + uniformValue.positiveZ + uniformValue.negativeZ; if (path !== material._texturePaths[uniformId]) { var promises = [ Resource.createIfNeeded(uniformValue.positiveX).fetchImage(), Resource.createIfNeeded(uniformValue.negativeX).fetchImage(), Resource.createIfNeeded(uniformValue.positiveY).fetchImage(), Resource.createIfNeeded(uniformValue.negativeY).fetchImage(), Resource.createIfNeeded(uniformValue.positiveZ).fetchImage(), Resource.createIfNeeded(uniformValue.negativeZ).fetchImage(), ]; when.all(promises).then(function (images) { material._loadedCubeMaps.push({ id: uniformId, images: images, }); }); material._texturePaths[uniformId] = path; } }; } function createUniforms(material) { var uniforms = material._template.uniforms; for (var uniformId in uniforms) { if (uniforms.hasOwnProperty(uniformId)) { createUniform$1(material, uniformId); } } } // Writes uniform declarations to the shader file and connects uniform values with // corresponding material properties through the returnUniforms function. function createUniform$1(material, uniformId) { var strict = material._strict; var materialUniforms = material._template.uniforms; var uniformValue = materialUniforms[uniformId]; var uniformType = getUniformType(uniformValue); //>>includeStart('debug', pragmas.debug); if (!defined(uniformType)) { throw new DeveloperError( "fabric: uniform '" + uniformId + "' has invalid type." ); } //>>includeEnd('debug'); var replacedTokenCount; if (uniformType === "channels") { replacedTokenCount = replaceToken(material, uniformId, uniformValue, false); //>>includeStart('debug', pragmas.debug); if (replacedTokenCount === 0 && strict) { throw new DeveloperError( "strict: shader source does not use channels '" + uniformId + "'." ); } //>>includeEnd('debug'); } else { // Since webgl doesn't allow texture dimension queries in glsl, create a uniform to do it. // Check if the shader source actually uses texture dimensions before creating the uniform. if (uniformType === "sampler2D") { var imageDimensionsUniformName = uniformId + "Dimensions"; if (getNumberOfTokens(material, imageDimensionsUniformName) > 0) { materialUniforms[imageDimensionsUniformName] = { type: "ivec3", x: 1, y: 1, }; createUniform$1(material, imageDimensionsUniformName); } } // Add uniform declaration to source code. var uniformDeclarationRegex = new RegExp( "uniform\\s+" + uniformType + "\\s+" + uniformId + "\\s*;" ); if (!uniformDeclarationRegex.test(material.shaderSource)) { var uniformDeclaration = "uniform " + uniformType + " " + uniformId + ";"; material.shaderSource = uniformDeclaration + material.shaderSource; } var newUniformId = uniformId + "_" + material._count++; replacedTokenCount = replaceToken(material, uniformId, newUniformId); //>>includeStart('debug', pragmas.debug); if (replacedTokenCount === 1 && strict) { throw new DeveloperError( "strict: shader source does not use uniform '" + uniformId + "'." ); } //>>includeEnd('debug'); // Set uniform value material.uniforms[uniformId] = uniformValue; if (uniformType === "sampler2D") { material._uniforms[newUniformId] = function () { return material._textures[uniformId]; }; material._updateFunctions.push(createTexture2DUpdateFunction(uniformId)); } else if (uniformType === "samplerCube") { material._uniforms[newUniformId] = function () { return material._textures[uniformId]; }; material._updateFunctions.push(createCubeMapUpdateFunction(uniformId)); } else if (uniformType.indexOf("mat") !== -1) { var scratchMatrix = new matrixMap[uniformType](); material._uniforms[newUniformId] = function () { return matrixMap[uniformType].fromColumnMajorArray( material.uniforms[uniformId], scratchMatrix ); }; } else { material._uniforms[newUniformId] = function () { return material.uniforms[uniformId]; }; } } } // Determines the uniform type based on the uniform in the template. function getUniformType(uniformValue) { var uniformType = uniformValue.type; if (!defined(uniformType)) { var type = typeof uniformValue; if (type === "number") { uniformType = "float"; } else if (type === "boolean") { uniformType = "bool"; } else if ( type === "string" || uniformValue instanceof Resource || uniformValue instanceof HTMLCanvasElement || uniformValue instanceof HTMLImageElement ) { if (/^([rgba]){1,4}$/i.test(uniformValue)) { uniformType = "channels"; } else if (uniformValue === Material.DefaultCubeMapId) { uniformType = "samplerCube"; } else { uniformType = "sampler2D"; } } else if (type === "object") { if (Array.isArray(uniformValue)) { if ( uniformValue.length === 4 || uniformValue.length === 9 || uniformValue.length === 16 ) { uniformType = "mat" + Math.sqrt(uniformValue.length); } } else { var numAttributes = 0; for (var attribute in uniformValue) { if (uniformValue.hasOwnProperty(attribute)) { numAttributes += 1; } } if (numAttributes >= 2 && numAttributes <= 4) { uniformType = "vec" + numAttributes; } else if (numAttributes === 6) { uniformType = "samplerCube"; } } } } return uniformType; } // Create all sub-materials by combining source and uniforms together. function createSubMaterials(material) { var strict = material._strict; var subMaterialTemplates = material._template.materials; for (var subMaterialId in subMaterialTemplates) { if (subMaterialTemplates.hasOwnProperty(subMaterialId)) { // Construct the sub-material. var subMaterial = new Material({ strict: strict, fabric: subMaterialTemplates[subMaterialId], count: material._count, }); material._count = subMaterial._count; material._uniforms = combine$2( material._uniforms, subMaterial._uniforms, true ); material.materials[subMaterialId] = subMaterial; material._translucentFunctions = material._translucentFunctions.concat( subMaterial._translucentFunctions ); // Make the material's czm_getMaterial unique by appending the sub-material type. var originalMethodName = "czm_getMaterial"; var newMethodName = originalMethodName + "_" + material._count++; replaceToken(subMaterial, originalMethodName, newMethodName); material.shaderSource = subMaterial.shaderSource + material.shaderSource; // Replace each material id with an czm_getMaterial method call. var materialMethodCall = newMethodName + "(materialInput)"; var tokensReplacedCount = replaceToken( material, subMaterialId, materialMethodCall ); //>>includeStart('debug', pragmas.debug); if (tokensReplacedCount === 0 && strict) { throw new DeveloperError( "strict: shader source does not use material '" + subMaterialId + "'." ); } //>>includeEnd('debug'); } } } // Used for searching or replacing a token in a material's shader source with something else. // If excludePeriod is true, do not accept tokens that are preceded by periods. // http://stackoverflow.com/questions/641407/javascript-negative-lookbehind-equivalent function replaceToken(material, token, newToken, excludePeriod) { excludePeriod = defaultValue(excludePeriod, true); var count = 0; var suffixChars = "([\\w])?"; var prefixChars = "([\\w" + (excludePeriod ? "." : "") + "])?"; var regExp = new RegExp(prefixChars + token + suffixChars, "g"); material.shaderSource = material.shaderSource.replace(regExp, function ( $0, $1, $2 ) { if ($1 || $2) { return $0; } count += 1; return newToken; }); return count; } function getNumberOfTokens(material, token, excludePeriod) { return replaceToken(material, token, token, excludePeriod); } Material._materialCache = { _materials: {}, addMaterial: function (type, materialTemplate) { this._materials[type] = materialTemplate; }, getMaterial: function (type) { return this._materials[type]; }, }; /** * Gets or sets the default texture uniform value. * @type {String} */ Material.DefaultImageId = "czm_defaultImage"; /** * Gets or sets the default cube map texture uniform value. * @type {String} */ Material.DefaultCubeMapId = "czm_defaultCubeMap"; /** * Gets the name of the color material. * @type {String} * @readonly */ Material.ColorType = "Color"; Material._materialCache.addMaterial(Material.ColorType, { fabric: { type: Material.ColorType, uniforms: { color: new Color(1.0, 0.0, 0.0, 0.5), }, components: { diffuse: "color.rgb", alpha: "color.a", }, }, translucent: function (material) { return material.uniforms.color.alpha < 1.0; }, }); /** * Gets the name of the image material. * @type {String} * @readonly */ Material.ImageType = "Image"; Material._materialCache.addMaterial(Material.ImageType, { fabric: { type: Material.ImageType, uniforms: { image: Material.DefaultImageId, repeat: new Cartesian2(1.0, 1.0), color: new Color(1.0, 1.0, 1.0, 1.0), }, components: { diffuse: "texture2D(image, fract(repeat * materialInput.st)).rgb * color.rgb", alpha: "texture2D(image, fract(repeat * materialInput.st)).a * color.a", }, }, translucent: function (material) { return material.uniforms.color.alpha < 1.0; }, }); /** * Gets the name of the diffuce map material. * @type {String} * @readonly */ Material.DiffuseMapType = "DiffuseMap"; Material._materialCache.addMaterial(Material.DiffuseMapType, { fabric: { type: Material.DiffuseMapType, uniforms: { image: Material.DefaultImageId, channels: "rgb", repeat: new Cartesian2(1.0, 1.0), }, components: { diffuse: "texture2D(image, fract(repeat * materialInput.st)).channels", }, }, translucent: false, }); /** * Gets the name of the alpha map material. * @type {String} * @readonly */ Material.AlphaMapType = "AlphaMap"; Material._materialCache.addMaterial(Material.AlphaMapType, { fabric: { type: Material.AlphaMapType, uniforms: { image: Material.DefaultImageId, channel: "a", repeat: new Cartesian2(1.0, 1.0), }, components: { alpha: "texture2D(image, fract(repeat * materialInput.st)).channel", }, }, translucent: true, }); /** * Gets the name of the specular map material. * @type {String} * @readonly */ Material.SpecularMapType = "SpecularMap"; Material._materialCache.addMaterial(Material.SpecularMapType, { fabric: { type: Material.SpecularMapType, uniforms: { image: Material.DefaultImageId, channel: "r", repeat: new Cartesian2(1.0, 1.0), }, components: { specular: "texture2D(image, fract(repeat * materialInput.st)).channel", }, }, translucent: false, }); /** * Gets the name of the emmision map material. * @type {String} * @readonly */ Material.EmissionMapType = "EmissionMap"; Material._materialCache.addMaterial(Material.EmissionMapType, { fabric: { type: Material.EmissionMapType, uniforms: { image: Material.DefaultImageId, channels: "rgb", repeat: new Cartesian2(1.0, 1.0), }, components: { emission: "texture2D(image, fract(repeat * materialInput.st)).channels", }, }, translucent: false, }); /** * Gets the name of the bump map material. * @type {String} * @readonly */ Material.BumpMapType = "BumpMap"; Material._materialCache.addMaterial(Material.BumpMapType, { fabric: { type: Material.BumpMapType, uniforms: { image: Material.DefaultImageId, channel: "r", strength: 0.8, repeat: new Cartesian2(1.0, 1.0), }, source: BumpMapMaterial, }, translucent: false, }); /** * Gets the name of the normal map material. * @type {String} * @readonly */ Material.NormalMapType = "NormalMap"; Material._materialCache.addMaterial(Material.NormalMapType, { fabric: { type: Material.NormalMapType, uniforms: { image: Material.DefaultImageId, channels: "rgb", strength: 0.8, repeat: new Cartesian2(1.0, 1.0), }, source: NormalMapMaterial, }, translucent: false, }); /** * Gets the name of the grid material. * @type {String} * @readonly */ Material.GridType = "Grid"; Material._materialCache.addMaterial(Material.GridType, { fabric: { type: Material.GridType, uniforms: { color: new Color(0.0, 1.0, 0.0, 1.0), cellAlpha: 0.1, lineCount: new Cartesian2(8.0, 8.0), lineThickness: new Cartesian2(1.0, 1.0), lineOffset: new Cartesian2(0.0, 0.0), }, source: GridMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.color.alpha < 1.0 || uniforms.cellAlpha < 1.0; }, }); /** * Gets the name of the stripe material. * @type {String} * @readonly */ Material.StripeType = "Stripe"; Material._materialCache.addMaterial(Material.StripeType, { fabric: { type: Material.StripeType, uniforms: { horizontal: true, evenColor: new Color(1.0, 1.0, 1.0, 0.5), oddColor: new Color(0.0, 0.0, 1.0, 0.5), offset: 0.0, repeat: 5.0, }, source: StripeMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.evenColor.alpha < 1.0 || uniforms.oddColor.alpha < 1.0; }, }); /** * Gets the name of the checkerboard material. * @type {String} * @readonly */ Material.CheckerboardType = "Checkerboard"; Material._materialCache.addMaterial(Material.CheckerboardType, { fabric: { type: Material.CheckerboardType, uniforms: { lightColor: new Color(1.0, 1.0, 1.0, 0.5), darkColor: new Color(0.0, 0.0, 0.0, 0.5), repeat: new Cartesian2(5.0, 5.0), }, source: CheckerboardMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 1.0; }, }); /** * Gets the name of the dot material. * @type {String} * @readonly */ Material.DotType = "Dot"; Material._materialCache.addMaterial(Material.DotType, { fabric: { type: Material.DotType, uniforms: { lightColor: new Color(1.0, 1.0, 0.0, 0.75), darkColor: new Color(0.0, 1.0, 1.0, 0.75), repeat: new Cartesian2(5.0, 5.0), }, source: DotMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.lightColor.alpha < 1.0 || uniforms.darkColor.alpha < 1.0; }, }); /** * Gets the name of the water material. * @type {String} * @readonly */ Material.WaterType = "Water"; Material._materialCache.addMaterial(Material.WaterType, { fabric: { type: Material.WaterType, uniforms: { baseWaterColor: new Color(0.2, 0.3, 0.6, 1.0), blendColor: new Color(0.0, 1.0, 0.699, 1.0), specularMap: Material.DefaultImageId, normalMap: Material.DefaultImageId, frequency: 10.0, animationSpeed: 0.01, amplitude: 1.0, specularIntensity: 0.5, fadeFactor: 1.0, }, source: WaterMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return ( uniforms.baseWaterColor.alpha < 1.0 || uniforms.blendColor.alpha < 1.0 ); }, }); /** * Gets the name of the rim lighting material. * @type {String} * @readonly */ Material.RimLightingType = "RimLighting"; Material._materialCache.addMaterial(Material.RimLightingType, { fabric: { type: Material.RimLightingType, uniforms: { color: new Color(1.0, 0.0, 0.0, 0.7), rimColor: new Color(1.0, 1.0, 1.0, 0.4), width: 0.3, }, source: RimLightingMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.color.alpha < 1.0 || uniforms.rimColor.alpha < 1.0; }, }); /** * Gets the name of the fade material. * @type {String} * @readonly */ Material.FadeType = "Fade"; Material._materialCache.addMaterial(Material.FadeType, { fabric: { type: Material.FadeType, uniforms: { fadeInColor: new Color(1.0, 0.0, 0.0, 1.0), fadeOutColor: new Color(0.0, 0.0, 0.0, 0.0), maximumDistance: 0.5, repeat: true, fadeDirection: { x: true, y: true, }, time: new Cartesian2(0.5, 0.5), }, source: FadeMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return ( uniforms.fadeInColor.alpha < 1.0 || uniforms.fadeOutColor.alpha < 1.0 ); }, }); /** * Gets the name of the polyline arrow material. * @type {String} * @readonly */ Material.PolylineArrowType = "PolylineArrow"; Material._materialCache.addMaterial(Material.PolylineArrowType, { fabric: { type: Material.PolylineArrowType, uniforms: { color: new Color(1.0, 1.0, 1.0, 1.0), }, source: PolylineArrowMaterial, }, translucent: true, }); /** * Gets the name of the polyline glow material. * @type {String} * @readonly */ Material.PolylineDashType = "PolylineDash"; Material._materialCache.addMaterial(Material.PolylineDashType, { fabric: { type: Material.PolylineDashType, uniforms: { color: new Color(1.0, 0.0, 1.0, 1.0), gapColor: new Color(0.0, 0.0, 0.0, 0.0), dashLength: 16.0, dashPattern: 255.0, }, source: PolylineDashMaterial, }, translucent: true, }); /** * Gets the name of the polyline glow material. * @type {String} * @readonly */ Material.PolylineGlowType = "PolylineGlow"; Material._materialCache.addMaterial(Material.PolylineGlowType, { fabric: { type: Material.PolylineGlowType, uniforms: { color: new Color(0.0, 0.5, 1.0, 1.0), glowPower: 0.25, taperPower: 1.0, }, source: PolylineGlowMaterial, }, translucent: true, }); /** * Gets the name of the polyline outline material. * @type {String} * @readonly */ Material.PolylineOutlineType = "PolylineOutline"; Material._materialCache.addMaterial(Material.PolylineOutlineType, { fabric: { type: Material.PolylineOutlineType, uniforms: { color: new Color(1.0, 1.0, 1.0, 1.0), outlineColor: new Color(1.0, 0.0, 0.0, 1.0), outlineWidth: 1.0, }, source: PolylineOutlineMaterial, }, translucent: function (material) { var uniforms = material.uniforms; return uniforms.color.alpha < 1.0 || uniforms.outlineColor.alpha < 1.0; }, }); /** * Gets the name of the elevation contour material. * @type {String} * @readonly */ Material.ElevationContourType = "ElevationContour"; Material._materialCache.addMaterial(Material.ElevationContourType, { fabric: { type: Material.ElevationContourType, uniforms: { spacing: 100.0, color: new Color(1.0, 0.0, 0.0, 1.0), width: 1.0, }, source: ElevationContourMaterial, }, translucent: false, }); /** * Gets the name of the elevation contour material. * @type {String} * @readonly */ Material.ElevationRampType = "ElevationRamp"; Material._materialCache.addMaterial(Material.ElevationRampType, { fabric: { type: Material.ElevationRampType, uniforms: { image: Material.DefaultImageId, minimumHeight: 0.0, maximumHeight: 10000.0, }, source: ElevationRampMaterial, }, translucent: false, }); /** * Gets the name of the slope ramp material. * @type {String} * @readonly */ Material.SlopeRampMaterialType = "SlopeRamp"; Material._materialCache.addMaterial(Material.SlopeRampMaterialType, { fabric: { type: Material.SlopeRampMaterialType, uniforms: { image: Material.DefaultImageId, }, source: SlopeRampMaterial, }, translucent: false, }); /** * Gets the name of the aspect ramp material. * @type {String} * @readonly */ Material.AspectRampMaterialType = "AspectRamp"; Material._materialCache.addMaterial(Material.AspectRampMaterialType, { fabric: { type: Material.AspectRampMaterialType, uniforms: { image: Material.DefaultImageId, }, source: AspectRampMaterial, }, translucent: false, }); /** * Gets the name of the elevation band material. * @type {String} * @readonly */ Material.ElevationBandType = "ElevationBand"; Material._materialCache.addMaterial(Material.ElevationBandType, { fabric: { type: Material.ElevationBandType, uniforms: { heights: Material.DefaultImageId, colors: Material.DefaultImageId, }, source: ElevationBandMaterial, }, translucent: true, }); /** * An appearance for arbitrary geometry (as opposed to {@link EllipsoidSurfaceAppearance}, for example) * that supports shading with materials. * * @alias MaterialAppearance * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.flat=false] When true, flat shading is used in the fragment shader, which means lighting is not taking into account. * @param {Boolean} [options.faceForward=!options.closed] When true, the fragment shader flips the surface normal as needed to ensure that the normal faces the viewer to avoid dark spots. This is useful when both sides of a geometry should be shaded like {@link WallGeometry}. * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link MaterialAppearance#renderState} has alpha blending enabled. * @param {Boolean} [options.closed=false] When true, the geometry is expected to be closed so {@link MaterialAppearance#renderState} has backface culling enabled. * @param {MaterialAppearance.MaterialSupportType} [options.materialSupport=MaterialAppearance.MaterialSupport.TEXTURED] The type of materials that will be supported. * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color. * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {Object} [options.renderState] Optional render state to override the default render state. * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} * @demo {@link https://sandcastle.cesium.com/index.html?src=Materials.html|Cesium Sandcastle Material Appearance Demo} * * @example * var primitive = new Cesium.Primitive({ * geometryInstances : new Cesium.GeometryInstance({ * geometry : new Cesium.WallGeometry({ materialSupport : Cesium.MaterialAppearance.MaterialSupport.BASIC.vertexFormat, * // ... * }) * }), * appearance : new Cesium.MaterialAppearance({ * material : Cesium.Material.fromType('Color'), * faceForward : true * }) * * }); */ function MaterialAppearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var translucent = defaultValue(options.translucent, true); var closed = defaultValue(options.closed, false); var materialSupport = defaultValue( options.materialSupport, MaterialAppearance.MaterialSupport.TEXTURED ); /** * The material used to determine the fragment color. Unlike other {@link MaterialAppearance} * properties, this is not read-only, so an appearance's material can change on the fly. * * @type Material * * @default {@link Material.ColorType} * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} */ this.material = defined(options.material) ? options.material : Material.fromType(Material.ColorType); /** * When true, the geometry is expected to appear translucent. * * @type {Boolean} * * @default true */ this.translucent = translucent; this._vertexShaderSource = defaultValue( options.vertexShaderSource, materialSupport.vertexShaderSource ); this._fragmentShaderSource = defaultValue( options.fragmentShaderSource, materialSupport.fragmentShaderSource ); this._renderState = Appearance.getDefaultRenderState( translucent, closed, options.renderState ); this._closed = closed; // Non-derived members this._materialSupport = materialSupport; this._vertexFormat = materialSupport.vertexFormat; this._flat = defaultValue(options.flat, false); this._faceForward = defaultValue(options.faceForward, !closed); } Object.defineProperties(MaterialAppearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof MaterialAppearance.prototype * * @type {String} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * The GLSL source code for the fragment shader. The full fragment shader * source is built procedurally taking into account {@link MaterialAppearance#material}, * {@link MaterialAppearance#flat}, and {@link MaterialAppearance#faceForward}. * Use {@link MaterialAppearance#getFragmentShaderSource} to get the full source. * * @memberof MaterialAppearance.prototype * * @type {String} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. *

* The render state can be explicitly defined when constructing a {@link MaterialAppearance} * instance, or it is set implicitly via {@link MaterialAppearance#translucent} * and {@link MaterialAppearance#closed}. *

* * @memberof MaterialAppearance.prototype * * @type {Object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed so * {@link MaterialAppearance#renderState} has backface culling enabled. * If the viewer enters the geometry, it will not be visible. * * @memberof MaterialAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, /** * The type of materials supported by this instance. This impacts the required * {@link VertexFormat} and the complexity of the vertex and fragment shaders. * * @memberof MaterialAppearance.prototype * * @type {MaterialAppearance.MaterialSupportType} * @readonly * * @default {@link MaterialAppearance.MaterialSupport.TEXTURED} */ materialSupport: { get: function () { return this._materialSupport; }, }, /** * The {@link VertexFormat} that this appearance instance is compatible with. * A geometry can have more vertex attributes and still be compatible - at a * potential performance cost - but it can't have less. * * @memberof MaterialAppearance.prototype * * @type VertexFormat * @readonly * * @default {@link MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat} */ vertexFormat: { get: function () { return this._vertexFormat; }, }, /** * When true, flat shading is used in the fragment shader, * which means lighting is not taking into account. * * @memberof MaterialAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ flat: { get: function () { return this._flat; }, }, /** * When true, the fragment shader flips the surface normal * as needed to ensure that the normal faces the viewer to avoid * dark spots. This is useful when both sides of a geometry should be * shaded like {@link WallGeometry}. * * @memberof MaterialAppearance.prototype * * @type {Boolean} * @readonly * * @default true */ faceForward: { get: function () { return this._faceForward; }, }, }); /** * Procedurally creates the full GLSL fragment shader source. For {@link MaterialAppearance}, * this is derived from {@link MaterialAppearance#fragmentShaderSource}, {@link MaterialAppearance#material}, * {@link MaterialAppearance#flat}, and {@link MaterialAppearance#faceForward}. * * @function * * @returns {String} The full GLSL fragment shader source. */ MaterialAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource; /** * Determines if the geometry is translucent based on {@link MaterialAppearance#translucent} and {@link Material#isTranslucent}. * * @function * * @returns {Boolean} true if the appearance is translucent. */ MaterialAppearance.prototype.isTranslucent = Appearance.prototype.isTranslucent; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @function * * @returns {Object} The render state. */ MaterialAppearance.prototype.getRenderState = Appearance.prototype.getRenderState; /** * @typedef MaterialAppearance.MaterialSupportType * @type {Object} * @property {VertexFormat} vertexFormat * @property {String} vertexShaderSource * @property {String} fragmentShaderSource */ /** * Determines the type of {@link Material} that is supported by a * {@link MaterialAppearance} instance. This is a trade-off between * flexibility (a wide array of materials) and memory/performance * (required vertex format and GLSL shader complexity. * @namespace */ MaterialAppearance.MaterialSupport = { /** * Only basic materials, which require just position and * normal vertex attributes, are supported. * * @type {MaterialAppearance.MaterialSupportType} * @constant */ BASIC: Object.freeze({ vertexFormat: VertexFormat.POSITION_AND_NORMAL, vertexShaderSource: BasicMaterialAppearanceVS, fragmentShaderSource: BasicMaterialAppearanceFS, }), /** * Materials with textures, which require position, * normal, and st vertex attributes, * are supported. The vast majority of materials fall into this category. * * @type {MaterialAppearance.MaterialSupportType} * @constant */ TEXTURED: Object.freeze({ vertexFormat: VertexFormat.POSITION_NORMAL_AND_ST, vertexShaderSource: TexturedMaterialAppearanceVS, fragmentShaderSource: TexturedMaterialAppearanceFS, }), /** * All materials, including those that work in tangent space, are supported. * This requires position, normal, st, * tangent, and bitangent vertex attributes. * * @type {MaterialAppearance.MaterialSupportType} * @constant */ ALL: Object.freeze({ vertexFormat: VertexFormat.ALL, vertexShaderSource: AllMaterialAppearanceVS, fragmentShaderSource: AllMaterialAppearanceFS, }), }; //This file is automatically rebuilt by the Cesium build process. var PerInstanceColorAppearanceFS = "varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec4 v_color;\n\ \n\ void main()\n\ {\n\ vec3 positionToEyeEC = -v_positionEC;\n\ \n\ vec3 normalEC = normalize(v_normalEC);\n\ #ifdef FACE_FORWARD\n\ normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n\ #endif\n\ \n\ vec4 color = czm_gammaCorrect(v_color);\n\ \n\ czm_materialInput materialInput;\n\ materialInput.normalEC = normalEC;\n\ materialInput.positionToEyeEC = positionToEyeEC;\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ \n\ gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PerInstanceColorAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 normal;\n\ attribute vec4 color;\n\ attribute float batchId;\n\ \n\ varying vec3 v_positionEC;\n\ varying vec3 v_normalEC;\n\ varying vec4 v_color;\n\ \n\ void main()\n\ {\n\ vec4 p = czm_computePosition();\n\ \n\ v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates\n\ v_normalEC = czm_normal * normal; // normal in eye coordinates\n\ v_color = color;\n\ \n\ gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PerInstanceFlatColorAppearanceFS = "varying vec4 v_color;\n\ \n\ void main()\n\ {\n\ gl_FragColor = czm_gammaCorrect(v_color);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PerInstanceFlatColorAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec4 color;\n\ attribute float batchId;\n\ \n\ varying vec4 v_color;\n\ \n\ void main()\n\ {\n\ vec4 p = czm_computePosition();\n\ \n\ v_color = color;\n\ \n\ gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ }\n\ "; /** * An appearance for {@link GeometryInstance} instances with color attributes. * This allows several geometry instances, each with a different color, to * be drawn with the same {@link Primitive} as shown in the second example below. * * @alias PerInstanceColorAppearance * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.flat=false] When true, flat shading is used in the fragment shader, which means lighting is not taking into account. * @param {Boolean} [options.faceForward=!options.closed] When true, the fragment shader flips the surface normal as needed to ensure that the normal faces the viewer to avoid dark spots. This is useful when both sides of a geometry should be shaded like {@link WallGeometry}. * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link PerInstanceColorAppearance#renderState} has alpha blending enabled. * @param {Boolean} [options.closed=false] When true, the geometry is expected to be closed so {@link PerInstanceColorAppearance#renderState} has backface culling enabled. * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {Object} [options.renderState] Optional render state to override the default render state. * * @example * // A solid white line segment * var primitive = new Cesium.Primitive({ * geometryInstances : new Cesium.GeometryInstance({ * geometry : new Cesium.SimplePolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 5.0, 0.0 * ]) * }), * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 1.0)) * } * }), * appearance : new Cesium.PerInstanceColorAppearance({ * flat : true, * translucent : false * }) * }); * * // Two rectangles in a primitive, each with a different color * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.RectangleGeometry({ * rectangle : Cesium.Rectangle.fromDegrees(0.0, 20.0, 10.0, 30.0) * }), * attributes : { * color : new Cesium.ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 0.5) * } * }); * * var anotherInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.RectangleGeometry({ * rectangle : Cesium.Rectangle.fromDegrees(0.0, 40.0, 10.0, 50.0) * }), * attributes : { * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 0.0, 1.0, 0.5) * } * }); * * var rectanglePrimitive = new Cesium.Primitive({ * geometryInstances : [instance, anotherInstance], * appearance : new Cesium.PerInstanceColorAppearance() * }); */ function PerInstanceColorAppearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var translucent = defaultValue(options.translucent, true); var closed = defaultValue(options.closed, false); var flat = defaultValue(options.flat, false); var vs = flat ? PerInstanceFlatColorAppearanceVS : PerInstanceColorAppearanceVS; var fs = flat ? PerInstanceFlatColorAppearanceFS : PerInstanceColorAppearanceFS; var vertexFormat = flat ? PerInstanceColorAppearance.FLAT_VERTEX_FORMAT : PerInstanceColorAppearance.VERTEX_FORMAT; /** * This property is part of the {@link Appearance} interface, but is not * used by {@link PerInstanceColorAppearance} since a fully custom fragment shader is used. * * @type Material * * @default undefined */ this.material = undefined; /** * When true, the geometry is expected to appear translucent so * {@link PerInstanceColorAppearance#renderState} has alpha blending enabled. * * @type {Boolean} * * @default true */ this.translucent = translucent; this._vertexShaderSource = defaultValue(options.vertexShaderSource, vs); this._fragmentShaderSource = defaultValue(options.fragmentShaderSource, fs); this._renderState = Appearance.getDefaultRenderState( translucent, closed, options.renderState ); this._closed = closed; // Non-derived members this._vertexFormat = vertexFormat; this._flat = flat; this._faceForward = defaultValue(options.faceForward, !closed); } Object.defineProperties(PerInstanceColorAppearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof PerInstanceColorAppearance.prototype * * @type {String} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * The GLSL source code for the fragment shader. * * @memberof PerInstanceColorAppearance.prototype * * @type {String} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. *

* The render state can be explicitly defined when constructing a {@link PerInstanceColorAppearance} * instance, or it is set implicitly via {@link PerInstanceColorAppearance#translucent} * and {@link PerInstanceColorAppearance#closed}. *

* * @memberof PerInstanceColorAppearance.prototype * * @type {Object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed so * {@link PerInstanceColorAppearance#renderState} has backface culling enabled. * If the viewer enters the geometry, it will not be visible. * * @memberof PerInstanceColorAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, /** * The {@link VertexFormat} that this appearance instance is compatible with. * A geometry can have more vertex attributes and still be compatible - at a * potential performance cost - but it can't have less. * * @memberof PerInstanceColorAppearance.prototype * * @type VertexFormat * @readonly */ vertexFormat: { get: function () { return this._vertexFormat; }, }, /** * When true, flat shading is used in the fragment shader, * which means lighting is not taking into account. * * @memberof PerInstanceColorAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ flat: { get: function () { return this._flat; }, }, /** * When true, the fragment shader flips the surface normal * as needed to ensure that the normal faces the viewer to avoid * dark spots. This is useful when both sides of a geometry should be * shaded like {@link WallGeometry}. * * @memberof PerInstanceColorAppearance.prototype * * @type {Boolean} * @readonly * * @default true */ faceForward: { get: function () { return this._faceForward; }, }, }); /** * The {@link VertexFormat} that all {@link PerInstanceColorAppearance} instances * are compatible with. This requires only position and normal * attributes. * * @type VertexFormat * * @constant */ PerInstanceColorAppearance.VERTEX_FORMAT = VertexFormat.POSITION_AND_NORMAL; /** * The {@link VertexFormat} that all {@link PerInstanceColorAppearance} instances * are compatible with when {@link PerInstanceColorAppearance#flat} is true. * This requires only a position attribute. * * @type VertexFormat * * @constant */ PerInstanceColorAppearance.FLAT_VERTEX_FORMAT = VertexFormat.POSITION_ONLY; /** * Procedurally creates the full GLSL fragment shader source. For {@link PerInstanceColorAppearance}, * this is derived from {@link PerInstanceColorAppearance#fragmentShaderSource}, {@link PerInstanceColorAppearance#flat}, * and {@link PerInstanceColorAppearance#faceForward}. * * @function * * @returns {String} The full GLSL fragment shader source. */ PerInstanceColorAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource; /** * Determines if the geometry is translucent based on {@link PerInstanceColorAppearance#translucent}. * * @function * * @returns {Boolean} true if the appearance is translucent. */ PerInstanceColorAppearance.prototype.isTranslucent = Appearance.prototype.isTranslucent; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @function * * @returns {Object} The render state. */ PerInstanceColorAppearance.prototype.getRenderState = Appearance.prototype.getRenderState; /** * A {@link MaterialProperty} that maps to solid color {@link Material} uniforms. * * @param {Property|Color} [color=Color.WHITE] The {@link Color} Property to be used. * * @alias ColorMaterialProperty * @constructor */ function ColorMaterialProperty(color) { this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this.color = color; } Object.defineProperties(ColorMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof ColorMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return Property.isConstant(this._color); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof ColorMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the {@link Color} {@link Property}. * @memberof ColorMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ ColorMaterialProperty.prototype.getType = function (time) { return "Color"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ ColorMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, Color.WHITE, result.color ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ ColorMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof ColorMaterialProperty && // Property.equals(this._color, other._color)) ); }; /** * Represents a command to the renderer for drawing. * * @private */ function DrawCommand(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._boundingVolume = options.boundingVolume; this._orientedBoundingBox = options.orientedBoundingBox; this._cull = defaultValue(options.cull, true); this._occlude = defaultValue(options.occlude, true); this._modelMatrix = options.modelMatrix; this._primitiveType = defaultValue( options.primitiveType, PrimitiveType$1.TRIANGLES ); this._vertexArray = options.vertexArray; this._count = options.count; this._offset = defaultValue(options.offset, 0); this._instanceCount = defaultValue(options.instanceCount, 0); this._shaderProgram = options.shaderProgram; this._uniformMap = options.uniformMap; this._renderState = options.renderState; this._framebuffer = options.framebuffer; this._pass = options.pass; this._executeInClosestFrustum = defaultValue( options.executeInClosestFrustum, false ); this._owner = options.owner; this._debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this._debugOverlappingFrustums = 0; this._castShadows = defaultValue(options.castShadows, false); this._receiveShadows = defaultValue(options.receiveShadows, false); this._pickId = options.pickId; this._pickOnly = defaultValue(options.pickOnly, false); this.dirty = true; this.lastDirtyTime = 0; /** * @private */ this.derivedCommands = {}; } Object.defineProperties(DrawCommand.prototype, { /** * The bounding volume of the geometry in world space. This is used for culling and frustum selection. *

* For best rendering performance, use the tightest possible bounding volume. Although * undefined is allowed, always try to provide a bounding volume to * allow the tightest possible near and far planes to be computed for the scene, and * minimize the number of frustums needed. *

* * @memberof DrawCommand.prototype * @type {Object} * @default undefined * * @see DrawCommand#debugShowBoundingVolume */ boundingVolume: { get: function () { return this._boundingVolume; }, set: function (value) { if (this._boundingVolume !== value) { this._boundingVolume = value; this.dirty = true; } }, }, /** * The oriented bounding box of the geometry in world space. If this is defined, it is used instead of * {@link DrawCommand#boundingVolume} for plane intersection testing. * * @memberof DrawCommand.prototype * @type {OrientedBoundingBox} * @default undefined * * @see DrawCommand#debugShowBoundingVolume */ orientedBoundingBox: { get: function () { return this._orientedBoundingBox; }, set: function (value) { if (this._orientedBoundingBox !== value) { this._orientedBoundingBox = value; this.dirty = true; } }, }, /** * When true, the renderer frustum and horizon culls the command based on its {@link DrawCommand#boundingVolume}. * If the command was already culled, set this to false for a performance improvement. * * @memberof DrawCommand.prototype * @type {Boolean} * @default true */ cull: { get: function () { return this._cull; }, set: function (value) { if (this._cull !== value) { this._cull = value; this.dirty = true; } }, }, /** * When true, the horizon culls the command based on its {@link DrawCommand#boundingVolume}. * {@link DrawCommand#cull} must also be true in order for the command to be culled. * * @memberof DrawCommand.prototype * @type {Boolean} * @default true */ occlude: { get: function () { return this._occlude; }, set: function (value) { if (this._occlude !== value) { this._occlude = value; this.dirty = true; } }, }, /** * The transformation from the geometry in model space to world space. *

* When undefined, the geometry is assumed to be defined in world space. *

* * @memberof DrawCommand.prototype * @type {Matrix4} * @default undefined */ modelMatrix: { get: function () { return this._modelMatrix; }, set: function (value) { if (this._modelMatrix !== value) { this._modelMatrix = value; this.dirty = true; } }, }, /** * The type of geometry in the vertex array. * * @memberof DrawCommand.prototype * @type {PrimitiveType} * @default PrimitiveType.TRIANGLES */ primitiveType: { get: function () { return this._primitiveType; }, set: function (value) { if (this._primitiveType !== value) { this._primitiveType = value; this.dirty = true; } }, }, /** * The vertex array. * * @memberof DrawCommand.prototype * @type {VertexArray} * @default undefined */ vertexArray: { get: function () { return this._vertexArray; }, set: function (value) { if (this._vertexArray !== value) { this._vertexArray = value; this.dirty = true; } }, }, /** * The number of vertices to draw in the vertex array. * * @memberof DrawCommand.prototype * @type {Number} * @default undefined */ count: { get: function () { return this._count; }, set: function (value) { if (this._count !== value) { this._count = value; this.dirty = true; } }, }, /** * The offset to start drawing in the vertex array. * * @memberof DrawCommand.prototype * @type {Number} * @default 0 */ offset: { get: function () { return this._offset; }, set: function (value) { if (this._offset !== value) { this._offset = value; this.dirty = true; } }, }, /** * The number of instances to draw. * * @memberof DrawCommand.prototype * @type {Number} * @default 0 */ instanceCount: { get: function () { return this._instanceCount; }, set: function (value) { if (this._instanceCount !== value) { this._instanceCount = value; this.dirty = true; } }, }, /** * The shader program to apply. * * @memberof DrawCommand.prototype * @type {ShaderProgram} * @default undefined */ shaderProgram: { get: function () { return this._shaderProgram; }, set: function (value) { if (this._shaderProgram !== value) { this._shaderProgram = value; this.dirty = true; } }, }, /** * Whether this command should cast shadows when shadowing is enabled. * * @memberof DrawCommand.prototype * @type {Boolean} * @default false */ castShadows: { get: function () { return this._castShadows; }, set: function (value) { if (this._castShadows !== value) { this._castShadows = value; this.dirty = true; } }, }, /** * Whether this command should receive shadows when shadowing is enabled. * * @memberof DrawCommand.prototype * @type {Boolean} * @default false */ receiveShadows: { get: function () { return this._receiveShadows; }, set: function (value) { if (this._receiveShadows !== value) { this._receiveShadows = value; this.dirty = true; } }, }, /** * An object with functions whose names match the uniforms in the shader program * and return values to set those uniforms. * * @memberof DrawCommand.prototype * @type {Object} * @default undefined */ uniformMap: { get: function () { return this._uniformMap; }, set: function (value) { if (this._uniformMap !== value) { this._uniformMap = value; this.dirty = true; } }, }, /** * The render state. * * @memberof DrawCommand.prototype * @type {RenderState} * @default undefined */ renderState: { get: function () { return this._renderState; }, set: function (value) { if (this._renderState !== value) { this._renderState = value; this.dirty = true; } }, }, /** * The framebuffer to draw to. * * @memberof DrawCommand.prototype * @type {Framebuffer} * @default undefined */ framebuffer: { get: function () { return this._framebuffer; }, set: function (value) { if (this._framebuffer !== value) { this._framebuffer = value; this.dirty = true; } }, }, /** * The pass when to render. * * @memberof DrawCommand.prototype * @type {Pass} * @default undefined */ pass: { get: function () { return this._pass; }, set: function (value) { if (this._pass !== value) { this._pass = value; this.dirty = true; } }, }, /** * Specifies if this command is only to be executed in the frustum closest * to the eye containing the bounding volume. Defaults to false. * * @memberof DrawCommand.prototype * @type {Boolean} * @default false */ executeInClosestFrustum: { get: function () { return this._executeInClosestFrustum; }, set: function (value) { if (this._executeInClosestFrustum !== value) { this._executeInClosestFrustum = value; this.dirty = true; } }, }, /** * The object who created this command. This is useful for debugging command * execution; it allows us to see who created a command when we only have a * reference to the command, and can be used to selectively execute commands * with {@link Scene#debugCommandFilter}. * * @memberof DrawCommand.prototype * @type {Object} * @default undefined * * @see Scene#debugCommandFilter */ owner: { get: function () { return this._owner; }, set: function (value) { if (this._owner !== value) { this._owner = value; this.dirty = true; } }, }, /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the {@link DrawCommand#boundingVolume} for this command, assuming it is a sphere, when the command executes. *

* * @memberof DrawCommand.prototype * @type {Boolean} * @default false * * @see DrawCommand#boundingVolume */ debugShowBoundingVolume: { get: function () { return this._debugShowBoundingVolume; }, set: function (value) { if (this._debugShowBoundingVolume !== value) { this._debugShowBoundingVolume = value; this.dirty = true; } }, }, /** * Used to implement Scene.debugShowFrustums. * @private */ debugOverlappingFrustums: { get: function () { return this._debugOverlappingFrustums; }, set: function (value) { if (this._debugOverlappingFrustums !== value) { this._debugOverlappingFrustums = value; this.dirty = true; } }, }, /** * A GLSL string that will evaluate to a pick id. When undefined, the command will only draw depth * during the pick pass. * * @memberof DrawCommand.prototype * @type {String} * @default undefined */ pickId: { get: function () { return this._pickId; }, set: function (value) { if (this._pickId !== value) { this._pickId = value; this.dirty = true; } }, }, /** * Whether this command should be executed in the pick pass only. * * @memberof DrawCommand.prototype * @type {Boolean} * @default false */ pickOnly: { get: function () { return this._pickOnly; }, set: function (value) { if (this._pickOnly !== value) { this._pickOnly = value; this.dirty = true; } }, }, }); /** * @private */ DrawCommand.shallowClone = function (command, result) { if (!defined(command)) { return undefined; } if (!defined(result)) { result = new DrawCommand(); } result._boundingVolume = command._boundingVolume; result._orientedBoundingBox = command._orientedBoundingBox; result._cull = command._cull; result._occlude = command._occlude; result._modelMatrix = command._modelMatrix; result._primitiveType = command._primitiveType; result._vertexArray = command._vertexArray; result._count = command._count; result._offset = command._offset; result._instanceCount = command._instanceCount; result._shaderProgram = command._shaderProgram; result._uniformMap = command._uniformMap; result._renderState = command._renderState; result._framebuffer = command._framebuffer; result._pass = command._pass; result._executeInClosestFrustum = command._executeInClosestFrustum; result._owner = command._owner; result._debugShowBoundingVolume = command._debugShowBoundingVolume; result._debugOverlappingFrustums = command._debugOverlappingFrustums; result._castShadows = command._castShadows; result._receiveShadows = command._receiveShadows; result._pickId = command._pickId; result._pickOnly = command._pickOnly; result.dirty = true; result.lastDirtyTime = 0; return result; }; /** * Executes the draw command. * * @param {Context} context The renderer context in which to draw. * @param {PassState} [passState] The state for the current render pass. */ DrawCommand.prototype.execute = function (context, passState) { context.draw(this, passState); }; /** * The render pass for a command. * * @private */ var Pass = { // If you add/modify/remove Pass constants, also change the automatic GLSL constants // that start with 'czm_pass' // // Commands are executed in order by pass up to the translucent pass. // Translucent geometry needs special handling (sorting/OIT). The compute pass // is executed first and the overlay pass is executed last. Both are not sorted // by frustum. ENVIRONMENT: 0, COMPUTE: 1, GLOBE: 2, TERRAIN_CLASSIFICATION: 3, CESIUM_3D_TILE: 4, CESIUM_3D_TILE_CLASSIFICATION: 5, CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW: 6, OPAQUE: 7, TRANSLUCENT: 8, OVERLAY: 9, NUMBER_OF_PASSES: 10, }; var Pass$1 = Object.freeze(Pass); /** * Returns frozen renderState as well as all of the object literal properties. This function is deep object freeze * function ignoring properties named "_applyFunctions". * * @private * * @param {Object} renderState * @returns {Object} Returns frozen renderState. * */ function freezeRenderState(renderState) { if (typeof renderState !== "object" || renderState === null) { return renderState; } var propName; var propNames = Object.keys(renderState); for (var i = 0; i < propNames.length; i++) { propName = propNames[i]; if ( renderState.hasOwnProperty(propName) && propName !== "_applyFunctions" ) { renderState[propName] = freezeRenderState(renderState[propName]); } } return Object.freeze(renderState); } function validateBlendEquation(blendEquation) { return ( blendEquation === WebGLConstants$1.FUNC_ADD || blendEquation === WebGLConstants$1.FUNC_SUBTRACT || blendEquation === WebGLConstants$1.FUNC_REVERSE_SUBTRACT || blendEquation === WebGLConstants$1.MIN || blendEquation === WebGLConstants$1.MAX ); } function validateBlendFunction(blendFunction) { return ( blendFunction === WebGLConstants$1.ZERO || blendFunction === WebGLConstants$1.ONE || blendFunction === WebGLConstants$1.SRC_COLOR || blendFunction === WebGLConstants$1.ONE_MINUS_SRC_COLOR || blendFunction === WebGLConstants$1.DST_COLOR || blendFunction === WebGLConstants$1.ONE_MINUS_DST_COLOR || blendFunction === WebGLConstants$1.SRC_ALPHA || blendFunction === WebGLConstants$1.ONE_MINUS_SRC_ALPHA || blendFunction === WebGLConstants$1.DST_ALPHA || blendFunction === WebGLConstants$1.ONE_MINUS_DST_ALPHA || blendFunction === WebGLConstants$1.CONSTANT_COLOR || blendFunction === WebGLConstants$1.ONE_MINUS_CONSTANT_COLOR || blendFunction === WebGLConstants$1.CONSTANT_ALPHA || blendFunction === WebGLConstants$1.ONE_MINUS_CONSTANT_ALPHA || blendFunction === WebGLConstants$1.SRC_ALPHA_SATURATE ); } function validateCullFace(cullFace) { return ( cullFace === WebGLConstants$1.FRONT || cullFace === WebGLConstants$1.BACK || cullFace === WebGLConstants$1.FRONT_AND_BACK ); } function validateDepthFunction(depthFunction) { return ( depthFunction === WebGLConstants$1.NEVER || depthFunction === WebGLConstants$1.LESS || depthFunction === WebGLConstants$1.EQUAL || depthFunction === WebGLConstants$1.LEQUAL || depthFunction === WebGLConstants$1.GREATER || depthFunction === WebGLConstants$1.NOTEQUAL || depthFunction === WebGLConstants$1.GEQUAL || depthFunction === WebGLConstants$1.ALWAYS ); } function validateStencilFunction(stencilFunction) { return ( stencilFunction === WebGLConstants$1.NEVER || stencilFunction === WebGLConstants$1.LESS || stencilFunction === WebGLConstants$1.EQUAL || stencilFunction === WebGLConstants$1.LEQUAL || stencilFunction === WebGLConstants$1.GREATER || stencilFunction === WebGLConstants$1.NOTEQUAL || stencilFunction === WebGLConstants$1.GEQUAL || stencilFunction === WebGLConstants$1.ALWAYS ); } function validateStencilOperation(stencilOperation) { return ( stencilOperation === WebGLConstants$1.ZERO || stencilOperation === WebGLConstants$1.KEEP || stencilOperation === WebGLConstants$1.REPLACE || stencilOperation === WebGLConstants$1.INCR || stencilOperation === WebGLConstants$1.DECR || stencilOperation === WebGLConstants$1.INVERT || stencilOperation === WebGLConstants$1.INCR_WRAP || stencilOperation === WebGLConstants$1.DECR_WRAP ); } /** * @private */ function RenderState(renderState) { var rs = defaultValue(renderState, defaultValue.EMPTY_OBJECT); var cull = defaultValue(rs.cull, defaultValue.EMPTY_OBJECT); var polygonOffset = defaultValue(rs.polygonOffset, defaultValue.EMPTY_OBJECT); var scissorTest = defaultValue(rs.scissorTest, defaultValue.EMPTY_OBJECT); var scissorTestRectangle = defaultValue( scissorTest.rectangle, defaultValue.EMPTY_OBJECT ); var depthRange = defaultValue(rs.depthRange, defaultValue.EMPTY_OBJECT); var depthTest = defaultValue(rs.depthTest, defaultValue.EMPTY_OBJECT); var colorMask = defaultValue(rs.colorMask, defaultValue.EMPTY_OBJECT); var blending = defaultValue(rs.blending, defaultValue.EMPTY_OBJECT); var blendingColor = defaultValue(blending.color, defaultValue.EMPTY_OBJECT); var stencilTest = defaultValue(rs.stencilTest, defaultValue.EMPTY_OBJECT); var stencilTestFrontOperation = defaultValue( stencilTest.frontOperation, defaultValue.EMPTY_OBJECT ); var stencilTestBackOperation = defaultValue( stencilTest.backOperation, defaultValue.EMPTY_OBJECT ); var sampleCoverage = defaultValue( rs.sampleCoverage, defaultValue.EMPTY_OBJECT ); var viewport = rs.viewport; this.frontFace = defaultValue(rs.frontFace, WindingOrder$1.COUNTER_CLOCKWISE); this.cull = { enabled: defaultValue(cull.enabled, false), face: defaultValue(cull.face, WebGLConstants$1.BACK), }; this.lineWidth = defaultValue(rs.lineWidth, 1.0); this.polygonOffset = { enabled: defaultValue(polygonOffset.enabled, false), factor: defaultValue(polygonOffset.factor, 0), units: defaultValue(polygonOffset.units, 0), }; this.scissorTest = { enabled: defaultValue(scissorTest.enabled, false), rectangle: BoundingRectangle.clone(scissorTestRectangle), }; this.depthRange = { near: defaultValue(depthRange.near, 0), far: defaultValue(depthRange.far, 1), }; this.depthTest = { enabled: defaultValue(depthTest.enabled, false), func: defaultValue(depthTest.func, WebGLConstants$1.LESS), // func, because function is a JavaScript keyword }; this.colorMask = { red: defaultValue(colorMask.red, true), green: defaultValue(colorMask.green, true), blue: defaultValue(colorMask.blue, true), alpha: defaultValue(colorMask.alpha, true), }; this.depthMask = defaultValue(rs.depthMask, true); this.stencilMask = defaultValue(rs.stencilMask, ~0); this.blending = { enabled: defaultValue(blending.enabled, false), color: new Color( defaultValue(blendingColor.red, 0.0), defaultValue(blendingColor.green, 0.0), defaultValue(blendingColor.blue, 0.0), defaultValue(blendingColor.alpha, 0.0) ), equationRgb: defaultValue(blending.equationRgb, WebGLConstants$1.FUNC_ADD), equationAlpha: defaultValue( blending.equationAlpha, WebGLConstants$1.FUNC_ADD ), functionSourceRgb: defaultValue( blending.functionSourceRgb, WebGLConstants$1.ONE ), functionSourceAlpha: defaultValue( blending.functionSourceAlpha, WebGLConstants$1.ONE ), functionDestinationRgb: defaultValue( blending.functionDestinationRgb, WebGLConstants$1.ZERO ), functionDestinationAlpha: defaultValue( blending.functionDestinationAlpha, WebGLConstants$1.ZERO ), }; this.stencilTest = { enabled: defaultValue(stencilTest.enabled, false), frontFunction: defaultValue( stencilTest.frontFunction, WebGLConstants$1.ALWAYS ), backFunction: defaultValue(stencilTest.backFunction, WebGLConstants$1.ALWAYS), reference: defaultValue(stencilTest.reference, 0), mask: defaultValue(stencilTest.mask, ~0), frontOperation: { fail: defaultValue(stencilTestFrontOperation.fail, WebGLConstants$1.KEEP), zFail: defaultValue(stencilTestFrontOperation.zFail, WebGLConstants$1.KEEP), zPass: defaultValue(stencilTestFrontOperation.zPass, WebGLConstants$1.KEEP), }, backOperation: { fail: defaultValue(stencilTestBackOperation.fail, WebGLConstants$1.KEEP), zFail: defaultValue(stencilTestBackOperation.zFail, WebGLConstants$1.KEEP), zPass: defaultValue(stencilTestBackOperation.zPass, WebGLConstants$1.KEEP), }, }; this.sampleCoverage = { enabled: defaultValue(sampleCoverage.enabled, false), value: defaultValue(sampleCoverage.value, 1.0), invert: defaultValue(sampleCoverage.invert, false), }; this.viewport = defined(viewport) ? new BoundingRectangle( viewport.x, viewport.y, viewport.width, viewport.height ) : undefined; //>>includeStart('debug', pragmas.debug); if ( this.lineWidth < ContextLimits.minimumAliasedLineWidth || this.lineWidth > ContextLimits.maximumAliasedLineWidth ) { throw new DeveloperError( "renderState.lineWidth is out of range. Check minimumAliasedLineWidth and maximumAliasedLineWidth." ); } if (!WindingOrder$1.validate(this.frontFace)) { throw new DeveloperError("Invalid renderState.frontFace."); } if (!validateCullFace(this.cull.face)) { throw new DeveloperError("Invalid renderState.cull.face."); } if ( this.scissorTest.rectangle.width < 0 || this.scissorTest.rectangle.height < 0 ) { throw new DeveloperError( "renderState.scissorTest.rectangle.width and renderState.scissorTest.rectangle.height must be greater than or equal to zero." ); } if (this.depthRange.near > this.depthRange.far) { // WebGL specific - not an error in GL ES throw new DeveloperError( "renderState.depthRange.near can not be greater than renderState.depthRange.far." ); } if (this.depthRange.near < 0) { // Would be clamped by GL throw new DeveloperError( "renderState.depthRange.near must be greater than or equal to zero." ); } if (this.depthRange.far > 1) { // Would be clamped by GL throw new DeveloperError( "renderState.depthRange.far must be less than or equal to one." ); } if (!validateDepthFunction(this.depthTest.func)) { throw new DeveloperError("Invalid renderState.depthTest.func."); } if ( this.blending.color.red < 0.0 || this.blending.color.red > 1.0 || this.blending.color.green < 0.0 || this.blending.color.green > 1.0 || this.blending.color.blue < 0.0 || this.blending.color.blue > 1.0 || this.blending.color.alpha < 0.0 || this.blending.color.alpha > 1.0 ) { // Would be clamped by GL throw new DeveloperError( "renderState.blending.color components must be greater than or equal to zero and less than or equal to one." ); } if (!validateBlendEquation(this.blending.equationRgb)) { throw new DeveloperError("Invalid renderState.blending.equationRgb."); } if (!validateBlendEquation(this.blending.equationAlpha)) { throw new DeveloperError("Invalid renderState.blending.equationAlpha."); } if (!validateBlendFunction(this.blending.functionSourceRgb)) { throw new DeveloperError("Invalid renderState.blending.functionSourceRgb."); } if (!validateBlendFunction(this.blending.functionSourceAlpha)) { throw new DeveloperError( "Invalid renderState.blending.functionSourceAlpha." ); } if (!validateBlendFunction(this.blending.functionDestinationRgb)) { throw new DeveloperError( "Invalid renderState.blending.functionDestinationRgb." ); } if (!validateBlendFunction(this.blending.functionDestinationAlpha)) { throw new DeveloperError( "Invalid renderState.blending.functionDestinationAlpha." ); } if (!validateStencilFunction(this.stencilTest.frontFunction)) { throw new DeveloperError("Invalid renderState.stencilTest.frontFunction."); } if (!validateStencilFunction(this.stencilTest.backFunction)) { throw new DeveloperError("Invalid renderState.stencilTest.backFunction."); } if (!validateStencilOperation(this.stencilTest.frontOperation.fail)) { throw new DeveloperError( "Invalid renderState.stencilTest.frontOperation.fail." ); } if (!validateStencilOperation(this.stencilTest.frontOperation.zFail)) { throw new DeveloperError( "Invalid renderState.stencilTest.frontOperation.zFail." ); } if (!validateStencilOperation(this.stencilTest.frontOperation.zPass)) { throw new DeveloperError( "Invalid renderState.stencilTest.frontOperation.zPass." ); } if (!validateStencilOperation(this.stencilTest.backOperation.fail)) { throw new DeveloperError( "Invalid renderState.stencilTest.backOperation.fail." ); } if (!validateStencilOperation(this.stencilTest.backOperation.zFail)) { throw new DeveloperError( "Invalid renderState.stencilTest.backOperation.zFail." ); } if (!validateStencilOperation(this.stencilTest.backOperation.zPass)) { throw new DeveloperError( "Invalid renderState.stencilTest.backOperation.zPass." ); } if (defined(this.viewport)) { if (this.viewport.width < 0) { throw new DeveloperError( "renderState.viewport.width must be greater than or equal to zero." ); } if (this.viewport.height < 0) { throw new DeveloperError( "renderState.viewport.height must be greater than or equal to zero." ); } if (this.viewport.width > ContextLimits.maximumViewportWidth) { throw new DeveloperError( "renderState.viewport.width must be less than or equal to the maximum viewport width (" + ContextLimits.maximumViewportWidth.toString() + "). Check maximumViewportWidth." ); } if (this.viewport.height > ContextLimits.maximumViewportHeight) { throw new DeveloperError( "renderState.viewport.height must be less than or equal to the maximum viewport height (" + ContextLimits.maximumViewportHeight.toString() + "). Check maximumViewportHeight." ); } } //>>includeEnd('debug'); this.id = 0; this._applyFunctions = []; } var nextRenderStateId = 0; var renderStateCache = {}; /** * Validates and then finds or creates an immutable render state, which defines the pipeline * state for a {@link DrawCommand} or {@link ClearCommand}. All inputs states are optional. Omitted states * use the defaults shown in the example below. * * @param {Object} [renderState] The states defining the render state as shown in the example below. * * @exception {RuntimeError} renderState.lineWidth is out of range. * @exception {DeveloperError} Invalid renderState.frontFace. * @exception {DeveloperError} Invalid renderState.cull.face. * @exception {DeveloperError} scissorTest.rectangle.width and scissorTest.rectangle.height must be greater than or equal to zero. * @exception {DeveloperError} renderState.depthRange.near can't be greater than renderState.depthRange.far. * @exception {DeveloperError} renderState.depthRange.near must be greater than or equal to zero. * @exception {DeveloperError} renderState.depthRange.far must be less than or equal to zero. * @exception {DeveloperError} Invalid renderState.depthTest.func. * @exception {DeveloperError} renderState.blending.color components must be greater than or equal to zero and less than or equal to one * @exception {DeveloperError} Invalid renderState.blending.equationRgb. * @exception {DeveloperError} Invalid renderState.blending.equationAlpha. * @exception {DeveloperError} Invalid renderState.blending.functionSourceRgb. * @exception {DeveloperError} Invalid renderState.blending.functionSourceAlpha. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationRgb. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationAlpha. * @exception {DeveloperError} Invalid renderState.stencilTest.frontFunction. * @exception {DeveloperError} Invalid renderState.stencilTest.backFunction. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.fail. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zFail. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zPass. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.fail. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zFail. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zPass. * @exception {DeveloperError} renderState.viewport.width must be greater than or equal to zero. * @exception {DeveloperError} renderState.viewport.width must be less than or equal to the maximum viewport width. * @exception {DeveloperError} renderState.viewport.height must be greater than or equal to zero. * @exception {DeveloperError} renderState.viewport.height must be less than or equal to the maximum viewport height. * * * @example * var defaults = { * frontFace : WindingOrder.COUNTER_CLOCKWISE, * cull : { * enabled : false, * face : CullFace.BACK * }, * lineWidth : 1, * polygonOffset : { * enabled : false, * factor : 0, * units : 0 * }, * scissorTest : { * enabled : false, * rectangle : { * x : 0, * y : 0, * width : 0, * height : 0 * } * }, * depthRange : { * near : 0, * far : 1 * }, * depthTest : { * enabled : false, * func : DepthFunction.LESS * }, * colorMask : { * red : true, * green : true, * blue : true, * alpha : true * }, * depthMask : true, * stencilMask : ~0, * blending : { * enabled : false, * color : { * red : 0.0, * green : 0.0, * blue : 0.0, * alpha : 0.0 * }, * equationRgb : BlendEquation.ADD, * equationAlpha : BlendEquation.ADD, * functionSourceRgb : BlendFunction.ONE, * functionSourceAlpha : BlendFunction.ONE, * functionDestinationRgb : BlendFunction.ZERO, * functionDestinationAlpha : BlendFunction.ZERO * }, * stencilTest : { * enabled : false, * frontFunction : StencilFunction.ALWAYS, * backFunction : StencilFunction.ALWAYS, * reference : 0, * mask : ~0, * frontOperation : { * fail : StencilOperation.KEEP, * zFail : StencilOperation.KEEP, * zPass : StencilOperation.KEEP * }, * backOperation : { * fail : StencilOperation.KEEP, * zFail : StencilOperation.KEEP, * zPass : StencilOperation.KEEP * } * }, * sampleCoverage : { * enabled : false, * value : 1.0, * invert : false * } * }; * * var rs = RenderState.fromCache(defaults); * * @see DrawCommand * @see ClearCommand * * @private */ RenderState.fromCache = function (renderState) { var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { ++cachedState.referenceCount; return cachedState.state; } // Cache miss. Fully define render state and try again. var states = new RenderState(renderState); var fullKey = JSON.stringify(states); cachedState = renderStateCache[fullKey]; if (!defined(cachedState)) { states.id = nextRenderStateId++; //>>includeStart('debug', pragmas.debug); states = freezeRenderState(states); //>>includeEnd('debug'); cachedState = { referenceCount: 0, state: states, }; // Cache full render state. Multiple partially defined render states may map to this. renderStateCache[fullKey] = cachedState; } ++cachedState.referenceCount; // Cache partial render state so we can skip validation on a cache hit for a partially defined render state renderStateCache[partialKey] = { referenceCount: 1, state: cachedState.state, }; return cachedState.state; }; /** * @private */ RenderState.removeFromCache = function (renderState) { var states = new RenderState(renderState); var fullKey = JSON.stringify(states); var fullCachedState = renderStateCache[fullKey]; // decrement partial key reference count var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { --cachedState.referenceCount; if (cachedState.referenceCount === 0) { // remove partial key delete renderStateCache[partialKey]; // decrement full key reference count if (defined(fullCachedState)) { --fullCachedState.referenceCount; } } } // remove full key if reference count is zero if (defined(fullCachedState) && fullCachedState.referenceCount === 0) { delete renderStateCache[fullKey]; } }; /** * This function is for testing purposes only. * @private */ RenderState.getCache = function () { return renderStateCache; }; /** * This function is for testing purposes only. * @private */ RenderState.clearCache = function () { renderStateCache = {}; }; function enableOrDisable(gl, glEnum, enable) { if (enable) { gl.enable(glEnum); } else { gl.disable(glEnum); } } function applyFrontFace(gl, renderState) { gl.frontFace(renderState.frontFace); } function applyCull(gl, renderState) { var cull = renderState.cull; var enabled = cull.enabled; enableOrDisable(gl, gl.CULL_FACE, enabled); if (enabled) { gl.cullFace(cull.face); } } function applyLineWidth(gl, renderState) { gl.lineWidth(renderState.lineWidth); } function applyPolygonOffset(gl, renderState) { var polygonOffset = renderState.polygonOffset; var enabled = polygonOffset.enabled; enableOrDisable(gl, gl.POLYGON_OFFSET_FILL, enabled); if (enabled) { gl.polygonOffset(polygonOffset.factor, polygonOffset.units); } } function applyScissorTest(gl, renderState, passState) { var scissorTest = renderState.scissorTest; var enabled = defined(passState.scissorTest) ? passState.scissorTest.enabled : scissorTest.enabled; enableOrDisable(gl, gl.SCISSOR_TEST, enabled); if (enabled) { var rectangle = defined(passState.scissorTest) ? passState.scissorTest.rectangle : scissorTest.rectangle; gl.scissor(rectangle.x, rectangle.y, rectangle.width, rectangle.height); } } function applyDepthRange(gl, renderState) { var depthRange = renderState.depthRange; gl.depthRange(depthRange.near, depthRange.far); } function applyDepthTest(gl, renderState) { var depthTest = renderState.depthTest; var enabled = depthTest.enabled; enableOrDisable(gl, gl.DEPTH_TEST, enabled); if (enabled) { gl.depthFunc(depthTest.func); } } function applyColorMask(gl, renderState) { var colorMask = renderState.colorMask; gl.colorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha); } function applyDepthMask(gl, renderState) { gl.depthMask(renderState.depthMask); } function applyStencilMask(gl, renderState) { gl.stencilMask(renderState.stencilMask); } function applyBlendingColor(gl, color) { gl.blendColor(color.red, color.green, color.blue, color.alpha); } function applyBlending(gl, renderState, passState) { var blending = renderState.blending; var enabled = defined(passState.blendingEnabled) ? passState.blendingEnabled : blending.enabled; enableOrDisable(gl, gl.BLEND, enabled); if (enabled) { applyBlendingColor(gl, blending.color); gl.blendEquationSeparate(blending.equationRgb, blending.equationAlpha); gl.blendFuncSeparate( blending.functionSourceRgb, blending.functionDestinationRgb, blending.functionSourceAlpha, blending.functionDestinationAlpha ); } } function applyStencilTest(gl, renderState) { var stencilTest = renderState.stencilTest; var enabled = stencilTest.enabled; enableOrDisable(gl, gl.STENCIL_TEST, enabled); if (enabled) { var frontFunction = stencilTest.frontFunction; var backFunction = stencilTest.backFunction; var reference = stencilTest.reference; var mask = stencilTest.mask; // Section 6.8 of the WebGL spec requires the reference and masks to be the same for // front- and back-face tests. This call prevents invalid operation errors when calling // stencilFuncSeparate on Firefox. Perhaps they should delay validation to avoid requiring this. gl.stencilFunc(frontFunction, reference, mask); gl.stencilFuncSeparate(gl.BACK, backFunction, reference, mask); gl.stencilFuncSeparate(gl.FRONT, frontFunction, reference, mask); var frontOperation = stencilTest.frontOperation; var frontOperationFail = frontOperation.fail; var frontOperationZFail = frontOperation.zFail; var frontOperationZPass = frontOperation.zPass; gl.stencilOpSeparate( gl.FRONT, frontOperationFail, frontOperationZFail, frontOperationZPass ); var backOperation = stencilTest.backOperation; var backOperationFail = backOperation.fail; var backOperationZFail = backOperation.zFail; var backOperationZPass = backOperation.zPass; gl.stencilOpSeparate( gl.BACK, backOperationFail, backOperationZFail, backOperationZPass ); } } function applySampleCoverage(gl, renderState) { var sampleCoverage = renderState.sampleCoverage; var enabled = sampleCoverage.enabled; enableOrDisable(gl, gl.SAMPLE_COVERAGE, enabled); if (enabled) { gl.sampleCoverage(sampleCoverage.value, sampleCoverage.invert); } } var scratchViewport$2 = new BoundingRectangle(); function applyViewport(gl, renderState, passState) { var viewport = defaultValue(renderState.viewport, passState.viewport); if (!defined(viewport)) { viewport = scratchViewport$2; viewport.width = passState.context.drawingBufferWidth; viewport.height = passState.context.drawingBufferHeight; } passState.context.uniformState.viewport = viewport; gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); } RenderState.apply = function (gl, renderState, passState) { applyFrontFace(gl, renderState); applyCull(gl, renderState); applyLineWidth(gl, renderState); applyPolygonOffset(gl, renderState); applyDepthRange(gl, renderState); applyDepthTest(gl, renderState); applyColorMask(gl, renderState); applyDepthMask(gl, renderState); applyStencilMask(gl, renderState); applyStencilTest(gl, renderState); applySampleCoverage(gl, renderState); applyScissorTest(gl, renderState, passState); applyBlending(gl, renderState, passState); applyViewport(gl, renderState, passState); }; function createFuncs(previousState, nextState) { var funcs = []; if (previousState.frontFace !== nextState.frontFace) { funcs.push(applyFrontFace); } if ( previousState.cull.enabled !== nextState.cull.enabled || previousState.cull.face !== nextState.cull.face ) { funcs.push(applyCull); } if (previousState.lineWidth !== nextState.lineWidth) { funcs.push(applyLineWidth); } if ( previousState.polygonOffset.enabled !== nextState.polygonOffset.enabled || previousState.polygonOffset.factor !== nextState.polygonOffset.factor || previousState.polygonOffset.units !== nextState.polygonOffset.units ) { funcs.push(applyPolygonOffset); } if ( previousState.depthRange.near !== nextState.depthRange.near || previousState.depthRange.far !== nextState.depthRange.far ) { funcs.push(applyDepthRange); } if ( previousState.depthTest.enabled !== nextState.depthTest.enabled || previousState.depthTest.func !== nextState.depthTest.func ) { funcs.push(applyDepthTest); } if ( previousState.colorMask.red !== nextState.colorMask.red || previousState.colorMask.green !== nextState.colorMask.green || previousState.colorMask.blue !== nextState.colorMask.blue || previousState.colorMask.alpha !== nextState.colorMask.alpha ) { funcs.push(applyColorMask); } if (previousState.depthMask !== nextState.depthMask) { funcs.push(applyDepthMask); } if (previousState.stencilMask !== nextState.stencilMask) { funcs.push(applyStencilMask); } if ( previousState.stencilTest.enabled !== nextState.stencilTest.enabled || previousState.stencilTest.frontFunction !== nextState.stencilTest.frontFunction || previousState.stencilTest.backFunction !== nextState.stencilTest.backFunction || previousState.stencilTest.reference !== nextState.stencilTest.reference || previousState.stencilTest.mask !== nextState.stencilTest.mask || previousState.stencilTest.frontOperation.fail !== nextState.stencilTest.frontOperation.fail || previousState.stencilTest.frontOperation.zFail !== nextState.stencilTest.frontOperation.zFail || previousState.stencilTest.backOperation.fail !== nextState.stencilTest.backOperation.fail || previousState.stencilTest.backOperation.zFail !== nextState.stencilTest.backOperation.zFail || previousState.stencilTest.backOperation.zPass !== nextState.stencilTest.backOperation.zPass ) { funcs.push(applyStencilTest); } if ( previousState.sampleCoverage.enabled !== nextState.sampleCoverage.enabled || previousState.sampleCoverage.value !== nextState.sampleCoverage.value || previousState.sampleCoverage.invert !== nextState.sampleCoverage.invert ) { funcs.push(applySampleCoverage); } return funcs; } RenderState.partialApply = function ( gl, previousRenderState, renderState, previousPassState, passState, clear ) { if (previousRenderState !== renderState) { // When a new render state is applied, instead of making WebGL calls for all the states or first // comparing the states one-by-one with the previous state (basically a linear search), we take // advantage of RenderState's immutability, and store a dynamically populated sparse data structure // containing functions that make the minimum number of WebGL calls when transitioning from one state // to the other. In practice, this works well since state-to-state transitions generally only require a // few WebGL calls, especially if commands are stored by state. var funcs = renderState._applyFunctions[previousRenderState.id]; if (!defined(funcs)) { funcs = createFuncs(previousRenderState, renderState); renderState._applyFunctions[previousRenderState.id] = funcs; } var len = funcs.length; for (var i = 0; i < len; ++i) { funcs[i](gl, renderState); } } var previousScissorTest = defined(previousPassState.scissorTest) ? previousPassState.scissorTest : previousRenderState.scissorTest; var scissorTest = defined(passState.scissorTest) ? passState.scissorTest : renderState.scissorTest; // Our scissor rectangle can get out of sync with the GL scissor rectangle on clears. // Seems to be a problem only on ANGLE. See https://github.com/CesiumGS/cesium/issues/2994 if (previousScissorTest !== scissorTest || clear) { applyScissorTest(gl, renderState, passState); } var previousBlendingEnabled = defined(previousPassState.blendingEnabled) ? previousPassState.blendingEnabled : previousRenderState.blending.enabled; var blendingEnabled = defined(passState.blendingEnabled) ? passState.blendingEnabled : renderState.blending.enabled; if ( previousBlendingEnabled !== blendingEnabled || (blendingEnabled && previousRenderState.blending !== renderState.blending) ) { applyBlending(gl, renderState, passState); } if ( previousRenderState !== renderState || previousPassState !== passState || previousPassState.context !== passState.context ) { applyViewport(gl, renderState, passState); } }; RenderState.getState = function (renderState) { //>>includeStart('debug', pragmas.debug); if (!defined(renderState)) { throw new DeveloperError("renderState is required."); } //>>includeEnd('debug'); return { frontFace: renderState.frontFace, cull: { enabled: renderState.cull.enabled, face: renderState.cull.face, }, lineWidth: renderState.lineWidth, polygonOffset: { enabled: renderState.polygonOffset.enabled, factor: renderState.polygonOffset.factor, units: renderState.polygonOffset.units, }, scissorTest: { enabled: renderState.scissorTest.enabled, rectangle: BoundingRectangle.clone(renderState.scissorTest.rectangle), }, depthRange: { near: renderState.depthRange.near, far: renderState.depthRange.far, }, depthTest: { enabled: renderState.depthTest.enabled, func: renderState.depthTest.func, }, colorMask: { red: renderState.colorMask.red, green: renderState.colorMask.green, blue: renderState.colorMask.blue, alpha: renderState.colorMask.alpha, }, depthMask: renderState.depthMask, stencilMask: renderState.stencilMask, blending: { enabled: renderState.blending.enabled, color: Color.clone(renderState.blending.color), equationRgb: renderState.blending.equationRgb, equationAlpha: renderState.blending.equationAlpha, functionSourceRgb: renderState.blending.functionSourceRgb, functionSourceAlpha: renderState.blending.functionSourceAlpha, functionDestinationRgb: renderState.blending.functionDestinationRgb, functionDestinationAlpha: renderState.blending.functionDestinationAlpha, }, stencilTest: { enabled: renderState.stencilTest.enabled, frontFunction: renderState.stencilTest.frontFunction, backFunction: renderState.stencilTest.backFunction, reference: renderState.stencilTest.reference, mask: renderState.stencilTest.mask, frontOperation: { fail: renderState.stencilTest.frontOperation.fail, zFail: renderState.stencilTest.frontOperation.zFail, zPass: renderState.stencilTest.frontOperation.zPass, }, backOperation: { fail: renderState.stencilTest.backOperation.fail, zFail: renderState.stencilTest.backOperation.zFail, zPass: renderState.stencilTest.backOperation.zPass, }, }, sampleCoverage: { enabled: renderState.sampleCoverage.enabled, value: renderState.sampleCoverage.value, invert: renderState.sampleCoverage.invert, }, viewport: defined(renderState.viewport) ? BoundingRectangle.clone(renderState.viewport) : undefined, }; }; var viewerPositionWCScratch = new Cartesian3(); function AutomaticUniform(options) { this._size = options.size; this._datatype = options.datatype; this.getValue = options.getValue; } var datatypeToGlsl = {}; datatypeToGlsl[WebGLConstants$1.FLOAT] = "float"; datatypeToGlsl[WebGLConstants$1.FLOAT_VEC2] = "vec2"; datatypeToGlsl[WebGLConstants$1.FLOAT_VEC3] = "vec3"; datatypeToGlsl[WebGLConstants$1.FLOAT_VEC4] = "vec4"; datatypeToGlsl[WebGLConstants$1.INT] = "int"; datatypeToGlsl[WebGLConstants$1.INT_VEC2] = "ivec2"; datatypeToGlsl[WebGLConstants$1.INT_VEC3] = "ivec3"; datatypeToGlsl[WebGLConstants$1.INT_VEC4] = "ivec4"; datatypeToGlsl[WebGLConstants$1.BOOL] = "bool"; datatypeToGlsl[WebGLConstants$1.BOOL_VEC2] = "bvec2"; datatypeToGlsl[WebGLConstants$1.BOOL_VEC3] = "bvec3"; datatypeToGlsl[WebGLConstants$1.BOOL_VEC4] = "bvec4"; datatypeToGlsl[WebGLConstants$1.FLOAT_MAT2] = "mat2"; datatypeToGlsl[WebGLConstants$1.FLOAT_MAT3] = "mat3"; datatypeToGlsl[WebGLConstants$1.FLOAT_MAT4] = "mat4"; datatypeToGlsl[WebGLConstants$1.SAMPLER_2D] = "sampler2D"; datatypeToGlsl[WebGLConstants$1.SAMPLER_CUBE] = "samplerCube"; AutomaticUniform.prototype.getDeclaration = function (name) { var declaration = "uniform " + datatypeToGlsl[this._datatype] + " " + name; var size = this._size; if (size === 1) { declaration += ";"; } else { declaration += "[" + size.toString() + "];"; } return declaration; }; /** * @private */ var AutomaticUniforms = { /** * An automatic GLSL uniform containing the viewport's x, y, width, * and height properties in an vec4's x, y, z, * and w components, respectively. * * @example * // GLSL declaration * uniform vec4 czm_viewport; * * // Scale the window coordinate components to [0, 1] by dividing * // by the viewport's width and height. * vec2 v = gl_FragCoord.xy / czm_viewport.zw; * * @see Context#getViewport */ czm_viewport: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC4, getValue: function (uniformState) { return uniformState.viewportCartesian4; }, }), /** * An automatic GLSL uniform representing a 4x4 orthographic projection matrix that * transforms window coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. *

* This transform is useful when a vertex shader inputs or manipulates window coordinates * as done by {@link BillboardCollection}. *

* Do not confuse {@link czm_viewportTransformation} with czm_viewportOrthographic. * The former transforms from normalized device coordinates to window coordinates; the later transforms * from window coordinates to clip coordinates, and is often used to assign to gl_Position. * * @example * // GLSL declaration * uniform mat4 czm_viewportOrthographic; * * // Example * gl_Position = czm_viewportOrthographic * vec4(windowPosition, 0.0, 1.0); * * @see UniformState#viewportOrthographic * @see czm_viewport * @see czm_viewportTransformation * @see BillboardCollection */ czm_viewportOrthographic: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.viewportOrthographic; }, }), /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms normalized device coordinates to window coordinates. The context's * full viewport is used, and the depth range is assumed to be near = 0 * and far = 1. *

* This transform is useful when there is a need to manipulate window coordinates * in a vertex shader as done by {@link BillboardCollection}. In many cases, * this matrix will not be used directly; instead, {@link czm_modelToWindowCoordinates} * will be used to transform directly from model to window coordinates. *

* Do not confuse czm_viewportTransformation with {@link czm_viewportOrthographic}. * The former transforms from normalized device coordinates to window coordinates; the later transforms * from window coordinates to clip coordinates, and is often used to assign to gl_Position. * * @example * // GLSL declaration * uniform mat4 czm_viewportTransformation; * * // Use czm_viewportTransformation as part of the * // transform from model to window coordinates. * vec4 q = czm_modelViewProjection * positionMC; // model to clip coordinates * q.xyz /= q.w; // clip to normalized device coordinates (ndc) * q.xyz = (czm_viewportTransformation * vec4(q.xyz, 1.0)).xyz; // ndc to window coordinates * * @see UniformState#viewportTransformation * @see czm_viewport * @see czm_viewportOrthographic * @see czm_modelToWindowCoordinates * @see BillboardCollection */ czm_viewportTransformation: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.viewportTransformation; }, }), /** * An automatic GLSL uniform representing the depth of the scene * after the globe pass and then updated after the 3D Tiles pass. * The depth is packed into an RGBA texture. * * @example * // GLSL declaration * uniform sampler2D czm_globeDepthTexture; * * // Get the depth at the current fragment * vec2 coords = gl_FragCoord.xy / czm_viewport.zw; * float depth = czm_unpackDepth(texture2D(czm_globeDepthTexture, coords)); */ czm_globeDepthTexture: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.SAMPLER_2D, getValue: function (uniformState) { return uniformState.globeDepthTexture; }, }), /** * An automatic GLSL uniform representing a 4x4 model transformation matrix that * transforms model coordinates to world coordinates. * * @example * // GLSL declaration * uniform mat4 czm_model; * * // Example * vec4 worldPosition = czm_model * modelPosition; * * @see UniformState#model * @see czm_inverseModel * @see czm_modelView * @see czm_modelViewProjection */ czm_model: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.model; }, }), /** * An automatic GLSL uniform representing a 4x4 model transformation matrix that * transforms world coordinates to model coordinates. * * @example * // GLSL declaration * uniform mat4 czm_inverseModel; * * // Example * vec4 modelPosition = czm_inverseModel * worldPosition; * * @see UniformState#inverseModel * @see czm_model * @see czm_inverseModelView */ czm_inverseModel: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseModel; }, }), /** * An automatic GLSL uniform representing a 4x4 view transformation matrix that * transforms world coordinates to eye coordinates. * * @example * // GLSL declaration * uniform mat4 czm_view; * * // Example * vec4 eyePosition = czm_view * worldPosition; * * @see UniformState#view * @see czm_viewRotation * @see czm_modelView * @see czm_viewProjection * @see czm_modelViewProjection * @see czm_inverseView */ czm_view: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.view; }, }), /** * An automatic GLSL uniform representing a 4x4 view transformation matrix that * transforms 3D world coordinates to eye coordinates. In 3D mode, this is identical to * {@link czm_view}, but in 2D and Columbus View it represents the view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat4 czm_view3D; * * // Example * vec4 eyePosition3D = czm_view3D * worldPosition3D; * * @see UniformState#view3D * @see czm_view */ czm_view3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.view3D; }, }), /** * An automatic GLSL uniform representing a 3x3 view rotation matrix that * transforms vectors in world coordinates to eye coordinates. * * @example * // GLSL declaration * uniform mat3 czm_viewRotation; * * // Example * vec3 eyeVector = czm_viewRotation * worldVector; * * @see UniformState#viewRotation * @see czm_view * @see czm_inverseView * @see czm_inverseViewRotation */ czm_viewRotation: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.viewRotation; }, }), /** * An automatic GLSL uniform representing a 3x3 view rotation matrix that * transforms vectors in 3D world coordinates to eye coordinates. In 3D mode, this is identical to * {@link czm_viewRotation}, but in 2D and Columbus View it represents the view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat3 czm_viewRotation3D; * * // Example * vec3 eyeVector = czm_viewRotation3D * worldVector; * * @see UniformState#viewRotation3D * @see czm_viewRotation */ czm_viewRotation3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.viewRotation3D; }, }), /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from eye coordinates to world coordinates. * * @example * // GLSL declaration * uniform mat4 czm_inverseView; * * // Example * vec4 worldPosition = czm_inverseView * eyePosition; * * @see UniformState#inverseView * @see czm_view * @see czm_inverseNormal */ czm_inverseView: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseView; }, }), /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from 3D eye coordinates to world coordinates. In 3D mode, this is identical to * {@link czm_inverseView}, but in 2D and Columbus View it represents the inverse view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat4 czm_inverseView3D; * * // Example * vec4 worldPosition = czm_inverseView3D * eyePosition; * * @see UniformState#inverseView3D * @see czm_inverseView */ czm_inverseView3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseView3D; }, }), /** * An automatic GLSL uniform representing a 3x3 rotation matrix that * transforms vectors from eye coordinates to world coordinates. * * @example * // GLSL declaration * uniform mat3 czm_inverseViewRotation; * * // Example * vec4 worldVector = czm_inverseViewRotation * eyeVector; * * @see UniformState#inverseView * @see czm_view * @see czm_viewRotation * @see czm_inverseViewRotation */ czm_inverseViewRotation: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.inverseViewRotation; }, }), /** * An automatic GLSL uniform representing a 3x3 rotation matrix that * transforms vectors from 3D eye coordinates to world coordinates. In 3D mode, this is identical to * {@link czm_inverseViewRotation}, but in 2D and Columbus View it represents the inverse view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat3 czm_inverseViewRotation3D; * * // Example * vec4 worldVector = czm_inverseViewRotation3D * eyeVector; * * @see UniformState#inverseView3D * @see czm_inverseViewRotation */ czm_inverseViewRotation3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.inverseViewRotation3D; }, }), /** * An automatic GLSL uniform representing a 4x4 projection transformation matrix that * transforms eye coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_projection; * * // Example * gl_Position = czm_projection * eyePosition; * * @see UniformState#projection * @see czm_viewProjection * @see czm_modelViewProjection * @see czm_infiniteProjection */ czm_projection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.projection; }, }), /** * An automatic GLSL uniform representing a 4x4 inverse projection transformation matrix that * transforms from clip coordinates to eye coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_inverseProjection; * * // Example * vec4 eyePosition = czm_inverseProjection * clipPosition; * * @see UniformState#inverseProjection * @see czm_projection */ czm_inverseProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 projection transformation matrix with the far plane at infinity, * that transforms eye coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. An infinite far plane is used * in algorithms like shadow volumes and GPU ray casting with proxy geometry to ensure that triangles * are not clipped by the far plane. * * @example * // GLSL declaration * uniform mat4 czm_infiniteProjection; * * // Example * gl_Position = czm_infiniteProjection * eyePosition; * * @see UniformState#infiniteProjection * @see czm_projection * @see czm_modelViewInfiniteProjection */ czm_infiniteProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.infiniteProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view transformation matrix that * transforms model coordinates to eye coordinates. *

* Positions should be transformed to eye coordinates using czm_modelView and * normals should be transformed using {@link czm_normal}. * * @example * // GLSL declaration * uniform mat4 czm_modelView; * * // Example * vec4 eyePosition = czm_modelView * modelPosition; * * // The above is equivalent to, but more efficient than: * vec4 eyePosition = czm_view * czm_model * modelPosition; * * @see UniformState#modelView * @see czm_model * @see czm_view * @see czm_modelViewProjection * @see czm_normal */ czm_modelView: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelView; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view transformation matrix that * transforms 3D model coordinates to eye coordinates. In 3D mode, this is identical to * {@link czm_modelView}, but in 2D and Columbus View it represents the model-view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. *

* Positions should be transformed to eye coordinates using czm_modelView3D and * normals should be transformed using {@link czm_normal3D}. * * @example * // GLSL declaration * uniform mat4 czm_modelView3D; * * // Example * vec4 eyePosition = czm_modelView3D * modelPosition; * * // The above is equivalent to, but more efficient than: * vec4 eyePosition = czm_view3D * czm_model * modelPosition; * * @see UniformState#modelView3D * @see czm_modelView */ czm_modelView3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelView3D; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view transformation matrix that * transforms model coordinates, relative to the eye, to eye coordinates. This is used * in conjunction with {@link czm_translateRelativeToEye}. * * @example * // GLSL declaration * uniform mat4 czm_modelViewRelativeToEye; * * // Example * attribute vec3 positionHigh; * attribute vec3 positionLow; * * void main() * { * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); * gl_Position = czm_projection * (czm_modelViewRelativeToEye * p); * } * * @see czm_modelViewProjectionRelativeToEye * @see czm_translateRelativeToEye * @see EncodedCartesian3 */ czm_modelViewRelativeToEye: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelViewRelativeToEye; }, }), /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from eye coordinates to model coordinates. * * @example * // GLSL declaration * uniform mat4 czm_inverseModelView; * * // Example * vec4 modelPosition = czm_inverseModelView * eyePosition; * * @see UniformState#inverseModelView * @see czm_modelView */ czm_inverseModelView: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseModelView; }, }), /** * An automatic GLSL uniform representing a 4x4 transformation matrix that * transforms from eye coordinates to 3D model coordinates. In 3D mode, this is identical to * {@link czm_inverseModelView}, but in 2D and Columbus View it represents the inverse model-view matrix * as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat4 czm_inverseModelView3D; * * // Example * vec4 modelPosition = czm_inverseModelView3D * eyePosition; * * @see UniformState#inverseModelView * @see czm_inverseModelView * @see czm_modelView3D */ czm_inverseModelView3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseModelView3D; }, }), /** * An automatic GLSL uniform representing a 4x4 view-projection transformation matrix that * transforms world coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_viewProjection; * * // Example * vec4 gl_Position = czm_viewProjection * czm_model * modelPosition; * * // The above is equivalent to, but more efficient than: * gl_Position = czm_projection * czm_view * czm_model * modelPosition; * * @see UniformState#viewProjection * @see czm_view * @see czm_projection * @see czm_modelViewProjection * @see czm_inverseViewProjection */ czm_viewProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.viewProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 view-projection transformation matrix that * transforms clip coordinates to world coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_inverseViewProjection; * * // Example * vec4 worldPosition = czm_inverseViewProjection * clipPosition; * * @see UniformState#inverseViewProjection * @see czm_viewProjection */ czm_inverseViewProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseViewProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view-projection transformation matrix that * transforms model coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_modelViewProjection; * * // Example * vec4 gl_Position = czm_modelViewProjection * modelPosition; * * // The above is equivalent to, but more efficient than: * gl_Position = czm_projection * czm_view * czm_model * modelPosition; * * @see UniformState#modelViewProjection * @see czm_model * @see czm_view * @see czm_projection * @see czm_modelView * @see czm_viewProjection * @see czm_modelViewInfiniteProjection * @see czm_inverseModelViewProjection */ czm_modelViewProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelViewProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 inverse model-view-projection transformation matrix that * transforms clip coordinates to model coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. * * @example * // GLSL declaration * uniform mat4 czm_inverseModelViewProjection; * * // Example * vec4 modelPosition = czm_inverseModelViewProjection * clipPosition; * * @see UniformState#modelViewProjection * @see czm_modelViewProjection */ czm_inverseModelViewProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.inverseModelViewProjection; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view-projection transformation matrix that * transforms model coordinates, relative to the eye, to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. This is used in * conjunction with {@link czm_translateRelativeToEye}. * * @example * // GLSL declaration * uniform mat4 czm_modelViewProjectionRelativeToEye; * * // Example * attribute vec3 positionHigh; * attribute vec3 positionLow; * * void main() * { * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); * gl_Position = czm_modelViewProjectionRelativeToEye * p; * } * * @see czm_modelViewRelativeToEye * @see czm_translateRelativeToEye * @see EncodedCartesian3 */ czm_modelViewProjectionRelativeToEye: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelViewProjectionRelativeToEye; }, }), /** * An automatic GLSL uniform representing a 4x4 model-view-projection transformation matrix that * transforms model coordinates to clip coordinates. Clip coordinates is the * coordinate system for a vertex shader's gl_Position output. The projection matrix places * the far plane at infinity. This is useful in algorithms like shadow volumes and GPU ray casting with * proxy geometry to ensure that triangles are not clipped by the far plane. * * @example * // GLSL declaration * uniform mat4 czm_modelViewInfiniteProjection; * * // Example * vec4 gl_Position = czm_modelViewInfiniteProjection * modelPosition; * * // The above is equivalent to, but more efficient than: * gl_Position = czm_infiniteProjection * czm_view * czm_model * modelPosition; * * @see UniformState#modelViewInfiniteProjection * @see czm_model * @see czm_view * @see czm_infiniteProjection * @see czm_modelViewProjection */ czm_modelViewInfiniteProjection: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelViewInfiniteProjection; }, }), /** * An automatic GLSL uniform that indicates if the current camera is orthographic in 3D. * * @see UniformState#orthographicIn3D */ czm_orthographicIn3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.orthographicIn3D ? 1 : 0; }, }), /** * An automatic GLSL uniform representing a 3x3 normal transformation matrix that * transforms normal vectors in model coordinates to eye coordinates. *

* Positions should be transformed to eye coordinates using {@link czm_modelView} and * normals should be transformed using czm_normal. * * @example * // GLSL declaration * uniform mat3 czm_normal; * * // Example * vec3 eyeNormal = czm_normal * normal; * * @see UniformState#normal * @see czm_inverseNormal * @see czm_modelView */ czm_normal: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.normal; }, }), /** * An automatic GLSL uniform representing a 3x3 normal transformation matrix that * transforms normal vectors in 3D model coordinates to eye coordinates. * In 3D mode, this is identical to * {@link czm_normal}, but in 2D and Columbus View it represents the normal transformation * matrix as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. *

* Positions should be transformed to eye coordinates using {@link czm_modelView3D} and * normals should be transformed using czm_normal3D. * * @example * // GLSL declaration * uniform mat3 czm_normal3D; * * // Example * vec3 eyeNormal = czm_normal3D * normal; * * @see UniformState#normal3D * @see czm_normal */ czm_normal3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.normal3D; }, }), /** * An automatic GLSL uniform representing a 3x3 normal transformation matrix that * transforms normal vectors in eye coordinates to model coordinates. This is * the opposite of the transform provided by {@link czm_normal}. * * @example * // GLSL declaration * uniform mat3 czm_inverseNormal; * * // Example * vec3 normalMC = czm_inverseNormal * normalEC; * * @see UniformState#inverseNormal * @see czm_normal * @see czm_modelView * @see czm_inverseView */ czm_inverseNormal: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.inverseNormal; }, }), /** * An automatic GLSL uniform representing a 3x3 normal transformation matrix that * transforms normal vectors in eye coordinates to 3D model coordinates. This is * the opposite of the transform provided by {@link czm_normal}. * In 3D mode, this is identical to * {@link czm_inverseNormal}, but in 2D and Columbus View it represents the inverse normal transformation * matrix as if the camera were at an equivalent location in 3D mode. This is useful for lighting * 2D and Columbus View in the same way that 3D is lit. * * @example * // GLSL declaration * uniform mat3 czm_inverseNormal3D; * * // Example * vec3 normalMC = czm_inverseNormal3D * normalEC; * * @see UniformState#inverseNormal3D * @see czm_inverseNormal */ czm_inverseNormal3D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.inverseNormal3D; }, }), /** * An automatic GLSL uniform containing the height in meters of the * eye (camera) above or below the ellipsoid. * * @see UniformState#eyeHeight */ czm_eyeHeight: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.eyeHeight; }, }), /** * An automatic GLSL uniform containing height (x) and height squared (y) * in meters of the eye (camera) above the 2D world plane. This uniform is only valid * when the {@link SceneMode} is SCENE2D. * * @see UniformState#eyeHeight2D */ czm_eyeHeight2D: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC2, getValue: function (uniformState) { return uniformState.eyeHeight2D; }, }), /** * An automatic GLSL uniform containing the near distance (x) and the far distance (y) * of the frustum defined by the camera. This is the largest possible frustum, not an individual * frustum used for multi-frustum rendering. * * @example * // GLSL declaration * uniform vec2 czm_entireFrustum; * * // Example * float frustumLength = czm_entireFrustum.y - czm_entireFrustum.x; * * @see UniformState#entireFrustum * @see czm_currentFrustum */ czm_entireFrustum: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC2, getValue: function (uniformState) { return uniformState.entireFrustum; }, }), /** * An automatic GLSL uniform containing the near distance (x) and the far distance (y) * of the frustum defined by the camera. This is the individual * frustum used for multi-frustum rendering. * * @example * // GLSL declaration * uniform vec2 czm_currentFrustum; * * // Example * float frustumLength = czm_currentFrustum.y - czm_currentFrustum.x; * * @see UniformState#currentFrustum * @see czm_entireFrustum */ czm_currentFrustum: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC2, getValue: function (uniformState) { return uniformState.currentFrustum; }, }), /** * The distances to the frustum planes. The top, bottom, left and right distances are * the x, y, z, and w components, respectively. */ czm_frustumPlanes: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC4, getValue: function (uniformState) { return uniformState.frustumPlanes; }, }), /** * Gets the far plane's distance from the near plane, plus 1.0. */ czm_farDepthFromNearPlusOne: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.farDepthFromNearPlusOne; }, }), /** * Gets the log2 of {@link AutomaticUniforms#czm_farDepthFromNearPlusOne}. */ czm_log2FarDepthFromNearPlusOne: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.log2FarDepthFromNearPlusOne; }, }), /** * Gets 1.0 divided by {@link AutomaticUniforms#czm_log2FarDepthFromNearPlusOne}. */ czm_oneOverLog2FarDepthFromNearPlusOne: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.oneOverLog2FarDepthFromNearPlusOne; }, }), /** * An automatic GLSL uniform representing the sun position in world coordinates. * * @example * // GLSL declaration * uniform vec3 czm_sunPositionWC; * * @see UniformState#sunPositionWC * @see czm_sunPositionColumbusView * @see czm_sunDirectionWC */ czm_sunPositionWC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.sunPositionWC; }, }), /** * An automatic GLSL uniform representing the sun position in Columbus view world coordinates. * * @example * // GLSL declaration * uniform vec3 czm_sunPositionColumbusView; * * @see UniformState#sunPositionColumbusView * @see czm_sunPositionWC */ czm_sunPositionColumbusView: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.sunPositionColumbusView; }, }), /** * An automatic GLSL uniform representing the normalized direction to the sun in eye coordinates. * * @example * // GLSL declaration * uniform vec3 czm_sunDirectionEC; * * // Example * float diffuse = max(dot(czm_sunDirectionEC, normalEC), 0.0); * * @see UniformState#sunDirectionEC * @see czm_moonDirectionEC * @see czm_sunDirectionWC */ czm_sunDirectionEC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.sunDirectionEC; }, }), /** * An automatic GLSL uniform representing the normalized direction to the sun in world coordinates. * * @example * // GLSL declaration * uniform vec3 czm_sunDirectionWC; * * // Example * float diffuse = max(dot(czm_sunDirectionWC, normalWC), 0.0); * * @see UniformState#sunDirectionWC * @see czm_sunPositionWC * @see czm_sunDirectionEC */ czm_sunDirectionWC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.sunDirectionWC; }, }), /** * An automatic GLSL uniform representing the normalized direction to the moon in eye coordinates. * * @example * // GLSL declaration * uniform vec3 czm_moonDirectionEC; * * // Example * float diffuse = max(dot(czm_moonDirectionEC, normalEC), 0.0); * * @see UniformState#moonDirectionEC * @see czm_sunDirectionEC */ czm_moonDirectionEC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.moonDirectionEC; }, }), /** * An automatic GLSL uniform representing the normalized direction to the scene's light source in eye coordinates. * This is commonly used for directional lighting computations. * * @example * // GLSL declaration * uniform vec3 czm_lightDirectionEC; * * // Example * float diffuse = max(dot(czm_lightDirectionEC, normalEC), 0.0); * * @see UniformState#lightDirectionEC * @see czm_lightDirectionWC */ czm_lightDirectionEC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.lightDirectionEC; }, }), /** * An automatic GLSL uniform representing the normalized direction to the scene's light source in world coordinates. * This is commonly used for directional lighting computations. * * @example * // GLSL declaration * uniform vec3 czm_lightDirectionWC; * * // Example * float diffuse = max(dot(czm_lightDirectionWC, normalWC), 0.0); * * @see UniformState#lightDirectionWC * @see czm_lightDirectionEC */ czm_lightDirectionWC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.lightDirectionWC; }, }), /** * An automatic GLSL uniform that represents the color of light emitted by the scene's light source. This * is equivalent to the light color multiplied by the light intensity limited to a maximum luminance of 1.0 * suitable for non-HDR lighting. * * @example * // GLSL declaration * uniform vec3 czm_lightColor; * * // Example * vec3 diffuseColor = czm_lightColor * max(dot(czm_lightDirectionWC, normalWC), 0.0); * * @see UniformState#lightColor * @see czm_lightColorHdr */ czm_lightColor: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.lightColor; }, }), /** * An automatic GLSL uniform that represents the high dynamic range color of light emitted by the scene's light * source. This is equivalent to the light color multiplied by the light intensity suitable for HDR lighting. * * @example * // GLSL declaration * uniform vec3 czm_lightColorHdr; * * // Example * vec3 diffuseColor = czm_lightColorHdr * max(dot(czm_lightDirectionWC, normalWC), 0.0); * * @see UniformState#lightColorHdr * @see czm_lightColor */ czm_lightColorHdr: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.lightColorHdr; }, }), /** * An automatic GLSL uniform representing the high bits of the camera position in model * coordinates. This is used for GPU RTE to eliminate jittering artifacts when rendering * as described in {@link http://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}. * * @example * // GLSL declaration * uniform vec3 czm_encodedCameraPositionMCHigh; * * @see czm_encodedCameraPositionMCLow * @see czm_modelViewRelativeToEye * @see czm_modelViewProjectionRelativeToEye */ czm_encodedCameraPositionMCHigh: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.encodedCameraPositionMCHigh; }, }), /** * An automatic GLSL uniform representing the low bits of the camera position in model * coordinates. This is used for GPU RTE to eliminate jittering artifacts when rendering * as described in {@linkhttp://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}. * * @example * // GLSL declaration * uniform vec3 czm_encodedCameraPositionMCLow; * * @see czm_encodedCameraPositionMCHigh * @see czm_modelViewRelativeToEye * @see czm_modelViewProjectionRelativeToEye */ czm_encodedCameraPositionMCLow: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.encodedCameraPositionMCLow; }, }), /** * An automatic GLSL uniform representing the position of the viewer (camera) in world coordinates. * * @example * // GLSL declaration * uniform vec3 czm_viewerPositionWC; */ czm_viewerPositionWC: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return Matrix4.getTranslation( uniformState.inverseView, viewerPositionWCScratch ); }, }), /** * An automatic GLSL uniform representing the frame number. This uniform is automatically incremented * every frame. * * @example * // GLSL declaration * uniform float czm_frameNumber; */ czm_frameNumber: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.frameState.frameNumber; }, }), /** * An automatic GLSL uniform representing the current morph transition time between * 2D/Columbus View and 3D, with 0.0 being 2D or Columbus View and 1.0 being 3D. * * @example * // GLSL declaration * uniform float czm_morphTime; * * // Example * vec4 p = czm_columbusViewMorph(position2D, position3D, czm_morphTime); */ czm_morphTime: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.frameState.morphTime; }, }), /** * An automatic GLSL uniform representing the current {@link SceneMode}, expressed * as a float. * * @example * // GLSL declaration * uniform float czm_sceneMode; * * // Example * if (czm_sceneMode == czm_sceneMode2D) * { * eyeHeightSq = czm_eyeHeight2D.y; * } * * @see czm_sceneMode2D * @see czm_sceneModeColumbusView * @see czm_sceneMode3D * @see czm_sceneModeMorphing */ czm_sceneMode: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.frameState.mode; }, }), /** * An automatic GLSL uniform representing the current rendering pass. * * @example * // GLSL declaration * uniform float czm_pass; * * // Example * if ((czm_pass == czm_passTranslucent) && isOpaque()) * { * gl_Position *= 0.0; // Cull opaque geometry in the translucent pass * } */ czm_pass: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.pass; }, }), /** * An automatic GLSL uniform representing the current scene background color. * * @example * // GLSL declaration * uniform vec4 czm_backgroundColor; * * // Example: If the given color's RGB matches the background color, invert it. * vec4 adjustColorForContrast(vec4 color) * { * if (czm_backgroundColor.rgb == color.rgb) * { * color.rgb = vec3(1.0) - color.rgb; * } * * return color; * } */ czm_backgroundColor: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC4, getValue: function (uniformState) { return uniformState.backgroundColor; }, }), /** * An automatic GLSL uniform containing the BRDF look up texture used for image-based lighting computations. * * @example * // GLSL declaration * uniform sampler2D czm_brdfLut; * * // Example: For a given roughness and NdotV value, find the material's BRDF information in the red and green channels * float roughness = 0.5; * float NdotV = dot(normal, view); * vec2 brdfLut = texture2D(czm_brdfLut, vec2(NdotV, 1.0 - roughness)).rg; */ czm_brdfLut: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.SAMPLER_2D, getValue: function (uniformState) { return uniformState.brdfLut; }, }), /** * An automatic GLSL uniform containing the environment map used within the scene. * * @example * // GLSL declaration * uniform samplerCube czm_environmentMap; * * // Example: Create a perfect reflection of the environment map on a model * float reflected = reflect(view, normal); * vec4 reflectedColor = textureCube(czm_environmentMap, reflected); */ czm_environmentMap: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.SAMPLER_CUBE, getValue: function (uniformState) { return uniformState.environmentMap; }, }), /** * An automatic GLSL uniform containing the specular environment map atlas used within the scene. * * @example * // GLSL declaration * uniform sampler2D czm_specularEnvironmentMaps; */ czm_specularEnvironmentMaps: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.SAMPLER_2D, getValue: function (uniformState) { return uniformState.specularEnvironmentMaps; }, }), /** * An automatic GLSL uniform containing the size of the specular environment map atlas used within the scene. * * @example * // GLSL declaration * uniform vec2 czm_specularEnvironmentMapSize; */ czm_specularEnvironmentMapSize: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC2, getValue: function (uniformState) { return uniformState.specularEnvironmentMapsDimensions; }, }), /** * An automatic GLSL uniform containing the maximum level-of-detail of the specular environment map atlas used within the scene. * * @example * // GLSL declaration * uniform float czm_specularEnvironmentMapsMaximumLOD; */ czm_specularEnvironmentMapsMaximumLOD: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.specularEnvironmentMapsMaximumLOD; }, }), /** * An automatic GLSL uniform containing the spherical harmonic coefficients used within the scene. * * @example * // GLSL declaration * uniform vec3[9] czm_sphericalHarmonicCoefficients; */ czm_sphericalHarmonicCoefficients: new AutomaticUniform({ size: 9, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.sphericalHarmonicCoefficients; }, }), /** * An automatic GLSL uniform representing a 3x3 rotation matrix that transforms * from True Equator Mean Equinox (TEME) axes to the pseudo-fixed axes at the current scene time. * * @example * // GLSL declaration * uniform mat3 czm_temeToPseudoFixed; * * // Example * vec3 pseudoFixed = czm_temeToPseudoFixed * teme; * * @see UniformState#temeToPseudoFixedMatrix * @see Transforms.computeTemeToPseudoFixedMatrix */ czm_temeToPseudoFixed: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_MAT3, getValue: function (uniformState) { return uniformState.temeToPseudoFixedMatrix; }, }), /** * An automatic GLSL uniform representing the ratio of canvas coordinate space to canvas pixel space. * * @example * uniform float czm_pixelRatio; */ czm_pixelRatio: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.pixelRatio; }, }), /** * An automatic GLSL uniform scalar used to mix a color with the fog color based on the distance to the camera. * * @see czm_fog */ czm_fogDensity: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.fogDensity; }, }), /** * An automatic GLSL uniform representing the splitter position to use when rendering imagery layers with a splitter. * This will be in pixel coordinates relative to the canvas. * * @example * // GLSL declaration * uniform float czm_imagerySplitPosition; */ czm_imagerySplitPosition: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.imagerySplitPosition; }, }), /** * An automatic GLSL uniform scalar representing the geometric tolerance per meter */ czm_geometricToleranceOverMeter: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.geometricToleranceOverMeter; }, }), /** * An automatic GLSL uniform representing the distance from the camera at which to disable the depth test of billboards, labels and points * to, for example, prevent clipping against terrain. When set to zero, the depth test should always be applied. When less than zero, * the depth test should never be applied. */ czm_minimumDisableDepthTestDistance: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.minimumDisableDepthTestDistance; }, }), /** * An automatic GLSL uniform that will be the highlight color of unclassified 3D Tiles. */ czm_invertClassificationColor: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC4, getValue: function (uniformState) { return uniformState.invertClassificationColor; }, }), /** * An automatic GLSL uniform that is used for gamma correction. */ czm_gamma: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT, getValue: function (uniformState) { return uniformState.gamma; }, }), /** * An automatic GLSL uniform that stores the ellipsoid radii. */ czm_ellipsoidRadii: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.ellipsoid.radii; }, }), /** * An automatic GLSL uniform that stores the ellipsoid inverse radii. */ czm_ellipsoidInverseRadii: new AutomaticUniform({ size: 1, datatype: WebGLConstants$1.FLOAT_VEC3, getValue: function (uniformState) { return uniformState.ellipsoid.oneOverRadii; }, }), }; /** * @private * @constructor */ function createUniform(gl, activeUniform, uniformName, location) { switch (activeUniform.type) { case gl.FLOAT: return new UniformFloat(gl, activeUniform, uniformName, location); case gl.FLOAT_VEC2: return new UniformFloatVec2(gl, activeUniform, uniformName, location); case gl.FLOAT_VEC3: return new UniformFloatVec3(gl, activeUniform, uniformName, location); case gl.FLOAT_VEC4: return new UniformFloatVec4(gl, activeUniform, uniformName, location); case gl.SAMPLER_2D: case gl.SAMPLER_CUBE: return new UniformSampler(gl, activeUniform, uniformName, location); case gl.INT: case gl.BOOL: return new UniformInt(gl, activeUniform, uniformName, location); case gl.INT_VEC2: case gl.BOOL_VEC2: return new UniformIntVec2(gl, activeUniform, uniformName, location); case gl.INT_VEC3: case gl.BOOL_VEC3: return new UniformIntVec3(gl, activeUniform, uniformName, location); case gl.INT_VEC4: case gl.BOOL_VEC4: return new UniformIntVec4(gl, activeUniform, uniformName, location); case gl.FLOAT_MAT2: return new UniformMat2(gl, activeUniform, uniformName, location); case gl.FLOAT_MAT3: return new UniformMat3(gl, activeUniform, uniformName, location); case gl.FLOAT_MAT4: return new UniformMat4(gl, activeUniform, uniformName, location); default: throw new RuntimeError( "Unrecognized uniform type: " + activeUniform.type + ' for uniform "' + uniformName + '".' ); } } /** * @private * @constructor */ function UniformFloat(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = 0.0; this._gl = gl; this._location = location; } UniformFloat.prototype.set = function () { if (this.value !== this._value) { this._value = this.value; this._gl.uniform1f(this._location, this.value); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformFloatVec2(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Cartesian2(); this._gl = gl; this._location = location; } UniformFloatVec2.prototype.set = function () { var v = this.value; if (!Cartesian2.equals(v, this._value)) { Cartesian2.clone(v, this._value); this._gl.uniform2f(this._location, v.x, v.y); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformFloatVec3(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = undefined; this._gl = gl; this._location = location; } UniformFloatVec3.prototype.set = function () { var v = this.value; if (defined(v.red)) { if (!Color.equals(v, this._value)) { this._value = Color.clone(v, this._value); this._gl.uniform3f(this._location, v.red, v.green, v.blue); } } else if (defined(v.x)) { if (!Cartesian3.equals(v, this._value)) { this._value = Cartesian3.clone(v, this._value); this._gl.uniform3f(this._location, v.x, v.y, v.z); } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError( 'Invalid vec3 value for uniform "' + this.name + '".' ); //>>includeEnd('debug'); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformFloatVec4(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = undefined; this._gl = gl; this._location = location; } UniformFloatVec4.prototype.set = function () { var v = this.value; if (defined(v.red)) { if (!Color.equals(v, this._value)) { this._value = Color.clone(v, this._value); this._gl.uniform4f(this._location, v.red, v.green, v.blue, v.alpha); } } else if (defined(v.x)) { if (!Cartesian4.equals(v, this._value)) { this._value = Cartesian4.clone(v, this._value); this._gl.uniform4f(this._location, v.x, v.y, v.z, v.w); } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError( 'Invalid vec4 value for uniform "' + this.name + '".' ); //>>includeEnd('debug'); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformSampler(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._gl = gl; this._location = location; this.textureUnitIndex = undefined; } UniformSampler.prototype.set = function () { var gl = this._gl; gl.activeTexture(gl.TEXTURE0 + this.textureUnitIndex); var v = this.value; gl.bindTexture(v._target, v._texture); }; UniformSampler.prototype._setSampler = function (textureUnitIndex) { this.textureUnitIndex = textureUnitIndex; this._gl.uniform1i(this._location, textureUnitIndex); return textureUnitIndex + 1; }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformInt(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = 0.0; this._gl = gl; this._location = location; } UniformInt.prototype.set = function () { if (this.value !== this._value) { this._value = this.value; this._gl.uniform1i(this._location, this.value); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformIntVec2(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Cartesian2(); this._gl = gl; this._location = location; } UniformIntVec2.prototype.set = function () { var v = this.value; if (!Cartesian2.equals(v, this._value)) { Cartesian2.clone(v, this._value); this._gl.uniform2i(this._location, v.x, v.y); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformIntVec3(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Cartesian3(); this._gl = gl; this._location = location; } UniformIntVec3.prototype.set = function () { var v = this.value; if (!Cartesian3.equals(v, this._value)) { Cartesian3.clone(v, this._value); this._gl.uniform3i(this._location, v.x, v.y, v.z); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformIntVec4(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Cartesian4(); this._gl = gl; this._location = location; } UniformIntVec4.prototype.set = function () { var v = this.value; if (!Cartesian4.equals(v, this._value)) { Cartesian4.clone(v, this._value); this._gl.uniform4i(this._location, v.x, v.y, v.z, v.w); } }; /////////////////////////////////////////////////////////////////////////// var scratchUniformArray$1 = new Float32Array(4); /** * @private * @constructor */ function UniformMat2(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Matrix2(); this._gl = gl; this._location = location; } UniformMat2.prototype.set = function () { if (!Matrix2.equalsArray(this.value, this._value, 0)) { Matrix2.clone(this.value, this._value); var array = Matrix2.toArray(this.value, scratchUniformArray$1); this._gl.uniformMatrix2fv(this._location, false, array); } }; /////////////////////////////////////////////////////////////////////////// var scratchMat3Array = new Float32Array(9); /** * @private * @constructor */ function UniformMat3(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Matrix3(); this._gl = gl; this._location = location; } UniformMat3.prototype.set = function () { if (!Matrix3.equalsArray(this.value, this._value, 0)) { Matrix3.clone(this.value, this._value); var array = Matrix3.toArray(this.value, scratchMat3Array); this._gl.uniformMatrix3fv(this._location, false, array); } }; /////////////////////////////////////////////////////////////////////////// var scratchMat4Array = new Float32Array(16); /** * @private * @constructor */ function UniformMat4(gl, activeUniform, uniformName, location) { /** * @type {String} * @readonly */ this.name = uniformName; this.value = undefined; this._value = new Matrix4(); this._gl = gl; this._location = location; } UniformMat4.prototype.set = function () { if (!Matrix4.equalsArray(this.value, this._value, 0)) { Matrix4.clone(this.value, this._value); var array = Matrix4.toArray(this.value, scratchMat4Array); this._gl.uniformMatrix4fv(this._location, false, array); } }; /** * @private * @constructor */ function createUniformArray(gl, activeUniform, uniformName, locations) { switch (activeUniform.type) { case gl.FLOAT: return new UniformArrayFloat(gl, activeUniform, uniformName, locations); case gl.FLOAT_VEC2: return new UniformArrayFloatVec2( gl, activeUniform, uniformName, locations ); case gl.FLOAT_VEC3: return new UniformArrayFloatVec3( gl, activeUniform, uniformName, locations ); case gl.FLOAT_VEC4: return new UniformArrayFloatVec4( gl, activeUniform, uniformName, locations ); case gl.SAMPLER_2D: case gl.SAMPLER_CUBE: return new UniformArraySampler(gl, activeUniform, uniformName, locations); case gl.INT: case gl.BOOL: return new UniformArrayInt(gl, activeUniform, uniformName, locations); case gl.INT_VEC2: case gl.BOOL_VEC2: return new UniformArrayIntVec2(gl, activeUniform, uniformName, locations); case gl.INT_VEC3: case gl.BOOL_VEC3: return new UniformArrayIntVec3(gl, activeUniform, uniformName, locations); case gl.INT_VEC4: case gl.BOOL_VEC4: return new UniformArrayIntVec4(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT2: return new UniformArrayMat2(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT3: return new UniformArrayMat3(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT4: return new UniformArrayMat4(gl, activeUniform, uniformName, locations); default: throw new RuntimeError( "Unrecognized uniform type: " + activeUniform.type + ' for uniform "' + uniformName + '".' ); } } /** * @private * @constructor */ function UniformArrayFloat(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length); this._gl = gl; this._location = locations[0]; } UniformArrayFloat.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; for (var i = 0; i < length; ++i) { var v = value[i]; if (v !== arraybuffer[i]) { arraybuffer[i] = v; changed = true; } } if (changed) { this._gl.uniform1fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec2(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 2); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec2.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Cartesian2.equalsArray(v, arraybuffer, j)) { Cartesian2.pack(v, arraybuffer, j); changed = true; } j += 2; } if (changed) { this._gl.uniform2fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec3(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 3); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec3.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (defined(v.red)) { if ( v.red !== arraybuffer[j] || v.green !== arraybuffer[j + 1] || v.blue !== arraybuffer[j + 2] ) { arraybuffer[j] = v.red; arraybuffer[j + 1] = v.green; arraybuffer[j + 2] = v.blue; changed = true; } } else if (defined(v.x)) { if (!Cartesian3.equalsArray(v, arraybuffer, j)) { Cartesian3.pack(v, arraybuffer, j); changed = true; } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Invalid vec3 value."); //>>includeEnd('debug'); } j += 3; } if (changed) { this._gl.uniform3fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec4(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec4.prototype.set = function () { // PERFORMANCE_IDEA: if it is a common case that only a few elements // in a uniform array change, we could use heuristics to determine // when it is better to call uniform4f for each element that changed // vs. call uniform4fv once to set the entire array. This applies // to all uniform array types, not just vec4. We might not care // once we have uniform buffers since that will be the fast path. // PERFORMANCE_IDEA: Micro-optimization (I bet it works though): // As soon as changed is true, break into a separate loop that // does the copy without the equals check. var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (defined(v.red)) { if (!Color.equalsArray(v, arraybuffer, j)) { Color.pack(v, arraybuffer, j); changed = true; } } else if (defined(v.x)) { if (!Cartesian4.equalsArray(v, arraybuffer, j)) { Cartesian4.pack(v, arraybuffer, j); changed = true; } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Invalid vec4 value."); //>>includeEnd('debug'); } j += 4; } if (changed) { this._gl.uniform4fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArraySampler(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length); this._gl = gl; this._locations = locations; this.textureUnitIndex = undefined; } UniformArraySampler.prototype.set = function () { var gl = this._gl; var textureUnitIndex = gl.TEXTURE0 + this.textureUnitIndex; var value = this.value; var length = value.length; for (var i = 0; i < length; ++i) { var v = value[i]; gl.activeTexture(textureUnitIndex + i); gl.bindTexture(v._target, v._texture); } }; UniformArraySampler.prototype._setSampler = function (textureUnitIndex) { this.textureUnitIndex = textureUnitIndex; var locations = this._locations; var length = locations.length; for (var i = 0; i < length; ++i) { var index = textureUnitIndex + i; this._gl.uniform1i(locations[i], index); } return textureUnitIndex + length; }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayInt(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length); this._gl = gl; this._location = locations[0]; } UniformArrayInt.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; for (var i = 0; i < length; ++i) { var v = value[i]; if (v !== arraybuffer[i]) { arraybuffer[i] = v; changed = true; } } if (changed) { this._gl.uniform1iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec2(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 2); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec2.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Cartesian2.equalsArray(v, arraybuffer, j)) { Cartesian2.pack(v, arraybuffer, j); changed = true; } j += 2; } if (changed) { this._gl.uniform2iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec3(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 3); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec3.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Cartesian3.equalsArray(v, arraybuffer, j)) { Cartesian3.pack(v, arraybuffer, j); changed = true; } j += 3; } if (changed) { this._gl.uniform3iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec4(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec4.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Cartesian4.equalsArray(v, arraybuffer, j)) { Cartesian4.pack(v, arraybuffer, j); changed = true; } j += 4; } if (changed) { this._gl.uniform4iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat2(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayMat2.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Matrix2.equalsArray(v, arraybuffer, j)) { Matrix2.pack(v, arraybuffer, j); changed = true; } j += 4; } if (changed) { this._gl.uniformMatrix2fv(this._location, false, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat3(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 9); this._gl = gl; this._location = locations[0]; } UniformArrayMat3.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Matrix3.equalsArray(v, arraybuffer, j)) { Matrix3.pack(v, arraybuffer, j); changed = true; } j += 9; } if (changed) { this._gl.uniformMatrix3fv(this._location, false, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat4(gl, activeUniform, uniformName, locations) { var length = locations.length; /** * @type {String} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 16); this._gl = gl; this._location = locations[0]; } UniformArrayMat4.prototype.set = function () { var value = this.value; var length = value.length; var arraybuffer = this._value; var changed = false; var j = 0; for (var i = 0; i < length; ++i) { var v = value[i]; if (!Matrix4.equalsArray(v, arraybuffer, j)) { Matrix4.pack(v, arraybuffer, j); changed = true; } j += 16; } if (changed) { this._gl.uniformMatrix4fv(this._location, false, arraybuffer); } }; var nextShaderProgramId = 0; /** * @private */ function ShaderProgram(options) { var vertexShaderText = options.vertexShaderText; var fragmentShaderText = options.fragmentShaderText; if (typeof spector !== "undefined") { // The #line statements common in Cesium shaders interfere with the ability of the // SpectorJS to show errors on the correct line. So remove them when SpectorJS // is active. vertexShaderText = vertexShaderText.replace(/^#line/gm, "//#line"); fragmentShaderText = fragmentShaderText.replace(/^#line/gm, "//#line"); } var modifiedFS = handleUniformPrecisionMismatches( vertexShaderText, fragmentShaderText ); this._gl = options.gl; this._logShaderCompilation = options.logShaderCompilation; this._debugShaders = options.debugShaders; this._attributeLocations = options.attributeLocations; this._program = undefined; this._numberOfVertexAttributes = undefined; this._vertexAttributes = undefined; this._uniformsByName = undefined; this._uniforms = undefined; this._automaticUniforms = undefined; this._manualUniforms = undefined; this._duplicateUniformNames = modifiedFS.duplicateUniformNames; this._cachedShader = undefined; // Used by ShaderCache /** * @private */ this.maximumTextureUnitIndex = undefined; this._vertexShaderSource = options.vertexShaderSource; this._vertexShaderText = options.vertexShaderText; this._fragmentShaderSource = options.fragmentShaderSource; this._fragmentShaderText = modifiedFS.fragmentShaderText; /** * @private */ this.id = nextShaderProgramId++; } ShaderProgram.fromCache = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); return options.context.shaderCache.getShaderProgram(options); }; ShaderProgram.replaceCache = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); return options.context.shaderCache.replaceShaderProgram(options); }; Object.defineProperties(ShaderProgram.prototype, { /** * GLSL source for the shader program's vertex shader. * @memberof ShaderProgram.prototype * * @type {ShaderSource} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * GLSL source for the shader program's fragment shader. * @memberof ShaderProgram.prototype * * @type {ShaderSource} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, vertexAttributes: { get: function () { initialize$8(this); return this._vertexAttributes; }, }, numberOfVertexAttributes: { get: function () { initialize$8(this); return this._numberOfVertexAttributes; }, }, allUniforms: { get: function () { initialize$8(this); return this._uniformsByName; }, }, }); function extractUniforms(shaderText) { var uniformNames = []; var uniformLines = shaderText.match(/uniform.*?(?![^{]*})(?=[=\[;])/g); if (defined(uniformLines)) { var len = uniformLines.length; for (var i = 0; i < len; i++) { var line = uniformLines[i].trim(); var name = line.slice(line.lastIndexOf(" ") + 1); uniformNames.push(name); } } return uniformNames; } function handleUniformPrecisionMismatches( vertexShaderText, fragmentShaderText ) { // If a uniform exists in both the vertex and fragment shader but with different precision qualifiers, // give the fragment shader uniform a different name. This fixes shader compilation errors on devices // that only support mediump in the fragment shader. var duplicateUniformNames = {}; if (!ContextLimits.highpFloatSupported || !ContextLimits.highpIntSupported) { var i, j; var uniformName; var duplicateName; var vertexShaderUniforms = extractUniforms(vertexShaderText); var fragmentShaderUniforms = extractUniforms(fragmentShaderText); var vertexUniformsCount = vertexShaderUniforms.length; var fragmentUniformsCount = fragmentShaderUniforms.length; for (i = 0; i < vertexUniformsCount; i++) { for (j = 0; j < fragmentUniformsCount; j++) { if (vertexShaderUniforms[i] === fragmentShaderUniforms[j]) { uniformName = vertexShaderUniforms[i]; duplicateName = "czm_mediump_" + uniformName; // Update fragmentShaderText with renamed uniforms var re = new RegExp(uniformName + "\\b", "g"); fragmentShaderText = fragmentShaderText.replace(re, duplicateName); duplicateUniformNames[duplicateName] = uniformName; } } } } return { fragmentShaderText: fragmentShaderText, duplicateUniformNames: duplicateUniformNames, }; } var consolePrefix = "[Cesium WebGL] "; function createAndLinkProgram(gl, shader) { var vsSource = shader._vertexShaderText; var fsSource = shader._fragmentShaderText; var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vsSource); gl.compileShader(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fsSource); gl.compileShader(fragmentShader); var program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); var attributeLocations = shader._attributeLocations; if (defined(attributeLocations)) { for (var attribute in attributeLocations) { if (attributeLocations.hasOwnProperty(attribute)) { gl.bindAttribLocation( program, attributeLocations[attribute], attribute ); } } } gl.linkProgram(program); var log; if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { var debugShaders = shader._debugShaders; // For performance, only check compile errors if there is a linker error. if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { log = gl.getShaderInfoLog(fragmentShader); console.error(consolePrefix + "Fragment shader compile log: " + log); if (defined(debugShaders)) { var fragmentSourceTranslation = debugShaders.getTranslatedShaderSource( fragmentShader ); if (fragmentSourceTranslation !== "") { console.error( consolePrefix + "Translated fragment shader source:\n" + fragmentSourceTranslation ); } else { console.error(consolePrefix + "Fragment shader translation failed."); } } gl.deleteProgram(program); throw new RuntimeError( "Fragment shader failed to compile. Compile log: " + log ); } if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { log = gl.getShaderInfoLog(vertexShader); console.error(consolePrefix + "Vertex shader compile log: " + log); if (defined(debugShaders)) { var vertexSourceTranslation = debugShaders.getTranslatedShaderSource( vertexShader ); if (vertexSourceTranslation !== "") { console.error( consolePrefix + "Translated vertex shader source:\n" + vertexSourceTranslation ); } else { console.error(consolePrefix + "Vertex shader translation failed."); } } gl.deleteProgram(program); throw new RuntimeError( "Vertex shader failed to compile. Compile log: " + log ); } log = gl.getProgramInfoLog(program); console.error(consolePrefix + "Shader program link log: " + log); if (defined(debugShaders)) { console.error( consolePrefix + "Translated vertex shader source:\n" + debugShaders.getTranslatedShaderSource(vertexShader) ); console.error( consolePrefix + "Translated fragment shader source:\n" + debugShaders.getTranslatedShaderSource(fragmentShader) ); } gl.deleteProgram(program); throw new RuntimeError("Program failed to link. Link log: " + log); } var logShaderCompilation = shader._logShaderCompilation; if (logShaderCompilation) { log = gl.getShaderInfoLog(vertexShader); if (defined(log) && log.length > 0) { console.log(consolePrefix + "Vertex shader compile log: " + log); } } if (logShaderCompilation) { log = gl.getShaderInfoLog(fragmentShader); if (defined(log) && log.length > 0) { console.log(consolePrefix + "Fragment shader compile log: " + log); } } if (logShaderCompilation) { log = gl.getProgramInfoLog(program); if (defined(log) && log.length > 0) { console.log(consolePrefix + "Shader program link log: " + log); } } return program; } function findVertexAttributes(gl, program, numberOfAttributes) { var attributes = {}; for (var i = 0; i < numberOfAttributes; ++i) { var attr = gl.getActiveAttrib(program, i); var location = gl.getAttribLocation(program, attr.name); attributes[attr.name] = { name: attr.name, type: attr.type, index: location, }; } return attributes; } function findUniforms(gl, program) { var uniformsByName = {}; var uniforms = []; var samplerUniforms = []; var numberOfUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (var i = 0; i < numberOfUniforms; ++i) { var activeUniform = gl.getActiveUniform(program, i); var suffix = "[0]"; var uniformName = activeUniform.name.indexOf( suffix, activeUniform.name.length - suffix.length ) !== -1 ? activeUniform.name.slice(0, activeUniform.name.length - 3) : activeUniform.name; // Ignore GLSL built-in uniforms returned in Firefox. if (uniformName.indexOf("gl_") !== 0) { if (activeUniform.name.indexOf("[") < 0) { // Single uniform var location = gl.getUniformLocation(program, uniformName); // IE 11.0.9 needs this check since getUniformLocation can return null // if the uniform is not active (e.g., it is optimized out). Looks like // getActiveUniform() above returns uniforms that are not actually active. if (location !== null) { var uniform = createUniform(gl, activeUniform, uniformName, location); uniformsByName[uniformName] = uniform; uniforms.push(uniform); if (uniform._setSampler) { samplerUniforms.push(uniform); } } } else { // Uniform array var uniformArray; var locations; var value; var loc; // On some platforms - Nexus 4 in Firefox for one - an array of sampler2D ends up being represented // as separate uniforms, one for each array element. Check for and handle that case. var indexOfBracket = uniformName.indexOf("["); if (indexOfBracket >= 0) { // We're assuming the array elements show up in numerical order - it seems to be true. uniformArray = uniformsByName[uniformName.slice(0, indexOfBracket)]; // Nexus 4 with Android 4.3 needs this check, because it reports a uniform // with the strange name webgl_3467e0265d05c3c1[1] in our globe surface shader. if (!defined(uniformArray)) { continue; } locations = uniformArray._locations; // On the Nexus 4 in Chrome, we get one uniform per sampler, just like in Firefox, // but the size is not 1 like it is in Firefox. So if we push locations here, // we'll end up adding too many locations. if (locations.length <= 1) { value = uniformArray.value; loc = gl.getUniformLocation(program, uniformName); // Workaround for IE 11.0.9. See above. if (loc !== null) { locations.push(loc); value.push(gl.getUniform(program, loc)); } } } else { locations = []; for (var j = 0; j < activeUniform.size; ++j) { loc = gl.getUniformLocation(program, uniformName + "[" + j + "]"); // Workaround for IE 11.0.9. See above. if (loc !== null) { locations.push(loc); } } uniformArray = createUniformArray( gl, activeUniform, uniformName, locations ); uniformsByName[uniformName] = uniformArray; uniforms.push(uniformArray); if (uniformArray._setSampler) { samplerUniforms.push(uniformArray); } } } } } return { uniformsByName: uniformsByName, uniforms: uniforms, samplerUniforms: samplerUniforms, }; } function partitionUniforms(shader, uniforms) { var automaticUniforms = []; var manualUniforms = []; for (var uniform in uniforms) { if (uniforms.hasOwnProperty(uniform)) { var uniformObject = uniforms[uniform]; var uniformName = uniform; // if it's a duplicate uniform, use its original name so it is updated correctly var duplicateUniform = shader._duplicateUniformNames[uniformName]; if (defined(duplicateUniform)) { uniformObject.name = duplicateUniform; uniformName = duplicateUniform; } var automaticUniform = AutomaticUniforms[uniformName]; if (defined(automaticUniform)) { automaticUniforms.push({ uniform: uniformObject, automaticUniform: automaticUniform, }); } else { manualUniforms.push(uniformObject); } } } return { automaticUniforms: automaticUniforms, manualUniforms: manualUniforms, }; } function setSamplerUniforms(gl, program, samplerUniforms) { gl.useProgram(program); var textureUnitIndex = 0; var length = samplerUniforms.length; for (var i = 0; i < length; ++i) { textureUnitIndex = samplerUniforms[i]._setSampler(textureUnitIndex); } gl.useProgram(null); return textureUnitIndex; } function initialize$8(shader) { if (defined(shader._program)) { return; } reinitialize(shader); } function reinitialize(shader) { var oldProgram = shader._program; var gl = shader._gl; var program = createAndLinkProgram(gl, shader, shader._debugShaders); var numberOfVertexAttributes = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); var uniforms = findUniforms(gl, program); var partitionedUniforms = partitionUniforms(shader, uniforms.uniformsByName); shader._program = program; shader._numberOfVertexAttributes = numberOfVertexAttributes; shader._vertexAttributes = findVertexAttributes( gl, program, numberOfVertexAttributes ); shader._uniformsByName = uniforms.uniformsByName; shader._uniforms = uniforms.uniforms; shader._automaticUniforms = partitionedUniforms.automaticUniforms; shader._manualUniforms = partitionedUniforms.manualUniforms; shader.maximumTextureUnitIndex = setSamplerUniforms( gl, program, uniforms.samplerUniforms ); if (oldProgram) { shader._gl.deleteProgram(oldProgram); } // If SpectorJS is active, add the hook to make the shader editor work. // https://github.com/BabylonJS/Spector.js/blob/master/documentation/extension.md#shader-editor if (typeof spector !== "undefined") { shader._program.__SPECTOR_rebuildProgram = function ( vertexSourceCode, // The new vertex shader source fragmentSourceCode, // The new fragment shader source onCompiled, // Callback triggered by your engine when the compilation is successful. It needs to send back the new linked program. onError // Callback triggered by your engine in case of error. It needs to send the WebGL error to allow the editor to display the error in the gutter. ) { var originalVS = shader._vertexShaderText; var originalFS = shader._fragmentShaderText; // SpectorJS likes to replace `!=` with `! =` for unknown reasons, // and that causes glsl compile failures. So fix that up. var regex = / ! = /g; shader._vertexShaderText = vertexSourceCode.replace(regex, " != "); shader._fragmentShaderText = fragmentSourceCode.replace(regex, " != "); try { reinitialize(shader); onCompiled(shader._program); } catch (e) { shader._vertexShaderText = originalVS; shader._fragmentShaderText = originalFS; // Only pass on the WebGL error: var errorMatcher = /(?:Compile|Link) error: ([^]*)/; var match = errorMatcher.exec(e.message); if (match) { onError(match[1]); } else { onError(e.message); } } }; } } ShaderProgram.prototype._bind = function () { initialize$8(this); this._gl.useProgram(this._program); }; ShaderProgram.prototype._setUniforms = function ( uniformMap, uniformState, validate ) { var len; var i; if (defined(uniformMap)) { var manualUniforms = this._manualUniforms; len = manualUniforms.length; for (i = 0; i < len; ++i) { var mu = manualUniforms[i]; mu.value = uniformMap[mu.name](); } } var automaticUniforms = this._automaticUniforms; len = automaticUniforms.length; for (i = 0; i < len; ++i) { var au = automaticUniforms[i]; au.uniform.value = au.automaticUniform.getValue(uniformState); } /////////////////////////////////////////////////////////////////// // It appears that assigning the uniform values above and then setting them here // (which makes the GL calls) is faster than removing this loop and making // the GL calls above. I suspect this is because each GL call pollutes the // L2 cache making our JavaScript and the browser/driver ping-pong cache lines. var uniforms = this._uniforms; len = uniforms.length; for (i = 0; i < len; ++i) { uniforms[i].set(); } if (validate) { var gl = this._gl; var program = this._program; gl.validateProgram(program); //>>includeStart('debug', pragmas.debug); if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) { throw new DeveloperError( "Program validation failed. Program info log: " + gl.getProgramInfoLog(program) ); } //>>includeEnd('debug'); } }; ShaderProgram.prototype.isDestroyed = function () { return false; }; ShaderProgram.prototype.destroy = function () { this._cachedShader.cache.releaseShaderProgram(this); return undefined; }; ShaderProgram.prototype.finalDestroy = function () { this._gl.deleteProgram(this._program); return destroyObject(this); }; /** * A function to port GLSL shaders from GLSL ES 1.00 to GLSL ES 3.00 * * This function is nowhere near comprehensive or complete. It just * handles some common cases. * * Note that this function requires the presence of the * "#define OUTPUT_DECLARATION" line that is appended * by ShaderSource. * * @private */ function modernizeShader(source, isFragmentShader) { var outputDeclarationRegex = /#define OUTPUT_DECLARATION/; var splitSource = source.split("\n"); if (/#version 300 es/g.test(source)) { return source; } var outputDeclarationLine = -1; var i, line; for (i = 0; i < splitSource.length; ++i) { line = splitSource[i]; if (outputDeclarationRegex.test(line)) { outputDeclarationLine = i; break; } } if (outputDeclarationLine === -1) { throw new DeveloperError("Could not find a #define OUTPUT_DECLARATION!"); } var outputVariables = []; for (i = 0; i < 10; i++) { var fragDataString = "gl_FragData\\[" + i + "\\]"; var newOutput = "czm_out" + i; var regex = new RegExp(fragDataString, "g"); if (regex.test(source)) { setAdd(newOutput, outputVariables); replaceInSourceString(fragDataString, newOutput, splitSource); splitSource.splice( outputDeclarationLine, 0, "layout(location = " + i + ") out vec4 " + newOutput + ";" ); outputDeclarationLine += 1; } } var czmFragColor = "czm_fragColor"; if (findInSource("gl_FragColor", splitSource)) { setAdd(czmFragColor, outputVariables); replaceInSourceString("gl_FragColor", czmFragColor, splitSource); splitSource.splice( outputDeclarationLine, 0, "layout(location = 0) out vec4 czm_fragColor;" ); outputDeclarationLine += 1; } var variableMap = getVariablePreprocessorBranch(outputVariables, splitSource); var lineAdds = {}; for (i = 0; i < splitSource.length; i++) { line = splitSource[i]; for (var variable in variableMap) { if (variableMap.hasOwnProperty(variable)) { var matchVar = new RegExp( "(layout)[^]+(out)[^]+(" + variable + ")[^]+", "g" ); if (matchVar.test(line)) { lineAdds[line] = variable; } } } } for (var layoutDeclaration in lineAdds) { if (lineAdds.hasOwnProperty(layoutDeclaration)) { var variableName = lineAdds[layoutDeclaration]; var lineNumber = splitSource.indexOf(layoutDeclaration); var entry = variableMap[variableName]; var depth = entry.length; var d; for (d = 0; d < depth; d++) { splitSource.splice(lineNumber, 0, entry[d]); } lineNumber += depth + 1; for (d = depth - 1; d >= 0; d--) { splitSource.splice(lineNumber, 0, "#endif //" + entry[d]); } } } var webgl2UniqueID = "WEBGL_2"; var webgl2DefineMacro = "#define " + webgl2UniqueID; var versionThree = "#version 300 es"; var foundVersion = false; for (i = 0; i < splitSource.length; i++) { if (/#version/.test(splitSource[i])) { splitSource[i] = versionThree; foundVersion = true; break; } } if (!foundVersion) { splitSource.splice(0, 0, versionThree); } splitSource.splice(1, 0, webgl2DefineMacro); removeExtension("EXT_draw_buffers", webgl2UniqueID, splitSource); removeExtension("EXT_frag_depth", webgl2UniqueID, splitSource); removeExtension("OES_standard_derivatives", webgl2UniqueID, splitSource); replaceInSourceString("texture2D", "texture", splitSource); replaceInSourceString("texture3D", "texture", splitSource); replaceInSourceString("textureCube", "texture", splitSource); replaceInSourceString("gl_FragDepthEXT", "gl_FragDepth", splitSource); if (isFragmentShader) { replaceInSourceString("varying", "in", splitSource); } else { replaceInSourceString("attribute", "in", splitSource); replaceInSourceString("varying", "out", splitSource); } return compileSource(splitSource); } // Note that this fails if your string looks like // searchString[singleCharacter]searchString function replaceInSourceString(str, replacement, splitSource) { var regexStr = "(^|[^\\w])(" + str + ")($|[^\\w])"; var regex = new RegExp(regexStr, "g"); var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; splitSource[i] = line.replace(regex, "$1" + replacement + "$3"); } } function replaceInSourceRegex(regex, replacement, splitSource) { var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; splitSource[i] = line.replace(regex, replacement); } } function findInSource(str, splitSource) { var regexStr = "(^|[^\\w])(" + str + ")($|[^\\w])"; var regex = new RegExp(regexStr, "g"); var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { var line = splitSource[i]; if (regex.test(line)) { return true; } } return false; } function compileSource(splitSource) { var wholeSource = ""; var splitSourceLength = splitSource.length; for (var i = 0; i < splitSourceLength; ++i) { wholeSource += splitSource[i] + "\n"; } return wholeSource; } function setAdd(variable, set) { if (set.indexOf(variable) === -1) { set.push(variable); } } function getVariablePreprocessorBranch(layoutVariables, splitSource) { var variableMap = {}; var numLayoutVariables = layoutVariables.length; var stack = []; for (var i = 0; i < splitSource.length; ++i) { var line = splitSource[i]; var hasIF = /(#ifdef|#if)/g.test(line); var hasELSE = /#else/g.test(line); var hasENDIF = /#endif/g.test(line); if (hasIF) { stack.push(line); } else if (hasELSE) { var top = stack[stack.length - 1]; var op = top.replace("ifdef", "ifndef"); if (/if/g.test(op)) { op = op.replace(/(#if\s+)(\S*)([^]*)/, "$1!($2)$3"); } stack.pop(); stack.push(op); } else if (hasENDIF) { stack.pop(); } else if (!/layout/g.test(line)) { for (var varIndex = 0; varIndex < numLayoutVariables; ++varIndex) { var varName = layoutVariables[varIndex]; if (line.indexOf(varName) !== -1) { if (!defined(variableMap[varName])) { variableMap[varName] = stack.slice(); } else { variableMap[varName] = variableMap[varName].filter(function (x) { return stack.indexOf(x) >= 0; }); } } } } } return variableMap; } function removeExtension(name, webgl2UniqueID, splitSource) { var regex = "#extension\\s+GL_" + name + "\\s+:\\s+[a-zA-Z0-9]+\\s*$"; replaceInSourceRegex(new RegExp(regex, "g"), "", splitSource); // replace any possible directive #ifdef (GL_EXT_extension) with WEBGL_2 unique directive replaceInSourceString("GL_" + name, webgl2UniqueID, splitSource); } //This file is automatically rebuilt by the Cesium build process. var czm_degreesPerRadian = "/**\n\ * A built-in GLSL floating-point constant for converting radians to degrees.\n\ *\n\ * @alias czm_degreesPerRadian\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.DEGREES_PER_RADIAN\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_degreesPerRadian = ...;\n\ *\n\ * // Example\n\ * float deg = czm_degreesPerRadian * rad;\n\ */\n\ const float czm_degreesPerRadian = 57.29577951308232;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_depthRange = "/**\n\ * A built-in GLSL vec2 constant for defining the depth range.\n\ * This is a workaround to a bug where IE11 does not implement gl_DepthRange.\n\ *\n\ * @alias czm_depthRange\n\ * @glslConstant\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * float depthRangeNear = czm_depthRange.near;\n\ * float depthRangeFar = czm_depthRange.far;\n\ *\n\ */\n\ const czm_depthRangeStruct czm_depthRange = czm_depthRangeStruct(0.0, 1.0);\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon1 = "/**\n\ * 0.1\n\ *\n\ * @name czm_epsilon1\n\ * @glslConstant\n\ */\n\ const float czm_epsilon1 = 0.1;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon2 = "/**\n\ * 0.01\n\ *\n\ * @name czm_epsilon2\n\ * @glslConstant\n\ */\n\ const float czm_epsilon2 = 0.01;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon3 = "/**\n\ * 0.001\n\ *\n\ * @name czm_epsilon3\n\ * @glslConstant\n\ */\n\ const float czm_epsilon3 = 0.001;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon4 = "/**\n\ * 0.0001\n\ *\n\ * @name czm_epsilon4\n\ * @glslConstant\n\ */\n\ const float czm_epsilon4 = 0.0001;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon5 = "/**\n\ * 0.00001\n\ *\n\ * @name czm_epsilon5\n\ * @glslConstant\n\ */\n\ const float czm_epsilon5 = 0.00001;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon6 = "/**\n\ * 0.000001\n\ *\n\ * @name czm_epsilon6\n\ * @glslConstant\n\ */\n\ const float czm_epsilon6 = 0.000001;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_epsilon7 = "/**\n\ * 0.0000001\n\ *\n\ * @name czm_epsilon7\n\ * @glslConstant\n\ */\n\ const float czm_epsilon7 = 0.0000001;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_infinity = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_infinity\n\ * @glslConstant\n\ */\n\ const float czm_infinity = 5906376272000.0; // Distance from the Sun to Pluto in meters. TODO: What is best given lowp, mediump, and highp?\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_oneOverPi = "/**\n\ * A built-in GLSL floating-point constant for 1/pi.\n\ *\n\ * @alias czm_oneOverPi\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.ONE_OVER_PI\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_oneOverPi = ...;\n\ *\n\ * // Example\n\ * float pi = 1.0 / czm_oneOverPi;\n\ */\n\ const float czm_oneOverPi = 0.3183098861837907;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_oneOverTwoPi = "/**\n\ * A built-in GLSL floating-point constant for 1/2pi.\n\ *\n\ * @alias czm_oneOverTwoPi\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.ONE_OVER_TWO_PI\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_oneOverTwoPi = ...;\n\ *\n\ * // Example\n\ * float pi = 2.0 * czm_oneOverTwoPi;\n\ */\n\ const float czm_oneOverTwoPi = 0.15915494309189535;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passCesium3DTile = "/**\n\ * The automatic GLSL constant for {@link Pass#CESIUM_3D_TILE}\n\ *\n\ * @name czm_passCesium3DTile\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passCesium3DTile = 4.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passCesium3DTileClassification = "/**\n\ * The automatic GLSL constant for {@link Pass#CESIUM_3D_TILE_CLASSIFICATION}\n\ *\n\ * @name czm_passCesium3DTileClassification\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passCesium3DTileClassification = 5.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passCesium3DTileClassificationIgnoreShow = "/**\n\ * The automatic GLSL constant for {@link Pass#CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW}\n\ *\n\ * @name czm_passCesium3DTileClassificationIgnoreShow\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passCesium3DTileClassificationIgnoreShow = 6.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passClassification = "/**\n\ * The automatic GLSL constant for {@link Pass#CLASSIFICATION}\n\ *\n\ * @name czm_passClassification\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passClassification = 7.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passCompute = "/**\n\ * The automatic GLSL constant for {@link Pass#COMPUTE}\n\ *\n\ * @name czm_passCompute\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passCompute = 1.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passEnvironment = "/**\n\ * The automatic GLSL constant for {@link Pass#ENVIRONMENT}\n\ *\n\ * @name czm_passEnvironment\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passEnvironment = 0.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passGlobe = "/**\n\ * The automatic GLSL constant for {@link Pass#GLOBE}\n\ *\n\ * @name czm_passGlobe\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passGlobe = 2.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passOpaque = "/**\n\ * The automatic GLSL constant for {@link Pass#OPAQUE}\n\ *\n\ * @name czm_passOpaque\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passOpaque = 7.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passOverlay = "/**\n\ * The automatic GLSL constant for {@link Pass#OVERLAY}\n\ *\n\ * @name czm_passOverlay\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passOverlay = 9.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passTerrainClassification = "/**\n\ * The automatic GLSL constant for {@link Pass#TERRAIN_CLASSIFICATION}\n\ *\n\ * @name czm_passTerrainClassification\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passTerrainClassification = 3.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_passTranslucent = "/**\n\ * The automatic GLSL constant for {@link Pass#TRANSLUCENT}\n\ *\n\ * @name czm_passTranslucent\n\ * @glslConstant\n\ *\n\ * @see czm_pass\n\ */\n\ const float czm_passTranslucent = 8.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_pi = "/**\n\ * A built-in GLSL floating-point constant for Math.PI.\n\ *\n\ * @alias czm_pi\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.PI\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_pi = ...;\n\ *\n\ * // Example\n\ * float twoPi = 2.0 * czm_pi;\n\ */\n\ const float czm_pi = 3.141592653589793;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_piOverFour = "/**\n\ * A built-in GLSL floating-point constant for pi/4.\n\ *\n\ * @alias czm_piOverFour\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.PI_OVER_FOUR\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_piOverFour = ...;\n\ *\n\ * // Example\n\ * float pi = 4.0 * czm_piOverFour;\n\ */\n\ const float czm_piOverFour = 0.7853981633974483;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_piOverSix = "/**\n\ * A built-in GLSL floating-point constant for pi/6.\n\ *\n\ * @alias czm_piOverSix\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.PI_OVER_SIX\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_piOverSix = ...;\n\ *\n\ * // Example\n\ * float pi = 6.0 * czm_piOverSix;\n\ */\n\ const float czm_piOverSix = 0.5235987755982988;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_piOverThree = "/**\n\ * A built-in GLSL floating-point constant for pi/3.\n\ *\n\ * @alias czm_piOverThree\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.PI_OVER_THREE\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_piOverThree = ...;\n\ *\n\ * // Example\n\ * float pi = 3.0 * czm_piOverThree;\n\ */\n\ const float czm_piOverThree = 1.0471975511965976;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_piOverTwo = "/**\n\ * A built-in GLSL floating-point constant for pi/2.\n\ *\n\ * @alias czm_piOverTwo\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.PI_OVER_TWO\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_piOverTwo = ...;\n\ *\n\ * // Example\n\ * float pi = 2.0 * czm_piOverTwo;\n\ */\n\ const float czm_piOverTwo = 1.5707963267948966;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_radiansPerDegree = "/**\n\ * A built-in GLSL floating-point constant for converting degrees to radians.\n\ *\n\ * @alias czm_radiansPerDegree\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.RADIANS_PER_DEGREE\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_radiansPerDegree = ...;\n\ *\n\ * // Example\n\ * float rad = czm_radiansPerDegree * deg;\n\ */\n\ const float czm_radiansPerDegree = 0.017453292519943295;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sceneMode2D = "/**\n\ * The constant identifier for the 2D {@link SceneMode}\n\ *\n\ * @name czm_sceneMode2D\n\ * @glslConstant\n\ * @see czm_sceneMode\n\ * @see czm_sceneModeColumbusView\n\ * @see czm_sceneMode3D\n\ * @see czm_sceneModeMorphing\n\ */\n\ const float czm_sceneMode2D = 2.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sceneMode3D = "/**\n\ * The constant identifier for the 3D {@link SceneMode}\n\ *\n\ * @name czm_sceneMode3D\n\ * @glslConstant\n\ * @see czm_sceneMode\n\ * @see czm_sceneMode2D\n\ * @see czm_sceneModeColumbusView\n\ * @see czm_sceneModeMorphing\n\ */\n\ const float czm_sceneMode3D = 3.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sceneModeColumbusView = "/**\n\ * The constant identifier for the Columbus View {@link SceneMode}\n\ *\n\ * @name czm_sceneModeColumbusView\n\ * @glslConstant\n\ * @see czm_sceneMode\n\ * @see czm_sceneMode2D\n\ * @see czm_sceneMode3D\n\ * @see czm_sceneModeMorphing\n\ */\n\ const float czm_sceneModeColumbusView = 1.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sceneModeMorphing = "/**\n\ * The constant identifier for the Morphing {@link SceneMode}\n\ *\n\ * @name czm_sceneModeMorphing\n\ * @glslConstant\n\ * @see czm_sceneMode\n\ * @see czm_sceneMode2D\n\ * @see czm_sceneModeColumbusView\n\ * @see czm_sceneMode3D\n\ */\n\ const float czm_sceneModeMorphing = 0.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_solarRadius = "/**\n\ * A built-in GLSL floating-point constant for one solar radius.\n\ *\n\ * @alias czm_solarRadius\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.SOLAR_RADIUS\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_solarRadius = ...;\n\ */\n\ const float czm_solarRadius = 695500000.0;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_threePiOver2 = "/**\n\ * A built-in GLSL floating-point constant for 3pi/2.\n\ *\n\ * @alias czm_threePiOver2\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.THREE_PI_OVER_TWO\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_threePiOver2 = ...;\n\ *\n\ * // Example\n\ * float pi = (2.0 / 3.0) * czm_threePiOver2;\n\ */\n\ const float czm_threePiOver2 = 4.71238898038469;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_twoPi = "/**\n\ * A built-in GLSL floating-point constant for 2pi.\n\ *\n\ * @alias czm_twoPi\n\ * @glslConstant\n\ *\n\ * @see CesiumMath.TWO_PI\n\ *\n\ * @example\n\ * // GLSL declaration\n\ * const float czm_twoPi = ...;\n\ *\n\ * // Example\n\ * float pi = czm_twoPi / 2.0;\n\ */\n\ const float czm_twoPi = 6.283185307179586;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_webMercatorMaxLatitude = "/**\n\ * The maximum latitude, in radians, both North and South, supported by a Web Mercator\n\ * (EPSG:3857) projection. Technically, the Mercator projection is defined\n\ * for any latitude up to (but not including) 90 degrees, but it makes sense\n\ * to cut it off sooner because it grows exponentially with increasing latitude.\n\ * The logic behind this particular cutoff value, which is the one used by\n\ * Google Maps, Bing Maps, and Esri, is that it makes the projection\n\ * square. That is, the rectangle is equal in the X and Y directions.\n\ *\n\ * The constant value is computed as follows:\n\ * czm_pi * 0.5 - (2.0 * atan(exp(-czm_pi)))\n\ *\n\ * @name czm_webMercatorMaxLatitude\n\ * @glslConstant\n\ */\n\ const float czm_webMercatorMaxLatitude = 1.4844222297453324;\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_depthRangeStruct = "/**\n\ * @name czm_depthRangeStruct\n\ * @glslStruct\n\ */\n\ struct czm_depthRangeStruct\n\ {\n\ float near;\n\ float far;\n\ };\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_material = "/**\n\ * Holds material information that can be used for lighting. Returned by all czm_getMaterial functions.\n\ *\n\ * @name czm_material\n\ * @glslStruct\n\ *\n\ * @property {vec3} diffuse Incoming light that scatters evenly in all directions.\n\ * @property {float} specular Intensity of incoming light reflecting in a single direction.\n\ * @property {float} shininess The sharpness of the specular reflection. Higher values create a smaller, more focused specular highlight.\n\ * @property {vec3} normal Surface's normal in eye coordinates. It is used for effects such as normal mapping. The default is the surface's unmodified normal.\n\ * @property {vec3} emission Light emitted by the material equally in all directions. The default is vec3(0.0), which emits no light.\n\ * @property {float} alpha Opacity of this material. 0.0 is completely transparent; 1.0 is completely opaque.\n\ */\n\ struct czm_material\n\ {\n\ vec3 diffuse;\n\ float specular;\n\ float shininess;\n\ vec3 normal;\n\ vec3 emission;\n\ float alpha;\n\ };\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_materialInput = "/**\n\ * Used as input to every material's czm_getMaterial function.\n\ *\n\ * @name czm_materialInput\n\ * @glslStruct\n\ *\n\ * @property {float} s 1D texture coordinates.\n\ * @property {vec2} st 2D texture coordinates.\n\ * @property {vec3} str 3D texture coordinates.\n\ * @property {vec3} normalEC Unperturbed surface normal in eye coordinates.\n\ * @property {mat3} tangentToEyeMatrix Matrix for converting a tangent space normal to eye space.\n\ * @property {vec3} positionToEyeEC Vector from the fragment to the eye in eye coordinates. The magnitude is the distance in meters from the fragment to the eye.\n\ * @property {float} height The height of the terrain in meters above or below the WGS84 ellipsoid. Only available for globe materials.\n\ * @property {float} slope The slope of the terrain in radians. 0 is flat; pi/2 is vertical. Only available for globe materials.\n\ * @property {float} aspect The aspect of the terrain in radians. 0 is East, pi/2 is North, pi is West, 3pi/2 is South. Only available for globe materials.\n\ */\n\ struct czm_materialInput\n\ {\n\ float s;\n\ vec2 st;\n\ vec3 str;\n\ vec3 normalEC;\n\ mat3 tangentToEyeMatrix;\n\ vec3 positionToEyeEC;\n\ float height;\n\ float slope;\n\ float aspect;\n\ };\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_ray = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_ray\n\ * @glslStruct\n\ */\n\ struct czm_ray\n\ {\n\ vec3 origin;\n\ vec3 direction;\n\ };\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_raySegment = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_raySegment\n\ * @glslStruct\n\ */\n\ struct czm_raySegment\n\ {\n\ float start;\n\ float stop;\n\ };\n\ \n\ /**\n\ * DOC_TBA\n\ *\n\ * @name czm_emptyRaySegment\n\ * @glslConstant \n\ */\n\ const czm_raySegment czm_emptyRaySegment = czm_raySegment(-czm_infinity, -czm_infinity);\n\ \n\ /**\n\ * DOC_TBA\n\ *\n\ * @name czm_fullRaySegment\n\ * @glslConstant \n\ */\n\ const czm_raySegment czm_fullRaySegment = czm_raySegment(0.0, czm_infinity);\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_shadowParameters = "struct czm_shadowParameters\n\ {\n\ #ifdef USE_CUBE_MAP_SHADOW\n\ vec3 texCoords;\n\ #else\n\ vec2 texCoords;\n\ #endif\n\ \n\ float depthBias;\n\ float depth;\n\ float nDotL;\n\ vec2 texelStepSize;\n\ float normalShadingSmooth;\n\ float darkness;\n\ };\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_HSBToRGB = "/**\n\ * Converts an HSB color (hue, saturation, brightness) to RGB\n\ * HSB <-> RGB conversion with minimal branching: {@link http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl}\n\ *\n\ * @name czm_HSBToRGB\n\ * @glslFunction\n\ * \n\ * @param {vec3} hsb The color in HSB.\n\ *\n\ * @returns {vec3} The color in RGB.\n\ *\n\ * @example\n\ * vec3 hsb = czm_RGBToHSB(rgb);\n\ * hsb.z *= 0.1;\n\ * rgb = czm_HSBToRGB(hsb);\n\ */\n\ \n\ const vec4 K_HSB2RGB = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n\ \n\ vec3 czm_HSBToRGB(vec3 hsb)\n\ {\n\ vec3 p = abs(fract(hsb.xxx + K_HSB2RGB.xyz) * 6.0 - K_HSB2RGB.www);\n\ return hsb.z * mix(K_HSB2RGB.xxx, clamp(p - K_HSB2RGB.xxx, 0.0, 1.0), hsb.y);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_HSLToRGB = "/**\n\ * Converts an HSL color (hue, saturation, lightness) to RGB\n\ * HSL <-> RGB conversion: {@link http://www.chilliant.com/rgb2hsv.html}\n\ *\n\ * @name czm_HSLToRGB\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color in HSL.\n\ *\n\ * @returns {vec3} The color in RGB.\n\ *\n\ * @example\n\ * vec3 hsl = czm_RGBToHSL(rgb);\n\ * hsl.z *= 0.1;\n\ * rgb = czm_HSLToRGB(hsl);\n\ */\n\ \n\ vec3 hueToRGB(float hue)\n\ {\n\ float r = abs(hue * 6.0 - 3.0) - 1.0;\n\ float g = 2.0 - abs(hue * 6.0 - 2.0);\n\ float b = 2.0 - abs(hue * 6.0 - 4.0);\n\ return clamp(vec3(r, g, b), 0.0, 1.0);\n\ }\n\ \n\ vec3 czm_HSLToRGB(vec3 hsl)\n\ {\n\ vec3 rgb = hueToRGB(hsl.x);\n\ float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y;\n\ return (rgb - 0.5) * c + hsl.z;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_RGBToHSB = "/**\n\ * Converts an RGB color to HSB (hue, saturation, brightness)\n\ * HSB <-> RGB conversion with minimal branching: {@link http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl}\n\ *\n\ * @name czm_RGBToHSB\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color in RGB.\n\ *\n\ * @returns {vec3} The color in HSB.\n\ *\n\ * @example\n\ * vec3 hsb = czm_RGBToHSB(rgb);\n\ * hsb.z *= 0.1;\n\ * rgb = czm_HSBToRGB(hsb);\n\ */\n\ \n\ const vec4 K_RGB2HSB = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n\ \n\ vec3 czm_RGBToHSB(vec3 rgb)\n\ {\n\ vec4 p = mix(vec4(rgb.bg, K_RGB2HSB.wz), vec4(rgb.gb, K_RGB2HSB.xy), step(rgb.b, rgb.g));\n\ vec4 q = mix(vec4(p.xyw, rgb.r), vec4(rgb.r, p.yzx), step(p.x, rgb.r));\n\ \n\ float d = q.x - min(q.w, q.y);\n\ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + czm_epsilon7)), d / (q.x + czm_epsilon7), q.x);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_RGBToHSL = "/**\n\ * Converts an RGB color to HSL (hue, saturation, lightness)\n\ * HSL <-> RGB conversion: {@link http://www.chilliant.com/rgb2hsv.html}\n\ *\n\ * @name czm_RGBToHSL\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color in RGB.\n\ *\n\ * @returns {vec3} The color in HSL.\n\ *\n\ * @example\n\ * vec3 hsl = czm_RGBToHSL(rgb);\n\ * hsl.z *= 0.1;\n\ * rgb = czm_HSLToRGB(hsl);\n\ */\n\ \n\ vec3 RGBtoHCV(vec3 rgb)\n\ {\n\ // Based on work by Sam Hocevar and Emil Persson\n\ vec4 p = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0 / 3.0) : vec4(rgb.gb, 0.0, -1.0 / 3.0);\n\ vec4 q = (rgb.r < p.x) ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx);\n\ float c = q.x - min(q.w, q.y);\n\ float h = abs((q.w - q.y) / (6.0 * c + czm_epsilon7) + q.z);\n\ return vec3(h, c, q.x);\n\ }\n\ \n\ vec3 czm_RGBToHSL(vec3 rgb)\n\ {\n\ vec3 hcv = RGBtoHCV(rgb);\n\ float l = hcv.z - hcv.y * 0.5;\n\ float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + czm_epsilon7);\n\ return vec3(hcv.x, s, l);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_RGBToXYZ = "/**\n\ * Converts an RGB color to CIE Yxy.\n\ *

The conversion is described in\n\ * {@link http://content.gpwiki.org/index.php/D3DBook:High-Dynamic_Range_Rendering#Luminance_Transform|Luminance Transform}\n\ *

\n\ * \n\ * @name czm_RGBToXYZ\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color in RGB.\n\ *\n\ * @returns {vec3} The color in CIE Yxy.\n\ *\n\ * @example\n\ * vec3 xyz = czm_RGBToXYZ(rgb);\n\ * xyz.x = max(xyz.x - luminanceThreshold, 0.0);\n\ * rgb = czm_XYZToRGB(xyz);\n\ */\n\ vec3 czm_RGBToXYZ(vec3 rgb)\n\ {\n\ const mat3 RGB2XYZ = mat3(0.4124, 0.2126, 0.0193,\n\ 0.3576, 0.7152, 0.1192,\n\ 0.1805, 0.0722, 0.9505);\n\ vec3 xyz = RGB2XYZ * rgb;\n\ vec3 Yxy;\n\ Yxy.r = xyz.g;\n\ float temp = dot(vec3(1.0), xyz);\n\ Yxy.gb = xyz.rg / temp;\n\ return Yxy;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_XYZToRGB = "/**\n\ * Converts a CIE Yxy color to RGB.\n\ *

The conversion is described in\n\ * {@link http://content.gpwiki.org/index.php/D3DBook:High-Dynamic_Range_Rendering#Luminance_Transform|Luminance Transform}\n\ *

\n\ * \n\ * @name czm_XYZToRGB\n\ * @glslFunction\n\ * \n\ * @param {vec3} Yxy The color in CIE Yxy.\n\ *\n\ * @returns {vec3} The color in RGB.\n\ *\n\ * @example\n\ * vec3 xyz = czm_RGBToXYZ(rgb);\n\ * xyz.x = max(xyz.x - luminanceThreshold, 0.0);\n\ * rgb = czm_XYZToRGB(xyz);\n\ */\n\ vec3 czm_XYZToRGB(vec3 Yxy)\n\ {\n\ const mat3 XYZ2RGB = mat3( 3.2405, -0.9693, 0.0556,\n\ -1.5371, 1.8760, -0.2040,\n\ -0.4985, 0.0416, 1.0572);\n\ vec3 xyz;\n\ xyz.r = Yxy.r * Yxy.g / Yxy.b;\n\ xyz.g = Yxy.r;\n\ xyz.b = Yxy.r * (1.0 - Yxy.g - Yxy.b) / Yxy.b;\n\ \n\ return XYZ2RGB * xyz;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_acesTonemapping = "// See:\n\ // https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/\n\ \n\ vec3 czm_acesTonemapping(vec3 color) {\n\ float g = 0.985;\n\ float a = 0.065;\n\ float b = 0.0001;\n\ float c = 0.433;\n\ float d = 0.238;\n\ \n\ color = (color * (color + a) - b) / (color * (g * color + c) + d);\n\ \n\ color = clamp(color, 0.0, 1.0);\n\ \n\ return color;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_alphaWeight = "/**\n\ * @private\n\ */\n\ float czm_alphaWeight(float a)\n\ {\n\ float z = (gl_FragCoord.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];\n\ \n\ // See Weighted Blended Order-Independent Transparency for examples of different weighting functions:\n\ // http://jcgt.org/published/0002/02/09/\n\ return pow(a + 0.01, 4.0) + max(1e-2, min(3.0 * 1e3, 0.003 / (1e-5 + pow(abs(z) / 200.0, 4.0))));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_antialias = "/**\n\ * Procedural anti-aliasing by blurring two colors that meet at a sharp edge.\n\ *\n\ * @name czm_antialias\n\ * @glslFunction\n\ *\n\ * @param {vec4} color1 The color on one side of the edge.\n\ * @param {vec4} color2 The color on the other side of the edge.\n\ * @param {vec4} currentcolor The current color, either color1 or color2.\n\ * @param {float} dist The distance to the edge in texture coordinates.\n\ * @param {float} [fuzzFactor=0.1] Controls the blurriness between the two colors.\n\ * @returns {vec4} The anti-aliased color.\n\ *\n\ * @example\n\ * // GLSL declarations\n\ * vec4 czm_antialias(vec4 color1, vec4 color2, vec4 currentColor, float dist, float fuzzFactor);\n\ * vec4 czm_antialias(vec4 color1, vec4 color2, vec4 currentColor, float dist);\n\ *\n\ * // get the color for a material that has a sharp edge at the line y = 0.5 in texture space\n\ * float dist = abs(textureCoordinates.t - 0.5);\n\ * vec4 currentColor = mix(bottomColor, topColor, step(0.5, textureCoordinates.t));\n\ * vec4 color = czm_antialias(bottomColor, topColor, currentColor, dist, 0.1);\n\ */\n\ vec4 czm_antialias(vec4 color1, vec4 color2, vec4 currentColor, float dist, float fuzzFactor)\n\ {\n\ float val1 = clamp(dist / fuzzFactor, 0.0, 1.0);\n\ float val2 = clamp((dist - 0.5) / fuzzFactor, 0.0, 1.0);\n\ val1 = val1 * (1.0 - val2);\n\ val1 = val1 * val1 * (3.0 - (2.0 * val1));\n\ val1 = pow(val1, 0.5); //makes the transition nicer\n\ \n\ vec4 midColor = (color1 + color2) * 0.5;\n\ return mix(midColor, currentColor, val1);\n\ }\n\ \n\ vec4 czm_antialias(vec4 color1, vec4 color2, vec4 currentColor, float dist)\n\ {\n\ return czm_antialias(color1, color2, currentColor, dist, 0.1);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_approximateSphericalCoordinates = "/**\n\ * Approximately computes spherical coordinates given a normal.\n\ * Uses approximate inverse trigonometry for speed and consistency,\n\ * since inverse trigonometry can differ from vendor-to-vendor and when compared with the CPU.\n\ *\n\ * @name czm_approximateSphericalCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec3} normal arbitrary-length normal.\n\ *\n\ * @returns {vec2} Approximate latitude and longitude spherical coordinates.\n\ */\n\ vec2 czm_approximateSphericalCoordinates(vec3 normal) {\n\ // Project into plane with vertical for latitude\n\ float latitudeApproximation = czm_fastApproximateAtan(sqrt(normal.x * normal.x + normal.y * normal.y), normal.z);\n\ float longitudeApproximation = czm_fastApproximateAtan(normal.x, normal.y);\n\ return vec2(latitudeApproximation, longitudeApproximation);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_backFacing = "/**\n\ * Determines if the fragment is back facing\n\ *\n\ * @name czm_backFacing\n\ * @glslFunction \n\ * \n\ * @returns {bool} true if the fragment is back facing; otherwise, false.\n\ */\n\ bool czm_backFacing()\n\ {\n\ // !gl_FrontFacing doesn't work as expected on Mac/Intel so use the more verbose form instead. See https://github.com/CesiumGS/cesium/pull/8494.\n\ return gl_FrontFacing == false;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_branchFreeTernary = "/**\n\ * Branchless ternary operator to be used when it's inexpensive to explicitly\n\ * evaluate both possibilities for a float expression.\n\ *\n\ * @name czm_branchFreeTernary\n\ * @glslFunction\n\ *\n\ * @param {bool} comparison A comparison statement\n\ * @param {float} a Value to return if the comparison is true.\n\ * @param {float} b Value to return if the comparison is false.\n\ *\n\ * @returns {float} equivalent of comparison ? a : b\n\ */\n\ float czm_branchFreeTernary(bool comparison, float a, float b) {\n\ float useA = float(comparison);\n\ return a * useA + b * (1.0 - useA);\n\ }\n\ \n\ /**\n\ * Branchless ternary operator to be used when it's inexpensive to explicitly\n\ * evaluate both possibilities for a vec2 expression.\n\ *\n\ * @name czm_branchFreeTernary\n\ * @glslFunction\n\ *\n\ * @param {bool} comparison A comparison statement\n\ * @param {vec2} a Value to return if the comparison is true.\n\ * @param {vec2} b Value to return if the comparison is false.\n\ *\n\ * @returns {vec2} equivalent of comparison ? a : b\n\ */\n\ vec2 czm_branchFreeTernary(bool comparison, vec2 a, vec2 b) {\n\ float useA = float(comparison);\n\ return a * useA + b * (1.0 - useA);\n\ }\n\ \n\ /**\n\ * Branchless ternary operator to be used when it's inexpensive to explicitly\n\ * evaluate both possibilities for a vec3 expression.\n\ *\n\ * @name czm_branchFreeTernary\n\ * @glslFunction\n\ *\n\ * @param {bool} comparison A comparison statement\n\ * @param {vec3} a Value to return if the comparison is true.\n\ * @param {vec3} b Value to return if the comparison is false.\n\ *\n\ * @returns {vec3} equivalent of comparison ? a : b\n\ */\n\ vec3 czm_branchFreeTernary(bool comparison, vec3 a, vec3 b) {\n\ float useA = float(comparison);\n\ return a * useA + b * (1.0 - useA);\n\ }\n\ \n\ /**\n\ * Branchless ternary operator to be used when it's inexpensive to explicitly\n\ * evaluate both possibilities for a vec4 expression.\n\ *\n\ * @name czm_branchFreeTernary\n\ * @glslFunction\n\ *\n\ * @param {bool} comparison A comparison statement\n\ * @param {vec3} a Value to return if the comparison is true.\n\ * @param {vec3} b Value to return if the comparison is false.\n\ *\n\ * @returns {vec3} equivalent of comparison ? a : b\n\ */\n\ vec4 czm_branchFreeTernary(bool comparison, vec4 a, vec4 b) {\n\ float useA = float(comparison);\n\ return a * useA + b * (1.0 - useA);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_cascadeColor = "\n\ vec4 czm_cascadeColor(vec4 weights)\n\ {\n\ return vec4(1.0, 0.0, 0.0, 1.0) * weights.x +\n\ vec4(0.0, 1.0, 0.0, 1.0) * weights.y +\n\ vec4(0.0, 0.0, 1.0, 1.0) * weights.z +\n\ vec4(1.0, 0.0, 1.0, 1.0) * weights.w;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_cascadeDistance = "\n\ uniform vec4 shadowMap_cascadeDistances;\n\ \n\ float czm_cascadeDistance(vec4 weights)\n\ {\n\ return dot(shadowMap_cascadeDistances, weights);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_cascadeMatrix = "\n\ uniform mat4 shadowMap_cascadeMatrices[4];\n\ \n\ mat4 czm_cascadeMatrix(vec4 weights)\n\ {\n\ return shadowMap_cascadeMatrices[0] * weights.x +\n\ shadowMap_cascadeMatrices[1] * weights.y +\n\ shadowMap_cascadeMatrices[2] * weights.z +\n\ shadowMap_cascadeMatrices[3] * weights.w;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_cascadeWeights = "\n\ uniform vec4 shadowMap_cascadeSplits[2];\n\ \n\ vec4 czm_cascadeWeights(float depthEye)\n\ {\n\ // One component is set to 1.0 and all others set to 0.0.\n\ vec4 near = step(shadowMap_cascadeSplits[0], vec4(depthEye));\n\ vec4 far = step(depthEye, shadowMap_cascadeSplits[1]);\n\ return near * far;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_columbusViewMorph = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_columbusViewMorph\n\ * @glslFunction\n\ */\n\ vec4 czm_columbusViewMorph(vec4 position2D, vec4 position3D, float time)\n\ {\n\ // Just linear for now.\n\ vec3 p = mix(position2D.xyz, position3D.xyz, time);\n\ return vec4(p, 1.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_computePosition = "/**\n\ * Returns a position in model coordinates relative to eye taking into\n\ * account the current scene mode: 3D, 2D, or Columbus view.\n\ *

\n\ * This uses standard position attributes, position3DHigh, \n\ * position3DLow, position2DHigh, and position2DLow, \n\ * and should be used when writing a vertex shader for an {@link Appearance}.\n\ *

\n\ *\n\ * @name czm_computePosition\n\ * @glslFunction\n\ *\n\ * @returns {vec4} The position relative to eye.\n\ *\n\ * @example\n\ * vec4 p = czm_computePosition();\n\ * v_positionEC = (czm_modelViewRelativeToEye * p).xyz;\n\ * gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ *\n\ * @see czm_translateRelativeToEye\n\ */\n\ vec4 czm_computePosition();\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_cosineAndSine = "/**\n\ * @private\n\ */\n\ vec2 cordic(float angle)\n\ {\n\ // Scale the vector by the appropriate factor for the 24 iterations to follow.\n\ vec2 vector = vec2(6.0725293500888267e-1, 0.0);\n\ // Iteration 1\n\ float sense = (angle < 0.0) ? -1.0 : 1.0;\n\ // float factor = sense * 1.0; // 2^-0\n\ mat2 rotation = mat2(1.0, sense, -sense, 1.0);\n\ vector = rotation * vector;\n\ angle -= sense * 7.8539816339744828e-1; // atan(2^-0)\n\ // Iteration 2\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ float factor = sense * 5.0e-1; // 2^-1\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 4.6364760900080609e-1; // atan(2^-1)\n\ // Iteration 3\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 2.5e-1; // 2^-2\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 2.4497866312686414e-1; // atan(2^-2)\n\ // Iteration 4\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.25e-1; // 2^-3\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.2435499454676144e-1; // atan(2^-3)\n\ // Iteration 5\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 6.25e-2; // 2^-4\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 6.2418809995957350e-2; // atan(2^-4)\n\ // Iteration 6\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 3.125e-2; // 2^-5\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 3.1239833430268277e-2; // atan(2^-5)\n\ // Iteration 7\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.5625e-2; // 2^-6\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.5623728620476831e-2; // atan(2^-6)\n\ // Iteration 8\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 7.8125e-3; // 2^-7\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 7.8123410601011111e-3; // atan(2^-7)\n\ // Iteration 9\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 3.90625e-3; // 2^-8\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 3.9062301319669718e-3; // atan(2^-8)\n\ // Iteration 10\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.953125e-3; // 2^-9\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.9531225164788188e-3; // atan(2^-9)\n\ // Iteration 11\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 9.765625e-4; // 2^-10\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 9.7656218955931946e-4; // atan(2^-10)\n\ // Iteration 12\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 4.8828125e-4; // 2^-11\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 4.8828121119489829e-4; // atan(2^-11)\n\ // Iteration 13\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 2.44140625e-4; // 2^-12\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 2.4414062014936177e-4; // atan(2^-12)\n\ // Iteration 14\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.220703125e-4; // 2^-13\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.2207031189367021e-4; // atan(2^-13)\n\ // Iteration 15\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 6.103515625e-5; // 2^-14\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 6.1035156174208773e-5; // atan(2^-14)\n\ // Iteration 16\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 3.0517578125e-5; // 2^-15\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 3.0517578115526096e-5; // atan(2^-15)\n\ // Iteration 17\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.52587890625e-5; // 2^-16\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.5258789061315762e-5; // atan(2^-16)\n\ // Iteration 18\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 7.62939453125e-6; // 2^-17\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 7.6293945311019700e-6; // atan(2^-17)\n\ // Iteration 19\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 3.814697265625e-6; // 2^-18\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 3.8146972656064961e-6; // atan(2^-18)\n\ // Iteration 20\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.9073486328125e-6; // 2^-19\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 1.9073486328101870e-6; // atan(2^-19)\n\ // Iteration 21\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 9.5367431640625e-7; // 2^-20\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 9.5367431640596084e-7; // atan(2^-20)\n\ // Iteration 22\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 4.76837158203125e-7; // 2^-21\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 4.7683715820308884e-7; // atan(2^-21)\n\ // Iteration 23\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 2.384185791015625e-7; // 2^-22\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ angle -= sense * 2.3841857910155797e-7; // atan(2^-22)\n\ // Iteration 24\n\ sense = (angle < 0.0) ? -1.0 : 1.0;\n\ factor = sense * 1.1920928955078125e-7; // 2^-23\n\ rotation[0][1] = factor;\n\ rotation[1][0] = -factor;\n\ vector = rotation * vector;\n\ // angle -= sense * 1.1920928955078068e-7; // atan(2^-23)\n\ \n\ return vector;\n\ }\n\ \n\ /**\n\ * Computes the cosine and sine of the provided angle using the CORDIC algorithm.\n\ *\n\ * @name czm_cosineAndSine\n\ * @glslFunction\n\ *\n\ * @param {float} angle The angle in radians.\n\ *\n\ * @returns {vec2} The resulting cosine of the angle (as the x coordinate) and sine of the angle (as the y coordinate).\n\ *\n\ * @example\n\ * vec2 v = czm_cosineAndSine(czm_piOverSix);\n\ * float cosine = v.x;\n\ * float sine = v.y;\n\ */\n\ vec2 czm_cosineAndSine(float angle)\n\ {\n\ if (angle < -czm_piOverTwo || angle > czm_piOverTwo)\n\ {\n\ if (angle < 0.0)\n\ {\n\ return -cordic(angle + czm_pi);\n\ }\n\ else\n\ {\n\ return -cordic(angle - czm_pi);\n\ }\n\ }\n\ else\n\ {\n\ return cordic(angle);\n\ }\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_decompressTextureCoordinates = "/**\n\ * Decompresses texture coordinates that were packed into a single float.\n\ *\n\ * @name czm_decompressTextureCoordinates\n\ * @glslFunction\n\ *\n\ * @param {float} encoded The compressed texture coordinates.\n\ * @returns {vec2} The decompressed texture coordinates.\n\ */\n\ vec2 czm_decompressTextureCoordinates(float encoded)\n\ {\n\ float temp = encoded / 4096.0;\n\ float xZeroTo4095 = floor(temp);\n\ float stx = xZeroTo4095 / 4095.0;\n\ float sty = (encoded - xZeroTo4095 * 4096.0) / 4095.0;\n\ return vec2(stx, sty);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_depthClamp = "// emulated noperspective\n\ #if defined(GL_EXT_frag_depth) && !defined(LOG_DEPTH)\n\ varying float v_WindowZ;\n\ #endif\n\ \n\ /**\n\ * Emulates GL_DEPTH_CLAMP, which is not available in WebGL 1 or 2.\n\ * GL_DEPTH_CLAMP clamps geometry that is outside the near and far planes, \n\ * capping the shadow volume. More information here: \n\ * https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_depth_clamp.txt.\n\ *\n\ * When GL_EXT_frag_depth is available we emulate GL_DEPTH_CLAMP by ensuring \n\ * no geometry gets clipped by setting the clip space z value to 0.0 and then\n\ * sending the unaltered screen space z value (using emulated noperspective\n\ * interpolation) to the frag shader where it is clamped to [0,1] and then\n\ * written with gl_FragDepth (see czm_writeDepthClamp). This technique is based on:\n\ * https://stackoverflow.com/questions/5960757/how-to-emulate-gl-depth-clamp-nv.\n\ *\n\ * When GL_EXT_frag_depth is not available, which is the case on some mobile \n\ * devices, we must attempt to fix this only in the vertex shader. \n\ * The approach is to clamp the z value to the far plane, which closes the \n\ * shadow volume but also distorts the geometry, so there can still be artifacts\n\ * on frustum seams.\n\ *\n\ * @name czm_depthClamp\n\ * @glslFunction\n\ *\n\ * @param {vec4} coords The vertex in clip coordinates.\n\ * @returns {vec4} The modified vertex.\n\ *\n\ * @example\n\ * gl_Position = czm_depthClamp(czm_modelViewProjection * vec4(position, 1.0));\n\ *\n\ * @see czm_writeDepthClamp\n\ */\n\ vec4 czm_depthClamp(vec4 coords)\n\ {\n\ #ifndef LOG_DEPTH\n\ #ifdef GL_EXT_frag_depth\n\ v_WindowZ = (0.5 * (coords.z / coords.w) + 0.5) * coords.w;\n\ coords.z = 0.0;\n\ #else\n\ coords.z = min(coords.z, coords.w);\n\ #endif\n\ #endif\n\ return coords;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_eastNorthUpToEyeCoordinates = "/**\n\ * Computes a 3x3 rotation matrix that transforms vectors from an ellipsoid's east-north-up coordinate system \n\ * to eye coordinates. In east-north-up coordinates, x points east, y points north, and z points along the \n\ * surface normal. East-north-up can be used as an ellipsoid's tangent space for operations such as bump mapping.\n\ *

\n\ * The ellipsoid is assumed to be centered at the model coordinate's origin.\n\ *\n\ * @name czm_eastNorthUpToEyeCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec3} positionMC The position on the ellipsoid in model coordinates.\n\ * @param {vec3} normalEC The normalized ellipsoid surface normal, at positionMC, in eye coordinates.\n\ *\n\ * @returns {mat3} A 3x3 rotation matrix that transforms vectors from the east-north-up coordinate system to eye coordinates.\n\ *\n\ * @example\n\ * // Transform a vector defined in the east-north-up coordinate \n\ * // system, (0, 0, 1) which is the surface normal, to eye \n\ * // coordinates.\n\ * mat3 m = czm_eastNorthUpToEyeCoordinates(positionMC, normalEC);\n\ * vec3 normalEC = m * vec3(0.0, 0.0, 1.0);\n\ */\n\ mat3 czm_eastNorthUpToEyeCoordinates(vec3 positionMC, vec3 normalEC)\n\ {\n\ vec3 tangentMC = normalize(vec3(-positionMC.y, positionMC.x, 0.0)); // normalized surface tangent in model coordinates\n\ vec3 tangentEC = normalize(czm_normal3D * tangentMC); // normalized surface tangent in eye coordiantes\n\ vec3 bitangentEC = normalize(cross(normalEC, tangentEC)); // normalized surface bitangent in eye coordinates\n\ \n\ return mat3(\n\ tangentEC.x, tangentEC.y, tangentEC.z,\n\ bitangentEC.x, bitangentEC.y, bitangentEC.z,\n\ normalEC.x, normalEC.y, normalEC.z);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_ellipsoidContainsPoint = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_ellipsoidContainsPoint\n\ * @glslFunction\n\ *\n\ */\n\ bool czm_ellipsoidContainsPoint(vec3 ellipsoid_inverseRadii, vec3 point)\n\ {\n\ vec3 scaled = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(point, 1.0)).xyz;\n\ return (dot(scaled, scaled) <= 1.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_ellipsoidWgs84TextureCoordinates = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_ellipsoidWgs84TextureCoordinates\n\ * @glslFunction\n\ */\n\ vec2 czm_ellipsoidWgs84TextureCoordinates(vec3 normal)\n\ {\n\ return vec2(atan(normal.y, normal.x) * czm_oneOverTwoPi + 0.5, asin(normal.z) * czm_oneOverPi + 0.5);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_equalsEpsilon = "/**\n\ * Compares left and right componentwise. Returns true\n\ * if they are within epsilon and false otherwise. The inputs\n\ * left and right can be floats, vec2s,\n\ * vec3s, or vec4s.\n\ *\n\ * @name czm_equalsEpsilon\n\ * @glslFunction\n\ *\n\ * @param {} left The first vector.\n\ * @param {} right The second vector.\n\ * @param {float} epsilon The epsilon to use for equality testing.\n\ * @returns {bool} true if the components are within epsilon and false otherwise.\n\ *\n\ * @example\n\ * // GLSL declarations\n\ * bool czm_equalsEpsilon(float left, float right, float epsilon);\n\ * bool czm_equalsEpsilon(vec2 left, vec2 right, float epsilon);\n\ * bool czm_equalsEpsilon(vec3 left, vec3 right, float epsilon);\n\ * bool czm_equalsEpsilon(vec4 left, vec4 right, float epsilon);\n\ */\n\ bool czm_equalsEpsilon(vec4 left, vec4 right, float epsilon) {\n\ return all(lessThanEqual(abs(left - right), vec4(epsilon)));\n\ }\n\ \n\ bool czm_equalsEpsilon(vec3 left, vec3 right, float epsilon) {\n\ return all(lessThanEqual(abs(left - right), vec3(epsilon)));\n\ }\n\ \n\ bool czm_equalsEpsilon(vec2 left, vec2 right, float epsilon) {\n\ return all(lessThanEqual(abs(left - right), vec2(epsilon)));\n\ }\n\ \n\ bool czm_equalsEpsilon(float left, float right, float epsilon) {\n\ return (abs(left - right) <= epsilon);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_eyeOffset = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_eyeOffset\n\ * @glslFunction\n\ *\n\ * @param {vec4} positionEC DOC_TBA.\n\ * @param {vec3} eyeOffset DOC_TBA.\n\ *\n\ * @returns {vec4} DOC_TBA.\n\ */\n\ vec4 czm_eyeOffset(vec4 positionEC, vec3 eyeOffset)\n\ {\n\ // This equation is approximate in x and y.\n\ vec4 p = positionEC;\n\ vec4 zEyeOffset = normalize(p) * eyeOffset.z;\n\ p.xy += eyeOffset.xy + zEyeOffset.xy;\n\ p.z += zEyeOffset.z;\n\ return p;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_eyeToWindowCoordinates = "/**\n\ * Transforms a position from eye to window coordinates. The transformation\n\ * from eye to clip coordinates is done using {@link czm_projection}.\n\ * The transform from normalized device coordinates to window coordinates is\n\ * done using {@link czm_viewportTransformation}, which assumes a depth range\n\ * of near = 0 and far = 1.\n\ *

\n\ * This transform is useful when there is a need to manipulate window coordinates\n\ * in a vertex shader as done by {@link BillboardCollection}.\n\ *\n\ * @name czm_eyeToWindowCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec4} position The position in eye coordinates to transform.\n\ *\n\ * @returns {vec4} The transformed position in window coordinates.\n\ *\n\ * @see czm_modelToWindowCoordinates\n\ * @see czm_projection\n\ * @see czm_viewportTransformation\n\ * @see BillboardCollection\n\ *\n\ * @example\n\ * vec4 positionWC = czm_eyeToWindowCoordinates(positionEC);\n\ */\n\ vec4 czm_eyeToWindowCoordinates(vec4 positionEC)\n\ {\n\ vec4 q = czm_projection * positionEC; // clip coordinates\n\ q.xyz /= q.w; // normalized device coordinates\n\ q.xyz = (czm_viewportTransformation * vec4(q.xyz, 1.0)).xyz; // window coordinates\n\ return q;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_fastApproximateAtan = "/**\n\ * Approxiamtes atan over the range [0, 1]. Safe to flip output for negative input.\n\ *\n\ * Based on Michal Drobot's approximation from ShaderFastLibs, which in turn is based on\n\ * \"Efficient approximations for the arctangent function,\" Rajan, S. Sichun Wang Inkol, R. Joyal, A., May 2006.\n\ * Adapted from ShaderFastLibs under MIT License.\n\ *\n\ * Chosen for the following characteristics over range [0, 1]:\n\ * - basically no error at 0 and 1, important for getting around range limit (naive atan2 via atan requires infinite range atan)\n\ * - no visible artifacts from first-derivative discontinuities, unlike latitude via range-reduced sqrt asin approximations (at equator)\n\ *\n\ * The original code is x * (-0.1784 * abs(x) - 0.0663 * x * x + 1.0301);\n\ * Removed the abs() in here because it isn't needed, the input range is guaranteed as [0, 1] by how we're approximating atan2.\n\ *\n\ * @name czm_fastApproximateAtan\n\ * @glslFunction\n\ *\n\ * @param {float} x Value between 0 and 1 inclusive.\n\ *\n\ * @returns {float} Approximation of atan(x)\n\ */\n\ float czm_fastApproximateAtan(float x) {\n\ return x * (-0.1784 * x - 0.0663 * x * x + 1.0301);\n\ }\n\ \n\ /**\n\ * Approximation of atan2.\n\ *\n\ * Range reduction math based on nvidia's cg reference implementation for atan2: http://developer.download.nvidia.com/cg/atan2.html\n\ * However, we replaced their atan curve with Michael Drobot's (see above).\n\ *\n\ * @name czm_fastApproximateAtan\n\ * @glslFunction\n\ *\n\ * @param {float} x Value between -1 and 1 inclusive.\n\ * @param {float} y Value between -1 and 1 inclusive.\n\ *\n\ * @returns {float} Approximation of atan2(x, y)\n\ */\n\ float czm_fastApproximateAtan(float x, float y) {\n\ // atan approximations are usually only reliable over [-1, 1], or, in our case, [0, 1] due to modifications.\n\ // So range-reduce using abs and by flipping whether x or y is on top.\n\ float t = abs(x); // t used as swap and atan result.\n\ float opposite = abs(y);\n\ float adjacent = max(t, opposite);\n\ opposite = min(t, opposite);\n\ \n\ t = czm_fastApproximateAtan(opposite / adjacent);\n\ \n\ // Undo range reduction\n\ t = czm_branchFreeTernary(abs(y) > abs(x), czm_piOverTwo - t, t);\n\ t = czm_branchFreeTernary(x < 0.0, czm_pi - t, t);\n\ t = czm_branchFreeTernary(y < 0.0, -t, t);\n\ return t;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_fog = "/**\n\ * Gets the color with fog at a distance from the camera.\n\ *\n\ * @name czm_fog\n\ * @glslFunction\n\ *\n\ * @param {float} distanceToCamera The distance to the camera in meters.\n\ * @param {vec3} color The original color.\n\ * @param {vec3} fogColor The color of the fog.\n\ *\n\ * @returns {vec3} The color adjusted for fog at the distance from the camera.\n\ */\n\ vec3 czm_fog(float distanceToCamera, vec3 color, vec3 fogColor)\n\ {\n\ float scalar = distanceToCamera * czm_fogDensity;\n\ float fog = 1.0 - exp(-(scalar * scalar));\n\ return mix(color, fogColor, fog);\n\ }\n\ \n\ /**\n\ * Gets the color with fog at a distance from the camera.\n\ *\n\ * @name czm_fog\n\ * @glslFunction\n\ *\n\ * @param {float} distanceToCamera The distance to the camera in meters.\n\ * @param {vec3} color The original color.\n\ * @param {vec3} fogColor The color of the fog.\n\ * @param {float} fogModifierConstant A constant to modify the appearance of fog.\n\ *\n\ * @returns {vec3} The color adjusted for fog at the distance from the camera.\n\ */\n\ vec3 czm_fog(float distanceToCamera, vec3 color, vec3 fogColor, float fogModifierConstant)\n\ {\n\ float scalar = distanceToCamera * czm_fogDensity;\n\ float fog = 1.0 - exp(-((fogModifierConstant * scalar + fogModifierConstant) * (scalar * (1.0 + fogModifierConstant))));\n\ return mix(color, fogColor, fog);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_gammaCorrect = "/**\n\ * Converts a color from RGB space to linear space.\n\ *\n\ * @name czm_gammaCorrect\n\ * @glslFunction\n\ *\n\ * @param {vec3} color The color in RGB space.\n\ * @returns {vec3} The color in linear space.\n\ */\n\ vec3 czm_gammaCorrect(vec3 color) {\n\ #ifdef HDR\n\ color = pow(color, vec3(czm_gamma));\n\ #endif\n\ return color;\n\ }\n\ \n\ vec4 czm_gammaCorrect(vec4 color) {\n\ #ifdef HDR\n\ color.rgb = pow(color.rgb, vec3(czm_gamma));\n\ #endif\n\ return color;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_geodeticSurfaceNormal = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_geodeticSurfaceNormal\n\ * @glslFunction\n\ *\n\ * @param {vec3} positionOnEllipsoid DOC_TBA\n\ * @param {vec3} ellipsoidCenter DOC_TBA\n\ * @param {vec3} oneOverEllipsoidRadiiSquared DOC_TBA\n\ * \n\ * @returns {vec3} DOC_TBA.\n\ */\n\ vec3 czm_geodeticSurfaceNormal(vec3 positionOnEllipsoid, vec3 ellipsoidCenter, vec3 oneOverEllipsoidRadiiSquared)\n\ {\n\ return normalize((positionOnEllipsoid - ellipsoidCenter) * oneOverEllipsoidRadiiSquared);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_getDefaultMaterial = "/**\n\ * An czm_material with default values. Every material's czm_getMaterial\n\ * should use this default material as a base for the material it returns.\n\ * The default normal value is given by materialInput.normalEC.\n\ *\n\ * @name czm_getDefaultMaterial\n\ * @glslFunction\n\ *\n\ * @param {czm_materialInput} input The input used to construct the default material.\n\ *\n\ * @returns {czm_material} The default material.\n\ *\n\ * @see czm_materialInput\n\ * @see czm_material\n\ * @see czm_getMaterial\n\ */\n\ czm_material czm_getDefaultMaterial(czm_materialInput materialInput)\n\ {\n\ czm_material material;\n\ material.diffuse = vec3(0.0);\n\ material.specular = 0.0;\n\ material.shininess = 1.0;\n\ material.normal = materialInput.normalEC;\n\ material.emission = vec3(0.0);\n\ material.alpha = 1.0;\n\ return material;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_getLambertDiffuse = "/**\n\ * Calculates the intensity of diffusely reflected light.\n\ *\n\ * @name czm_getLambertDiffuse\n\ * @glslFunction\n\ *\n\ * @param {vec3} lightDirectionEC Unit vector pointing to the light source in eye coordinates.\n\ * @param {vec3} normalEC The surface normal in eye coordinates.\n\ *\n\ * @returns {float} The intensity of the diffuse reflection.\n\ *\n\ * @see czm_phong\n\ *\n\ * @example\n\ * float diffuseIntensity = czm_getLambertDiffuse(lightDirectionEC, normalEC);\n\ * float specularIntensity = czm_getSpecular(lightDirectionEC, toEyeEC, normalEC, 200);\n\ * vec3 color = (diffuseColor * diffuseIntensity) + (specularColor * specularIntensity);\n\ */\n\ float czm_getLambertDiffuse(vec3 lightDirectionEC, vec3 normalEC)\n\ {\n\ return max(dot(lightDirectionEC, normalEC), 0.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_getSpecular = "/**\n\ * Calculates the specular intensity of reflected light.\n\ *\n\ * @name czm_getSpecular\n\ * @glslFunction\n\ *\n\ * @param {vec3} lightDirectionEC Unit vector pointing to the light source in eye coordinates.\n\ * @param {vec3} toEyeEC Unit vector pointing to the eye position in eye coordinates.\n\ * @param {vec3} normalEC The surface normal in eye coordinates.\n\ * @param {float} shininess The sharpness of the specular reflection. Higher values create a smaller, more focused specular highlight.\n\ *\n\ * @returns {float} The intensity of the specular highlight.\n\ *\n\ * @see czm_phong\n\ *\n\ * @example\n\ * float diffuseIntensity = czm_getLambertDiffuse(lightDirectionEC, normalEC);\n\ * float specularIntensity = czm_getSpecular(lightDirectionEC, toEyeEC, normalEC, 200);\n\ * vec3 color = (diffuseColor * diffuseIntensity) + (specularColor * specularIntensity);\n\ */\n\ float czm_getSpecular(vec3 lightDirectionEC, vec3 toEyeEC, vec3 normalEC, float shininess)\n\ {\n\ vec3 toReflectedLight = reflect(-lightDirectionEC, normalEC);\n\ float specular = max(dot(toReflectedLight, toEyeEC), 0.0);\n\ \n\ // pow has undefined behavior if both parameters <= 0.\n\ // Prevent this by making sure shininess is at least czm_epsilon2.\n\ return pow(specular, max(shininess, czm_epsilon2));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_getWaterNoise = "/**\n\ * @private\n\ */\n\ vec4 czm_getWaterNoise(sampler2D normalMap, vec2 uv, float time, float angleInRadians)\n\ {\n\ float cosAngle = cos(angleInRadians);\n\ float sinAngle = sin(angleInRadians);\n\ \n\ // time dependent sampling directions\n\ vec2 s0 = vec2(1.0/17.0, 0.0);\n\ vec2 s1 = vec2(-1.0/29.0, 0.0);\n\ vec2 s2 = vec2(1.0/101.0, 1.0/59.0);\n\ vec2 s3 = vec2(-1.0/109.0, -1.0/57.0);\n\ \n\ // rotate sampling direction by specified angle\n\ s0 = vec2((cosAngle * s0.x) - (sinAngle * s0.y), (sinAngle * s0.x) + (cosAngle * s0.y));\n\ s1 = vec2((cosAngle * s1.x) - (sinAngle * s1.y), (sinAngle * s1.x) + (cosAngle * s1.y));\n\ s2 = vec2((cosAngle * s2.x) - (sinAngle * s2.y), (sinAngle * s2.x) + (cosAngle * s2.y));\n\ s3 = vec2((cosAngle * s3.x) - (sinAngle * s3.y), (sinAngle * s3.x) + (cosAngle * s3.y));\n\ \n\ vec2 uv0 = (uv/103.0) + (time * s0);\n\ vec2 uv1 = uv/107.0 + (time * s1) + vec2(0.23);\n\ vec2 uv2 = uv/vec2(897.0, 983.0) + (time * s2) + vec2(0.51);\n\ vec2 uv3 = uv/vec2(991.0, 877.0) + (time * s3) + vec2(0.71);\n\ \n\ uv0 = fract(uv0);\n\ uv1 = fract(uv1);\n\ uv2 = fract(uv2);\n\ uv3 = fract(uv3);\n\ vec4 noise = (texture2D(normalMap, uv0)) +\n\ (texture2D(normalMap, uv1)) +\n\ (texture2D(normalMap, uv2)) +\n\ (texture2D(normalMap, uv3));\n\ \n\ // average and scale to between -1 and 1\n\ return ((noise / 4.0) - 0.5) * 2.0;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_hue = "/**\n\ * Adjusts the hue of a color.\n\ * \n\ * @name czm_hue\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color.\n\ * @param {float} adjustment The amount to adjust the hue of the color in radians.\n\ *\n\ * @returns {float} The color with the hue adjusted.\n\ *\n\ * @example\n\ * vec3 adjustHue = czm_hue(color, czm_pi); // The same as czm_hue(color, -czm_pi)\n\ */\n\ vec3 czm_hue(vec3 rgb, float adjustment)\n\ {\n\ const mat3 toYIQ = mat3(0.299, 0.587, 0.114,\n\ 0.595716, -0.274453, -0.321263,\n\ 0.211456, -0.522591, 0.311135);\n\ const mat3 toRGB = mat3(1.0, 0.9563, 0.6210,\n\ 1.0, -0.2721, -0.6474,\n\ 1.0, -1.107, 1.7046);\n\ \n\ vec3 yiq = toYIQ * rgb;\n\ float hue = atan(yiq.z, yiq.y) + adjustment;\n\ float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);\n\ \n\ vec3 color = vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));\n\ return toRGB * color;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_inverseGamma = "/**\n\ * Converts a color in linear space to RGB space.\n\ *\n\ * @name czm_inverseGamma\n\ * @glslFunction\n\ *\n\ * @param {vec3} color The color in linear space.\n\ * @returns {vec3} The color in RGB space.\n\ */\n\ vec3 czm_inverseGamma(vec3 color) {\n\ return pow(color, vec3(1.0 / czm_gamma));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_isEmpty = "/**\n\ * Determines if a time interval is empty.\n\ *\n\ * @name czm_isEmpty\n\ * @glslFunction \n\ * \n\ * @param {czm_raySegment} interval The interval to test.\n\ * \n\ * @returns {bool} true if the time interval is empty; otherwise, false.\n\ *\n\ * @example\n\ * bool b0 = czm_isEmpty(czm_emptyRaySegment); // true\n\ * bool b1 = czm_isEmpty(czm_raySegment(0.0, 1.0)); // false\n\ * bool b2 = czm_isEmpty(czm_raySegment(1.0, 1.0)); // false, contains 1.0.\n\ */\n\ bool czm_isEmpty(czm_raySegment interval)\n\ {\n\ return (interval.stop < 0.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_isFull = "/**\n\ * Determines if a time interval is empty.\n\ *\n\ * @name czm_isFull\n\ * @glslFunction \n\ * \n\ * @param {czm_raySegment} interval The interval to test.\n\ * \n\ * @returns {bool} true if the time interval is empty; otherwise, false.\n\ *\n\ * @example\n\ * bool b0 = czm_isEmpty(czm_emptyRaySegment); // true\n\ * bool b1 = czm_isEmpty(czm_raySegment(0.0, 1.0)); // false\n\ * bool b2 = czm_isEmpty(czm_raySegment(1.0, 1.0)); // false, contains 1.0.\n\ */\n\ bool czm_isFull(czm_raySegment interval)\n\ {\n\ return (interval.start == 0.0 && interval.stop == czm_infinity);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_latitudeToWebMercatorFraction = "/**\n\ * Computes the fraction of a Web Wercator rectangle at which a given geodetic latitude is located.\n\ *\n\ * @name czm_latitudeToWebMercatorFraction\n\ * @glslFunction\n\ *\n\ * @param {float} latitude The geodetic latitude, in radians.\n\ * @param {float} southMercatorY The Web Mercator coordinate of the southern boundary of the rectangle.\n\ * @param {float} oneOverMercatorHeight The total height of the rectangle in Web Mercator coordinates.\n\ *\n\ * @returns {float} The fraction of the rectangle at which the latitude occurs. If the latitude is the southern\n\ * boundary of the rectangle, the return value will be zero. If it is the northern boundary, the return\n\ * value will be 1.0. Latitudes in between are mapped according to the Web Mercator projection.\n\ */ \n\ float czm_latitudeToWebMercatorFraction(float latitude, float southMercatorY, float oneOverMercatorHeight)\n\ {\n\ float sinLatitude = sin(latitude);\n\ float mercatorY = 0.5 * log((1.0 + sinLatitude) / (1.0 - sinLatitude));\n\ \n\ return (mercatorY - southMercatorY) * oneOverMercatorHeight;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_lineDistance = "/**\n\ * Computes distance from an point in 2D to a line in 2D.\n\ *\n\ * @name czm_lineDistance\n\ * @glslFunction\n\ *\n\ * param {vec2} point1 A point along the line.\n\ * param {vec2} point2 A point along the line.\n\ * param {vec2} point A point that may or may not be on the line.\n\ * returns {float} The distance from the point to the line.\n\ */\n\ float czm_lineDistance(vec2 point1, vec2 point2, vec2 point) {\n\ return abs((point2.y - point1.y) * point.x - (point2.x - point1.x) * point.y + point2.x * point1.y - point2.y * point1.x) / distance(point2, point1);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_luminance = "/**\n\ * Computes the luminance of a color. \n\ *\n\ * @name czm_luminance\n\ * @glslFunction\n\ *\n\ * @param {vec3} rgb The color.\n\ * \n\ * @returns {float} The luminance.\n\ *\n\ * @example\n\ * float light = czm_luminance(vec3(0.0)); // 0.0\n\ * float dark = czm_luminance(vec3(1.0)); // ~1.0 \n\ */\n\ float czm_luminance(vec3 rgb)\n\ {\n\ // Algorithm from Chapter 10 of Graphics Shaders.\n\ const vec3 W = vec3(0.2125, 0.7154, 0.0721);\n\ return dot(rgb, W);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_metersPerPixel = "/**\n\ * Computes the size of a pixel in meters at a distance from the eye.\n\ *

\n\ * Use this version when passing in a custom pixel ratio. For example, passing in 1.0 will return meters per native device pixel.\n\ *

\n\ * @name czm_metersPerPixel\n\ * @glslFunction\n\ *\n\ * @param {vec3} positionEC The position to get the meters per pixel in eye coordinates.\n\ * @param {float} pixelRatio The scaling factor from pixel space to coordinate space\n\ *\n\ * @returns {float} The meters per pixel at positionEC.\n\ */\n\ float czm_metersPerPixel(vec4 positionEC, float pixelRatio)\n\ {\n\ float width = czm_viewport.z;\n\ float height = czm_viewport.w;\n\ float pixelWidth;\n\ float pixelHeight;\n\ \n\ float top = czm_frustumPlanes.x;\n\ float bottom = czm_frustumPlanes.y;\n\ float left = czm_frustumPlanes.z;\n\ float right = czm_frustumPlanes.w;\n\ \n\ if (czm_sceneMode == czm_sceneMode2D || czm_orthographicIn3D == 1.0)\n\ {\n\ float frustumWidth = right - left;\n\ float frustumHeight = top - bottom;\n\ pixelWidth = frustumWidth / width;\n\ pixelHeight = frustumHeight / height;\n\ }\n\ else\n\ {\n\ float distanceToPixel = -positionEC.z;\n\ float inverseNear = 1.0 / czm_currentFrustum.x;\n\ float tanTheta = top * inverseNear;\n\ pixelHeight = 2.0 * distanceToPixel * tanTheta / height;\n\ tanTheta = right * inverseNear;\n\ pixelWidth = 2.0 * distanceToPixel * tanTheta / width;\n\ }\n\ \n\ return max(pixelWidth, pixelHeight) * pixelRatio;\n\ }\n\ \n\ /**\n\ * Computes the size of a pixel in meters at a distance from the eye.\n\ *

\n\ * Use this version when scaling by pixel ratio.\n\ *

\n\ * @name czm_metersPerPixel\n\ * @glslFunction\n\ *\n\ * @param {vec3} positionEC The position to get the meters per pixel in eye coordinates.\n\ *\n\ * @returns {float} The meters per pixel at positionEC.\n\ */\n\ float czm_metersPerPixel(vec4 positionEC)\n\ {\n\ return czm_metersPerPixel(positionEC, czm_pixelRatio);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_modelToWindowCoordinates = "/**\n\ * Transforms a position from model to window coordinates. The transformation\n\ * from model to clip coordinates is done using {@link czm_modelViewProjection}.\n\ * The transform from normalized device coordinates to window coordinates is\n\ * done using {@link czm_viewportTransformation}, which assumes a depth range\n\ * of near = 0 and far = 1.\n\ *

\n\ * This transform is useful when there is a need to manipulate window coordinates\n\ * in a vertex shader as done by {@link BillboardCollection}.\n\ *

\n\ * This function should not be confused with {@link czm_viewportOrthographic},\n\ * which is an orthographic projection matrix that transforms from window \n\ * coordinates to clip coordinates.\n\ *\n\ * @name czm_modelToWindowCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec4} position The position in model coordinates to transform.\n\ *\n\ * @returns {vec4} The transformed position in window coordinates.\n\ *\n\ * @see czm_eyeToWindowCoordinates\n\ * @see czm_modelViewProjection\n\ * @see czm_viewportTransformation\n\ * @see czm_viewportOrthographic\n\ * @see BillboardCollection\n\ *\n\ * @example\n\ * vec4 positionWC = czm_modelToWindowCoordinates(positionMC);\n\ */\n\ vec4 czm_modelToWindowCoordinates(vec4 position)\n\ {\n\ vec4 q = czm_modelViewProjection * position; // clip coordinates\n\ q.xyz /= q.w; // normalized device coordinates\n\ q.xyz = (czm_viewportTransformation * vec4(q.xyz, 1.0)).xyz; // window coordinates\n\ return q;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_multiplyWithColorBalance = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_multiplyWithColorBalance\n\ * @glslFunction\n\ */\n\ vec3 czm_multiplyWithColorBalance(vec3 left, vec3 right)\n\ {\n\ // Algorithm from Chapter 10 of Graphics Shaders.\n\ const vec3 W = vec3(0.2125, 0.7154, 0.0721);\n\ \n\ vec3 target = left * right;\n\ float leftLuminance = dot(left, W);\n\ float rightLuminance = dot(right, W);\n\ float targetLuminance = dot(target, W);\n\ \n\ return ((leftLuminance + rightLuminance) / (2.0 * targetLuminance)) * target;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_nearFarScalar = "/**\n\ * Computes a value that scales with distance. The scaling is clamped at the near and\n\ * far distances, and does not extrapolate. This function works with the\n\ * {@link NearFarScalar} JavaScript class.\n\ *\n\ * @name czm_nearFarScalar\n\ * @glslFunction\n\ *\n\ * @param {vec4} nearFarScalar A vector with 4 components: Near distance (x), Near value (y), Far distance (z), Far value (w).\n\ * @param {float} cameraDistSq The square of the current distance from the camera.\n\ *\n\ * @returns {float} The value at this distance.\n\ */\n\ float czm_nearFarScalar(vec4 nearFarScalar, float cameraDistSq)\n\ {\n\ float valueAtMin = nearFarScalar.y;\n\ float valueAtMax = nearFarScalar.w;\n\ float nearDistanceSq = nearFarScalar.x * nearFarScalar.x;\n\ float farDistanceSq = nearFarScalar.z * nearFarScalar.z;\n\ \n\ float t = (cameraDistSq - nearDistanceSq) / (farDistanceSq - nearDistanceSq);\n\ \n\ t = pow(clamp(t, 0.0, 1.0), 0.2);\n\ \n\ return mix(valueAtMin, valueAtMax, t);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_octDecode = " /**\n\ * Decodes a unit-length vector in 'oct' encoding to a normalized 3-component Cartesian vector.\n\ * The 'oct' encoding is described in \"A Survey of Efficient Representations of Independent Unit Vectors\",\n\ * Cigolle et al 2014: http://jcgt.org/published/0003/02/01/\n\ *\n\ * @name czm_octDecode\n\ * @param {vec2} encoded The oct-encoded, unit-length vector\n\ * @param {float} range The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits.\n\ * @returns {vec3} The decoded and normalized vector\n\ */\n\ vec3 czm_octDecode(vec2 encoded, float range)\n\ {\n\ if (encoded.x == 0.0 && encoded.y == 0.0) {\n\ return vec3(0.0, 0.0, 0.0);\n\ }\n\ \n\ encoded = encoded / range * 2.0 - 1.0;\n\ vec3 v = vec3(encoded.x, encoded.y, 1.0 - abs(encoded.x) - abs(encoded.y));\n\ if (v.z < 0.0)\n\ {\n\ v.xy = (1.0 - abs(v.yx)) * czm_signNotZero(v.xy);\n\ }\n\ \n\ return normalize(v);\n\ }\n\ \n\ /**\n\ * Decodes a unit-length vector in 'oct' encoding to a normalized 3-component Cartesian vector.\n\ * The 'oct' encoding is described in \"A Survey of Efficient Representations of Independent Unit Vectors\",\n\ * Cigolle et al 2014: http://jcgt.org/published/0003/02/01/\n\ *\n\ * @name czm_octDecode\n\ * @param {vec2} encoded The oct-encoded, unit-length vector\n\ * @returns {vec3} The decoded and normalized vector\n\ */\n\ vec3 czm_octDecode(vec2 encoded)\n\ {\n\ return czm_octDecode(encoded, 255.0);\n\ }\n\ \n\ /**\n\ * Decodes a unit-length vector in 'oct' encoding packed into a floating-point number to a normalized 3-component Cartesian vector.\n\ * The 'oct' encoding is described in \"A Survey of Efficient Representations of Independent Unit Vectors\",\n\ * Cigolle et al 2014: http://jcgt.org/published/0003/02/01/\n\ *\n\ * @name czm_octDecode\n\ * @param {float} encoded The oct-encoded, unit-length vector\n\ * @returns {vec3} The decoded and normalized vector\n\ */\n\ vec3 czm_octDecode(float encoded)\n\ {\n\ float temp = encoded / 256.0;\n\ float x = floor(temp);\n\ float y = (temp - x) * 256.0;\n\ return czm_octDecode(vec2(x, y));\n\ }\n\ \n\ /**\n\ * Decodes three unit-length vectors in 'oct' encoding packed into two floating-point numbers to normalized 3-component Cartesian vectors.\n\ * The 'oct' encoding is described in \"A Survey of Efficient Representations of Independent Unit Vectors\",\n\ * Cigolle et al 2014: http://jcgt.org/published/0003/02/01/\n\ *\n\ * @name czm_octDecode\n\ * @param {vec2} encoded The packed oct-encoded, unit-length vectors.\n\ * @param {vec3} vector1 One decoded and normalized vector.\n\ * @param {vec3} vector2 One decoded and normalized vector.\n\ * @param {vec3} vector3 One decoded and normalized vector.\n\ */\n\ void czm_octDecode(vec2 encoded, out vec3 vector1, out vec3 vector2, out vec3 vector3)\n\ {\n\ float temp = encoded.x / 65536.0;\n\ float x = floor(temp);\n\ float encodedFloat1 = (temp - x) * 65536.0;\n\ \n\ temp = encoded.y / 65536.0;\n\ float y = floor(temp);\n\ float encodedFloat2 = (temp - y) * 65536.0;\n\ \n\ vector1 = czm_octDecode(encodedFloat1);\n\ vector2 = czm_octDecode(encodedFloat2);\n\ vector3 = czm_octDecode(vec2(x, y));\n\ }\n\ \n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_packDepth = "/**\n\ * Packs a depth value into a vec3 that can be represented by unsigned bytes.\n\ *\n\ * @name czm_packDepth\n\ * @glslFunction\n\ *\n\ * @param {float} depth The floating-point depth.\n\ * @returns {vec3} The packed depth.\n\ */\n\ vec4 czm_packDepth(float depth)\n\ {\n\ // See Aras Pranckevičius' post Encoding Floats to RGBA\n\ // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/\n\ vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * depth;\n\ enc = fract(enc);\n\ enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);\n\ return enc;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_phong = "float czm_private_getLambertDiffuseOfMaterial(vec3 lightDirectionEC, czm_material material)\n\ {\n\ return czm_getLambertDiffuse(lightDirectionEC, material.normal);\n\ }\n\ \n\ float czm_private_getSpecularOfMaterial(vec3 lightDirectionEC, vec3 toEyeEC, czm_material material)\n\ {\n\ return czm_getSpecular(lightDirectionEC, toEyeEC, material.normal, material.shininess);\n\ }\n\ \n\ /**\n\ * Computes a color using the Phong lighting model.\n\ *\n\ * @name czm_phong\n\ * @glslFunction\n\ *\n\ * @param {vec3} toEye A normalized vector from the fragment to the eye in eye coordinates.\n\ * @param {czm_material} material The fragment's material.\n\ *\n\ * @returns {vec4} The computed color.\n\ *\n\ * @example\n\ * vec3 positionToEyeEC = // ...\n\ * czm_material material = // ...\n\ * vec3 lightDirectionEC = // ...\n\ * gl_FragColor = czm_phong(normalize(positionToEyeEC), material, lightDirectionEC);\n\ *\n\ * @see czm_getMaterial\n\ */\n\ vec4 czm_phong(vec3 toEye, czm_material material, vec3 lightDirectionEC)\n\ {\n\ // Diffuse from directional light sources at eye (for top-down)\n\ float diffuse = czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 0.0, 1.0), material);\n\ if (czm_sceneMode == czm_sceneMode3D) {\n\ // (and horizon views in 3D)\n\ diffuse += czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 1.0, 0.0), material);\n\ }\n\ \n\ float specular = czm_private_getSpecularOfMaterial(lightDirectionEC, toEye, material);\n\ \n\ // Temporary workaround for adding ambient.\n\ vec3 materialDiffuse = material.diffuse * 0.5;\n\ \n\ vec3 ambient = materialDiffuse;\n\ vec3 color = ambient + material.emission;\n\ color += materialDiffuse * diffuse * czm_lightColor;\n\ color += material.specular * specular * czm_lightColor;\n\ \n\ return vec4(color, material.alpha);\n\ }\n\ \n\ vec4 czm_private_phong(vec3 toEye, czm_material material, vec3 lightDirectionEC)\n\ {\n\ float diffuse = czm_private_getLambertDiffuseOfMaterial(lightDirectionEC, material);\n\ float specular = czm_private_getSpecularOfMaterial(lightDirectionEC, toEye, material);\n\ \n\ vec3 ambient = vec3(0.0);\n\ vec3 color = ambient + material.emission;\n\ color += material.diffuse * diffuse * czm_lightColor;\n\ color += material.specular * specular * czm_lightColor;\n\ \n\ return vec4(color, material.alpha);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_planeDistance = "/**\n\ * Computes distance from a point to a plane.\n\ *\n\ * @name czm_planeDistance\n\ * @glslFunction\n\ *\n\ * param {vec4} plane A Plane in Hessian Normal Form. See Plane.js\n\ * param {vec3} point A point in the same space as the plane.\n\ * returns {float} The distance from the point to the plane.\n\ */\n\ float czm_planeDistance(vec4 plane, vec3 point) {\n\ return (dot(plane.xyz, point) + plane.w);\n\ }\n\ \n\ /**\n\ * Computes distance from a point to a plane.\n\ *\n\ * @name czm_planeDistance\n\ * @glslFunction\n\ *\n\ * param {vec3} planeNormal Normal for a plane in Hessian Normal Form. See Plane.js\n\ * param {float} planeDistance Distance for a plane in Hessian Normal form. See Plane.js\n\ * param {vec3} point A point in the same space as the plane.\n\ * returns {float} The distance from the point to the plane.\n\ */\n\ float czm_planeDistance(vec3 planeNormal, float planeDistance, vec3 point) {\n\ return (dot(planeNormal, point) + planeDistance);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_pointAlongRay = "/**\n\ * Computes the point along a ray at the given time. time can be positive, negative, or zero.\n\ *\n\ * @name czm_pointAlongRay\n\ * @glslFunction\n\ *\n\ * @param {czm_ray} ray The ray to compute the point along.\n\ * @param {float} time The time along the ray.\n\ * \n\ * @returns {vec3} The point along the ray at the given time.\n\ * \n\ * @example\n\ * czm_ray ray = czm_ray(vec3(0.0), vec3(1.0, 0.0, 0.0)); // origin, direction\n\ * vec3 v = czm_pointAlongRay(ray, 2.0); // (2.0, 0.0, 0.0)\n\ */\n\ vec3 czm_pointAlongRay(czm_ray ray, float time)\n\ {\n\ return ray.origin + (time * ray.direction);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_rayEllipsoidIntersectionInterval = "/**\n\ * DOC_TBA\n\ *\n\ * @name czm_rayEllipsoidIntersectionInterval\n\ * @glslFunction\n\ */\n\ czm_raySegment czm_rayEllipsoidIntersectionInterval(czm_ray ray, vec3 ellipsoid_center, vec3 ellipsoid_inverseRadii)\n\ {\n\ // ray and ellipsoid center in eye coordinates. radii in model coordinates.\n\ vec3 q = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.origin, 1.0)).xyz;\n\ vec3 w = ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ray.direction, 0.0)).xyz;\n\ \n\ q = q - ellipsoid_inverseRadii * (czm_inverseModelView * vec4(ellipsoid_center, 1.0)).xyz;\n\ \n\ float q2 = dot(q, q);\n\ float qw = dot(q, w);\n\ \n\ if (q2 > 1.0) // Outside ellipsoid.\n\ {\n\ if (qw >= 0.0) // Looking outward or tangent (0 intersections).\n\ {\n\ return czm_emptyRaySegment;\n\ }\n\ else // qw < 0.0.\n\ {\n\ float qw2 = qw * qw;\n\ float difference = q2 - 1.0; // Positively valued.\n\ float w2 = dot(w, w);\n\ float product = w2 * difference;\n\ \n\ if (qw2 < product) // Imaginary roots (0 intersections).\n\ {\n\ return czm_emptyRaySegment;\n\ }\n\ else if (qw2 > product) // Distinct roots (2 intersections).\n\ {\n\ float discriminant = qw * qw - product;\n\ float temp = -qw + sqrt(discriminant); // Avoid cancellation.\n\ float root0 = temp / w2;\n\ float root1 = difference / temp;\n\ if (root0 < root1)\n\ {\n\ czm_raySegment i = czm_raySegment(root0, root1);\n\ return i;\n\ }\n\ else\n\ {\n\ czm_raySegment i = czm_raySegment(root1, root0);\n\ return i;\n\ }\n\ }\n\ else // qw2 == product. Repeated roots (2 intersections).\n\ {\n\ float root = sqrt(difference / w2);\n\ czm_raySegment i = czm_raySegment(root, root);\n\ return i;\n\ }\n\ }\n\ }\n\ else if (q2 < 1.0) // Inside ellipsoid (2 intersections).\n\ {\n\ float difference = q2 - 1.0; // Negatively valued.\n\ float w2 = dot(w, w);\n\ float product = w2 * difference; // Negatively valued.\n\ float discriminant = qw * qw - product;\n\ float temp = -qw + sqrt(discriminant); // Positively valued.\n\ czm_raySegment i = czm_raySegment(0.0, temp / w2);\n\ return i;\n\ }\n\ else // q2 == 1.0. On ellipsoid.\n\ {\n\ if (qw < 0.0) // Looking inward.\n\ {\n\ float w2 = dot(w, w);\n\ czm_raySegment i = czm_raySegment(0.0, -qw / w2);\n\ return i;\n\ }\n\ else // qw >= 0.0. Looking outward or tangent.\n\ {\n\ return czm_emptyRaySegment;\n\ }\n\ }\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_readDepth = "float czm_readDepth(sampler2D depthTexture, vec2 texCoords)\n\ {\n\ return czm_reverseLogDepth(texture2D(depthTexture, texCoords).r);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_readNonPerspective = "/**\n\ * Reads a value previously transformed with {@link czm_writeNonPerspective}\n\ * by dividing it by `w`, the value used in the perspective divide.\n\ * This function is intended to be called in a fragment shader to access a\n\ * `varying` that should not be subject to perspective interpolation.\n\ * For example, screen-space texture coordinates. The value should have been\n\ * previously written in the vertex shader with a call to\n\ * {@link czm_writeNonPerspective}.\n\ *\n\ * @name czm_readNonPerspective\n\ * @glslFunction\n\ *\n\ * @param {float|vec2|vec3|vec4} value The non-perspective value to be read.\n\ * @param {float} oneOverW One over the perspective divide value, `w`. Usually this is simply `gl_FragCoord.w`.\n\ * @returns {float|vec2|vec3|vec4} The usable value.\n\ */\n\ float czm_readNonPerspective(float value, float oneOverW) {\n\ return value * oneOverW;\n\ }\n\ \n\ vec2 czm_readNonPerspective(vec2 value, float oneOverW) {\n\ return value * oneOverW;\n\ }\n\ \n\ vec3 czm_readNonPerspective(vec3 value, float oneOverW) {\n\ return value * oneOverW;\n\ }\n\ \n\ vec4 czm_readNonPerspective(vec4 value, float oneOverW) {\n\ return value * oneOverW;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_reverseLogDepth = "float czm_reverseLogDepth(float logZ)\n\ {\n\ #ifdef LOG_DEPTH\n\ float near = czm_currentFrustum.x;\n\ float far = czm_currentFrustum.y;\n\ float log2Depth = logZ * czm_log2FarDepthFromNearPlusOne;\n\ float depthFromNear = pow(2.0, log2Depth) - 1.0;\n\ return far * (1.0 - near / (depthFromNear + near)) / (far - near);\n\ #endif\n\ return logZ;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sampleOctahedralProjection = "/**\n\ * Samples the 4 neighboring pixels and return the weighted average.\n\ *\n\ * @private\n\ */\n\ vec3 czm_sampleOctahedralProjectionWithFiltering(sampler2D projectedMap, vec2 textureSize, vec3 direction, float lod)\n\ {\n\ direction /= dot(vec3(1.0), abs(direction));\n\ vec2 rev = abs(direction.zx) - vec2(1.0);\n\ vec2 neg = vec2(direction.x < 0.0 ? rev.x : -rev.x,\n\ direction.z < 0.0 ? rev.y : -rev.y);\n\ vec2 uv = direction.y < 0.0 ? neg : direction.xz;\n\ vec2 coord = 0.5 * uv + vec2(0.5);\n\ vec2 pixel = 1.0 / textureSize;\n\ \n\ if (lod > 0.0)\n\ {\n\ // Each subseqeuent mip level is half the size\n\ float scale = 1.0 / pow(2.0, lod);\n\ float offset = ((textureSize.y + 1.0) / textureSize.x);\n\ \n\ coord.x *= offset;\n\ coord *= scale;\n\ \n\ coord.x += offset + pixel.x;\n\ coord.y += (1.0 - (1.0 / pow(2.0, lod - 1.0))) + pixel.y * (lod - 1.0) * 2.0;\n\ }\n\ else\n\ {\n\ coord.x *= (textureSize.y / textureSize.x);\n\ }\n\ \n\ // Do bilinear filtering\n\ #ifndef OES_texture_float_linear\n\ vec3 color1 = texture2D(projectedMap, coord + vec2(0.0, pixel.y)).rgb;\n\ vec3 color2 = texture2D(projectedMap, coord + vec2(pixel.x, 0.0)).rgb;\n\ vec3 color3 = texture2D(projectedMap, coord + pixel).rgb;\n\ vec3 color4 = texture2D(projectedMap, coord).rgb;\n\ \n\ vec2 texturePosition = coord * textureSize;\n\ \n\ float fu = fract(texturePosition.x);\n\ float fv = fract(texturePosition.y);\n\ \n\ vec3 average1 = mix(color4, color2, fu);\n\ vec3 average2 = mix(color1, color3, fu);\n\ \n\ vec3 color = mix(average1, average2, fv);\n\ #else\n\ vec3 color = texture2D(projectedMap, coord).rgb;\n\ #endif\n\ \n\ return color;\n\ }\n\ \n\ \n\ /**\n\ * Samples from a cube map that has been projected using an octahedral projection from the given direction.\n\ *\n\ * @name czm_sampleOctahedralProjection\n\ * @glslFunction\n\ *\n\ * @param {sampler2D} projectedMap The texture with the octahedral projected cube map.\n\ * @param {vec2} textureSize The width and height dimensions in pixels of the projected map.\n\ * @param {vec3} direction The normalized direction used to sample the cube map.\n\ * @param {float} lod The level of detail to sample.\n\ * @param {float} maxLod The maximum level of detail.\n\ * @returns {vec3} The color of the cube map at the direction.\n\ */\n\ vec3 czm_sampleOctahedralProjection(sampler2D projectedMap, vec2 textureSize, vec3 direction, float lod, float maxLod) {\n\ float currentLod = floor(lod + 0.5);\n\ float nextLod = min(currentLod + 1.0, maxLod);\n\ \n\ vec3 colorCurrentLod = czm_sampleOctahedralProjectionWithFiltering(projectedMap, textureSize, direction, currentLod);\n\ vec3 colorNextLod = czm_sampleOctahedralProjectionWithFiltering(projectedMap, textureSize, direction, nextLod);\n\ \n\ return mix(colorNextLod, colorCurrentLod, nextLod - lod);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_saturation = "/**\n\ * Adjusts the saturation of a color.\n\ * \n\ * @name czm_saturation\n\ * @glslFunction\n\ * \n\ * @param {vec3} rgb The color.\n\ * @param {float} adjustment The amount to adjust the saturation of the color.\n\ *\n\ * @returns {float} The color with the saturation adjusted.\n\ *\n\ * @example\n\ * vec3 greyScale = czm_saturation(color, 0.0);\n\ * vec3 doubleSaturation = czm_saturation(color, 2.0);\n\ */\n\ vec3 czm_saturation(vec3 rgb, float adjustment)\n\ {\n\ // Algorithm from Chapter 16 of OpenGL Shading Language\n\ const vec3 W = vec3(0.2125, 0.7154, 0.0721);\n\ vec3 intensity = vec3(dot(rgb, W));\n\ return mix(intensity, rgb, adjustment);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_shadowDepthCompare = "\n\ float czm_sampleShadowMap(highp samplerCube shadowMap, vec3 d)\n\ {\n\ return czm_unpackDepth(textureCube(shadowMap, d));\n\ }\n\ \n\ float czm_sampleShadowMap(highp sampler2D shadowMap, vec2 uv)\n\ {\n\ #ifdef USE_SHADOW_DEPTH_TEXTURE\n\ return texture2D(shadowMap, uv).r;\n\ #else\n\ return czm_unpackDepth(texture2D(shadowMap, uv));\n\ #endif\n\ }\n\ \n\ float czm_shadowDepthCompare(samplerCube shadowMap, vec3 uv, float depth)\n\ {\n\ return step(depth, czm_sampleShadowMap(shadowMap, uv));\n\ }\n\ \n\ float czm_shadowDepthCompare(sampler2D shadowMap, vec2 uv, float depth)\n\ {\n\ return step(depth, czm_sampleShadowMap(shadowMap, uv));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_shadowVisibility = "\n\ float czm_private_shadowVisibility(float visibility, float nDotL, float normalShadingSmooth, float darkness)\n\ {\n\ #ifdef USE_NORMAL_SHADING\n\ #ifdef USE_NORMAL_SHADING_SMOOTH\n\ float strength = clamp(nDotL / normalShadingSmooth, 0.0, 1.0);\n\ #else\n\ float strength = step(0.0, nDotL);\n\ #endif\n\ visibility *= strength;\n\ #endif\n\ \n\ visibility = max(visibility, darkness);\n\ return visibility;\n\ }\n\ \n\ #ifdef USE_CUBE_MAP_SHADOW\n\ float czm_shadowVisibility(samplerCube shadowMap, czm_shadowParameters shadowParameters)\n\ {\n\ float depthBias = shadowParameters.depthBias;\n\ float depth = shadowParameters.depth;\n\ float nDotL = shadowParameters.nDotL;\n\ float normalShadingSmooth = shadowParameters.normalShadingSmooth;\n\ float darkness = shadowParameters.darkness;\n\ vec3 uvw = shadowParameters.texCoords;\n\ \n\ depth -= depthBias;\n\ float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);\n\ return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);\n\ }\n\ #else\n\ float czm_shadowVisibility(sampler2D shadowMap, czm_shadowParameters shadowParameters)\n\ {\n\ float depthBias = shadowParameters.depthBias;\n\ float depth = shadowParameters.depth;\n\ float nDotL = shadowParameters.nDotL;\n\ float normalShadingSmooth = shadowParameters.normalShadingSmooth;\n\ float darkness = shadowParameters.darkness;\n\ vec2 uv = shadowParameters.texCoords;\n\ \n\ depth -= depthBias;\n\ #ifdef USE_SOFT_SHADOWS\n\ vec2 texelStepSize = shadowParameters.texelStepSize;\n\ float radius = 1.0;\n\ float dx0 = -texelStepSize.x * radius;\n\ float dy0 = -texelStepSize.y * radius;\n\ float dx1 = texelStepSize.x * radius;\n\ float dy1 = texelStepSize.y * radius;\n\ float visibility = (\n\ czm_shadowDepthCompare(shadowMap, uv, depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy0), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy0), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy0), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, 0.0), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, 0.0), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy1), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy1), depth) +\n\ czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy1), depth)\n\ ) * (1.0 / 9.0);\n\ #else\n\ float visibility = czm_shadowDepthCompare(shadowMap, uv, depth);\n\ #endif\n\ \n\ return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);\n\ }\n\ #endif\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_signNotZero = "/**\n\ * Returns 1.0 if the given value is positive or zero, and -1.0 if it is negative. This is similar to the GLSL\n\ * built-in function sign except that returns 1.0 instead of 0.0 when the input value is 0.0.\n\ * \n\ * @name czm_signNotZero\n\ * @glslFunction\n\ *\n\ * @param {} value The value for which to determine the sign.\n\ * @returns {} 1.0 if the value is positive or zero, -1.0 if the value is negative.\n\ */\n\ float czm_signNotZero(float value)\n\ {\n\ return value >= 0.0 ? 1.0 : -1.0;\n\ }\n\ \n\ vec2 czm_signNotZero(vec2 value)\n\ {\n\ return vec2(czm_signNotZero(value.x), czm_signNotZero(value.y));\n\ }\n\ \n\ vec3 czm_signNotZero(vec3 value)\n\ {\n\ return vec3(czm_signNotZero(value.x), czm_signNotZero(value.y), czm_signNotZero(value.z));\n\ }\n\ \n\ vec4 czm_signNotZero(vec4 value)\n\ {\n\ return vec4(czm_signNotZero(value.x), czm_signNotZero(value.y), czm_signNotZero(value.z), czm_signNotZero(value.w));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_sphericalHarmonics = "/**\n\ * Computes a color from the third order spherical harmonic coefficients and a normalized direction vector.\n\ *

\n\ * The order of the coefficients is [L00, L1_1, L10, L11, L2_2, L2_1, L20, L21, L22].\n\ *

\n\ *\n\ * @name czm_sphericalHarmonics\n\ * @glslFunction\n\ *\n\ * @param {vec3} normal The normalized direction.\n\ * @param {vec3[9]} coefficients The third order spherical harmonic coefficients.\n\ * @returns {vec3} The color at the direction.\n\ *\n\ * @see https://graphics.stanford.edu/papers/envmap/envmap.pdf\n\ */\n\ vec3 czm_sphericalHarmonics(vec3 normal, vec3 coefficients[9])\n\ {\n\ const float c1 = 0.429043;\n\ const float c2 = 0.511664;\n\ const float c3 = 0.743125;\n\ const float c4 = 0.886227;\n\ const float c5 = 0.247708;\n\ \n\ vec3 L00 = coefficients[0];\n\ vec3 L1_1 = coefficients[1];\n\ vec3 L10 = coefficients[2];\n\ vec3 L11 = coefficients[3];\n\ vec3 L2_2 = coefficients[4];\n\ vec3 L2_1 = coefficients[5];\n\ vec3 L20 = coefficients[6];\n\ vec3 L21 = coefficients[7];\n\ vec3 L22 = coefficients[8];\n\ \n\ float x = normal.x;\n\ float y = normal.y;\n\ float z = normal.z;\n\ \n\ return c1 * L22 * (x * x - y * y) + c3 * L20 * z * z + c4 * L00 - c5 * L20 +\n\ 2.0 * c1 * (L2_2 * x * y + L21 * x * z + L2_1 * y * z) +\n\ 2.0 * c2 * (L11 * x + L1_1 * y + L10 * z);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_tangentToEyeSpaceMatrix = "/**\n\ * Creates a matrix that transforms vectors from tangent space to eye space.\n\ *\n\ * @name czm_tangentToEyeSpaceMatrix\n\ * @glslFunction\n\ *\n\ * @param {vec3} normalEC The normal vector in eye coordinates.\n\ * @param {vec3} tangentEC The tangent vector in eye coordinates.\n\ * @param {vec3} bitangentEC The bitangent vector in eye coordinates.\n\ *\n\ * @returns {mat3} The matrix that transforms from tangent space to eye space.\n\ *\n\ * @example\n\ * mat3 tangentToEye = czm_tangentToEyeSpaceMatrix(normalEC, tangentEC, bitangentEC);\n\ * vec3 normal = tangentToEye * texture2D(normalMap, st).xyz;\n\ */\n\ mat3 czm_tangentToEyeSpaceMatrix(vec3 normalEC, vec3 tangentEC, vec3 bitangentEC)\n\ {\n\ vec3 normal = normalize(normalEC);\n\ vec3 tangent = normalize(tangentEC);\n\ vec3 bitangent = normalize(bitangentEC);\n\ return mat3(tangent.x , tangent.y , tangent.z,\n\ bitangent.x, bitangent.y, bitangent.z,\n\ normal.x , normal.y , normal.z);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_transformPlane = "/**\n\ * Transforms a plane.\n\ * \n\ * @name czm_transformPlane\n\ * @glslFunction\n\ *\n\ * @param {vec4} plane The plane in Hessian Normal Form.\n\ * @param {mat4} transform The inverse-transpose of a transformation matrix.\n\ */\n\ vec4 czm_transformPlane(vec4 plane, mat4 transform) {\n\ vec4 transformedPlane = transform * plane;\n\ // Convert the transformed plane to Hessian Normal Form\n\ float normalMagnitude = length(transformedPlane.xyz);\n\ return transformedPlane / normalMagnitude;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_translateRelativeToEye = "/**\n\ * Translates a position (or any vec3) that was encoded with {@link EncodedCartesian3},\n\ * and then provided to the shader as separate high and low bits to\n\ * be relative to the eye. As shown in the example, the position can then be transformed in eye\n\ * or clip coordinates using {@link czm_modelViewRelativeToEye} or {@link czm_modelViewProjectionRelativeToEye},\n\ * respectively.\n\ *

\n\ * This technique, called GPU RTE, eliminates jittering artifacts when using large coordinates as\n\ * described in {@link http://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm|Precisions, Precisions}.\n\ *

\n\ *\n\ * @name czm_translateRelativeToEye\n\ * @glslFunction\n\ *\n\ * @param {vec3} high The position's high bits.\n\ * @param {vec3} low The position's low bits.\n\ * @returns {vec3} The position translated to be relative to the camera's position.\n\ *\n\ * @example\n\ * attribute vec3 positionHigh;\n\ * attribute vec3 positionLow;\n\ *\n\ * void main()\n\ * {\n\ * vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);\n\ * gl_Position = czm_modelViewProjectionRelativeToEye * p;\n\ * }\n\ *\n\ * @see czm_modelViewRelativeToEye\n\ * @see czm_modelViewProjectionRelativeToEye\n\ * @see czm_computePosition\n\ * @see EncodedCartesian3\n\ */\n\ vec4 czm_translateRelativeToEye(vec3 high, vec3 low)\n\ {\n\ vec3 highDifference = high - czm_encodedCameraPositionMCHigh;\n\ vec3 lowDifference = low - czm_encodedCameraPositionMCLow;\n\ \n\ return vec4(highDifference + lowDifference, 1.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_translucentPhong = "/**\n\ * @private\n\ */\n\ vec4 czm_translucentPhong(vec3 toEye, czm_material material, vec3 lightDirectionEC)\n\ {\n\ // Diffuse from directional light sources at eye (for top-down and horizon views)\n\ float diffuse = czm_getLambertDiffuse(vec3(0.0, 0.0, 1.0), material.normal);\n\ \n\ if (czm_sceneMode == czm_sceneMode3D) {\n\ // (and horizon views in 3D)\n\ diffuse += czm_getLambertDiffuse(vec3(0.0, 1.0, 0.0), material.normal);\n\ }\n\ \n\ diffuse = clamp(diffuse, 0.0, 1.0);\n\ \n\ float specular = czm_getSpecular(lightDirectionEC, toEye, material.normal, material.shininess);\n\ \n\ // Temporary workaround for adding ambient.\n\ vec3 materialDiffuse = material.diffuse * 0.5;\n\ \n\ vec3 ambient = materialDiffuse;\n\ vec3 color = ambient + material.emission;\n\ color += materialDiffuse * diffuse * czm_lightColor;\n\ color += material.specular * specular * czm_lightColor;\n\ \n\ return vec4(color, material.alpha);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_transpose = "/**\n\ * Returns the transpose of the matrix. The input matrix can be\n\ * a mat2, mat3, or mat4.\n\ *\n\ * @name czm_transpose\n\ * @glslFunction\n\ *\n\ * @param {} matrix The matrix to transpose.\n\ *\n\ * @returns {} The transposed matrix.\n\ *\n\ * @example\n\ * // GLSL declarations\n\ * mat2 czm_transpose(mat2 matrix);\n\ * mat3 czm_transpose(mat3 matrix);\n\ * mat4 czm_transpose(mat4 matrix);\n\ *\n\ * // Transpose a 3x3 rotation matrix to find its inverse.\n\ * mat3 eastNorthUpToEye = czm_eastNorthUpToEyeCoordinates(\n\ * positionMC, normalEC);\n\ * mat3 eyeToEastNorthUp = czm_transpose(eastNorthUpToEye);\n\ */\n\ mat2 czm_transpose(mat2 matrix)\n\ {\n\ return mat2(\n\ matrix[0][0], matrix[1][0],\n\ matrix[0][1], matrix[1][1]);\n\ }\n\ \n\ mat3 czm_transpose(mat3 matrix)\n\ {\n\ return mat3(\n\ matrix[0][0], matrix[1][0], matrix[2][0],\n\ matrix[0][1], matrix[1][1], matrix[2][1],\n\ matrix[0][2], matrix[1][2], matrix[2][2]);\n\ }\n\ \n\ mat4 czm_transpose(mat4 matrix)\n\ {\n\ return mat4(\n\ matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],\n\ matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],\n\ matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],\n\ matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_unpackDepth = "/**\n\ * Unpacks a vec4 depth value to a float in [0, 1) range.\n\ *\n\ * @name czm_unpackDepth\n\ * @glslFunction\n\ *\n\ * @param {vec4} packedDepth The packed depth.\n\ *\n\ * @returns {float} The floating-point depth in [0, 1) range.\n\ */\n\ float czm_unpackDepth(vec4 packedDepth)\n\ {\n\ // See Aras Pranckevičius' post Encoding Floats to RGBA\n\ // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/\n\ return dot(packedDepth, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0));\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_unpackFloat = "/**\n\ * Unpack an IEEE 754 single-precision float that is packed as a little-endian unsigned normalized vec4.\n\ *\n\ * @name czm_unpackFloat\n\ * @glslFunction\n\ *\n\ * @param {vec4} packedFloat The packed float.\n\ *\n\ * @returns {float} The floating-point depth in arbitrary range.\n\ */\n\ float czm_unpackFloat(vec4 packedFloat)\n\ {\n\ // Convert to [0.0, 255.0] and round to integer\n\ packedFloat = floor(packedFloat * 255.0 + 0.5);\n\ float sign = 1.0 - step(128.0, packedFloat[3]) * 2.0;\n\ float exponent = 2.0 * mod(packedFloat[3], 128.0) + step(128.0, packedFloat[2]) - 127.0; \n\ if (exponent == -127.0)\n\ {\n\ return 0.0;\n\ }\n\ float mantissa = mod(packedFloat[2], 128.0) * 65536.0 + packedFloat[1] * 256.0 + packedFloat[0] + float(0x800000);\n\ float result = sign * exp2(exponent - 23.0) * mantissa;\n\ return result;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_vertexLogDepth = "#ifdef LOG_DEPTH\n\ // 1.0 at the near plane, increasing linearly from there.\n\ varying float v_depthFromNearPlusOne;\n\ #ifdef SHADOW_MAP\n\ varying vec3 v_logPositionEC;\n\ #endif\n\ #endif\n\ \n\ vec4 czm_updatePositionDepth(vec4 coords) {\n\ #if defined(LOG_DEPTH)\n\ \n\ #ifdef SHADOW_MAP\n\ vec3 logPositionEC = (czm_inverseProjection * coords).xyz;\n\ v_logPositionEC = logPositionEC;\n\ #endif\n\ \n\ // With the very high far/near ratios used with the logarithmic depth\n\ // buffer, floating point rounding errors can cause linear depth values\n\ // to end up on the wrong side of the far plane, even for vertices that\n\ // are really nowhere near it. Since we always write a correct logarithmic\n\ // depth value in the fragment shader anyway, we just need to make sure\n\ // such errors don't cause the primitive to be clipped entirely before\n\ // we even get to the fragment shader.\n\ coords.z = clamp(coords.z / coords.w, -1.0, 1.0) * coords.w;\n\ #endif\n\ \n\ return coords;\n\ }\n\ \n\ /**\n\ * Writes the logarithmic depth to gl_Position using the already computed gl_Position.\n\ *\n\ * @name czm_vertexLogDepth\n\ * @glslFunction\n\ */\n\ void czm_vertexLogDepth()\n\ {\n\ #ifdef LOG_DEPTH\n\ v_depthFromNearPlusOne = (gl_Position.w - czm_currentFrustum.x) + 1.0;\n\ gl_Position = czm_updatePositionDepth(gl_Position);\n\ #endif\n\ }\n\ \n\ /**\n\ * Writes the logarithmic depth to gl_Position using the provided clip coordinates.\n\ *

\n\ * An example use case for this function would be moving the vertex in window coordinates\n\ * before converting back to clip coordinates. Use the original vertex clip coordinates.\n\ *

\n\ * @name czm_vertexLogDepth\n\ * @glslFunction\n\ *\n\ * @param {vec4} clipCoords The vertex in clip coordinates.\n\ *\n\ * @example\n\ * czm_vertexLogDepth(czm_projection * vec4(positionEyeCoordinates, 1.0));\n\ */\n\ void czm_vertexLogDepth(vec4 clipCoords)\n\ {\n\ #ifdef LOG_DEPTH\n\ v_depthFromNearPlusOne = (clipCoords.w - czm_currentFrustum.x) + 1.0;\n\ czm_updatePositionDepth(clipCoords);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_windowToEyeCoordinates = "/**\n\ * Transforms a position from window to eye coordinates.\n\ * The transform from window to normalized device coordinates is done using components\n\ * of (@link czm_viewport} and {@link czm_viewportTransformation} instead of calculating\n\ * the inverse of czm_viewportTransformation. The transformation from\n\ * normalized device coordinates to clip coordinates is done using fragmentCoordinate.w,\n\ * which is expected to be the scalar used in the perspective divide. The transformation\n\ * from clip to eye coordinates is done using {@link czm_inverseProjection}.\n\ *\n\ * @name czm_windowToEyeCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec4} fragmentCoordinate The position in window coordinates to transform.\n\ *\n\ * @returns {vec4} The transformed position in eye coordinates.\n\ *\n\ * @see czm_modelToWindowCoordinates\n\ * @see czm_eyeToWindowCoordinates\n\ * @see czm_inverseProjection\n\ * @see czm_viewport\n\ * @see czm_viewportTransformation\n\ *\n\ * @example\n\ * vec4 positionEC = czm_windowToEyeCoordinates(gl_FragCoord);\n\ */\n\ vec4 czm_windowToEyeCoordinates(vec4 fragmentCoordinate)\n\ {\n\ // Reconstruct NDC coordinates\n\ float x = 2.0 * (fragmentCoordinate.x - czm_viewport.x) / czm_viewport.z - 1.0;\n\ float y = 2.0 * (fragmentCoordinate.y - czm_viewport.y) / czm_viewport.w - 1.0;\n\ float z = (fragmentCoordinate.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];\n\ vec4 q = vec4(x, y, z, 1.0);\n\ \n\ // Reverse the perspective division to obtain clip coordinates.\n\ q /= fragmentCoordinate.w;\n\ \n\ // Reverse the projection transformation to obtain eye coordinates.\n\ if (!(czm_inverseProjection == mat4(0.0))) // IE and Edge sometimes do something weird with != between mat4s\n\ {\n\ q = czm_inverseProjection * q;\n\ }\n\ else\n\ {\n\ float top = czm_frustumPlanes.x;\n\ float bottom = czm_frustumPlanes.y;\n\ float left = czm_frustumPlanes.z;\n\ float right = czm_frustumPlanes.w;\n\ \n\ float near = czm_currentFrustum.x;\n\ float far = czm_currentFrustum.y;\n\ \n\ q.x = (q.x * (right - left) + left + right) * 0.5;\n\ q.y = (q.y * (top - bottom) + bottom + top) * 0.5;\n\ q.z = (q.z * (near - far) - near - far) * 0.5;\n\ q.w = 1.0;\n\ }\n\ \n\ return q;\n\ }\n\ \n\ /**\n\ * Transforms a position given as window x/y and a depth or a log depth from window to eye coordinates.\n\ * This function produces more accurate results for window positions with log depth than\n\ * conventionally unpacking the log depth using czm_reverseLogDepth and using the standard version\n\ * of czm_windowToEyeCoordinates.\n\ *\n\ * @name czm_windowToEyeCoordinates\n\ * @glslFunction\n\ *\n\ * @param {vec2} fragmentCoordinateXY The XY position in window coordinates to transform.\n\ * @param {float} depthOrLogDepth A depth or log depth for the fragment.\n\ *\n\ * @see czm_modelToWindowCoordinates\n\ * @see czm_eyeToWindowCoordinates\n\ * @see czm_inverseProjection\n\ * @see czm_viewport\n\ * @see czm_viewportTransformation\n\ *\n\ * @returns {vec4} The transformed position in eye coordinates.\n\ */\n\ vec4 czm_windowToEyeCoordinates(vec2 fragmentCoordinateXY, float depthOrLogDepth)\n\ {\n\ // See reverseLogDepth.glsl. This is separate to re-use the pow.\n\ #ifdef LOG_DEPTH\n\ float near = czm_currentFrustum.x;\n\ float far = czm_currentFrustum.y;\n\ float log2Depth = depthOrLogDepth * czm_log2FarDepthFromNearPlusOne;\n\ float depthFromNear = pow(2.0, log2Depth) - 1.0;\n\ float depthFromCamera = depthFromNear + near;\n\ vec4 windowCoord = vec4(fragmentCoordinateXY, far * (1.0 - near / depthFromCamera) / (far - near), 1.0);\n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(windowCoord);\n\ eyeCoordinate.w = 1.0 / depthFromCamera; // Better precision\n\ return eyeCoordinate;\n\ #else\n\ vec4 windowCoord = vec4(fragmentCoordinateXY, depthOrLogDepth, 1.0);\n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(windowCoord);\n\ #endif\n\ return eyeCoordinate;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_writeDepthClamp = "// emulated noperspective\n\ #if defined(GL_EXT_frag_depth) && !defined(LOG_DEPTH)\n\ varying float v_WindowZ;\n\ #endif\n\ \n\ /**\n\ * Emulates GL_DEPTH_CLAMP. Clamps a fragment to the near and far plane\n\ * by writing the fragment's depth. See czm_depthClamp for more details.\n\ *

\n\ * The shader must enable the GL_EXT_frag_depth extension.\n\ *

\n\ *\n\ * @name czm_writeDepthClamp\n\ * @glslFunction\n\ *\n\ * @example\n\ * gl_FragColor = color;\n\ * czm_writeDepthClamp();\n\ *\n\ * @see czm_depthClamp\n\ */\n\ void czm_writeDepthClamp()\n\ {\n\ #if defined(GL_EXT_frag_depth) && !defined(LOG_DEPTH)\n\ gl_FragDepthEXT = clamp(v_WindowZ * gl_FragCoord.w, 0.0, 1.0);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_writeLogDepth = "#ifdef LOG_DEPTH\n\ varying float v_depthFromNearPlusOne;\n\ \n\ #ifdef POLYGON_OFFSET\n\ uniform vec2 u_polygonOffset;\n\ #endif\n\ \n\ #endif\n\ \n\ /**\n\ * Writes the fragment depth to the logarithmic depth buffer.\n\ *

\n\ * Use this when the vertex shader does not call {@link czm_vertexlogDepth}, for example, when\n\ * ray-casting geometry using a full screen quad.\n\ *

\n\ * @name czm_writeLogDepth\n\ * @glslFunction\n\ *\n\ * @param {float} depth The depth coordinate, where 1.0 is on the near plane and\n\ * depth increases in eye-space units from there\n\ *\n\ * @example\n\ * czm_writeLogDepth((czm_projection * v_positionEyeCoordinates).w + 1.0);\n\ */\n\ void czm_writeLogDepth(float depth)\n\ {\n\ #if defined(GL_EXT_frag_depth) && defined(LOG_DEPTH)\n\ // Discard the vertex if it's not between the near and far planes.\n\ // We allow a bit of epsilon on the near plane comparison because a 1.0\n\ // from the vertex shader (indicating the vertex should be _on_ the near\n\ // plane) will not necessarily come here as exactly 1.0.\n\ if (depth <= 0.9999999 || depth > czm_farDepthFromNearPlusOne) {\n\ discard;\n\ }\n\ \n\ #ifdef POLYGON_OFFSET\n\ // Polygon offset: m * factor + r * units\n\ float factor = u_polygonOffset[0];\n\ float units = u_polygonOffset[1];\n\ \n\ // If we can't compute derivatives, just leave out the factor I guess?\n\ #ifdef GL_OES_standard_derivatives\n\ // m = sqrt(dZdX^2 + dZdY^2);\n\ float x = dFdx(depth);\n\ float y = dFdy(depth);\n\ float m = sqrt(x * x + y * y);\n\ \n\ // Apply the factor before computing the log depth.\n\ depth += m * factor;\n\ #endif\n\ \n\ #endif\n\ \n\ gl_FragDepthEXT = log2(depth) * czm_oneOverLog2FarDepthFromNearPlusOne;\n\ \n\ #ifdef POLYGON_OFFSET\n\ // Apply the units after the log depth.\n\ gl_FragDepthEXT += czm_epsilon7 * units;\n\ #endif\n\ \n\ #endif\n\ }\n\ \n\ /**\n\ * Writes the fragment depth to the logarithmic depth buffer.\n\ *

\n\ * Use this when the vertex shader calls {@link czm_vertexlogDepth}.\n\ *

\n\ *\n\ * @name czm_writeLogDepth\n\ * @glslFunction\n\ */\n\ void czm_writeLogDepth() {\n\ #ifdef LOG_DEPTH\n\ czm_writeLogDepth(v_depthFromNearPlusOne);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var czm_writeNonPerspective = "/**\n\ * Transforms a value for non-perspective interpolation by multiplying\n\ * it by w, the value used in the perspective divide. This function is\n\ * intended to be called in a vertex shader to compute the value of a\n\ * `varying` that should not be subject to perspective interpolation.\n\ * For example, screen-space texture coordinates. The fragment shader\n\ * must call {@link czm_readNonPerspective} to retrieve the final\n\ * non-perspective value.\n\ *\n\ * @name czm_writeNonPerspective\n\ * @glslFunction\n\ *\n\ * @param {float|vec2|vec3|vec4} value The value to be interpolated without accounting for perspective.\n\ * @param {float} w The perspective divide value. Usually this is the computed `gl_Position.w`.\n\ * @returns {float|vec2|vec3|vec4} The transformed value, intended to be stored in a `varying` and read in the\n\ * fragment shader with {@link czm_readNonPerspective}.\n\ */\n\ float czm_writeNonPerspective(float value, float w) {\n\ return value * w;\n\ }\n\ \n\ vec2 czm_writeNonPerspective(vec2 value, float w) {\n\ return value * w;\n\ }\n\ \n\ vec3 czm_writeNonPerspective(vec3 value, float w) {\n\ return value * w;\n\ }\n\ \n\ vec4 czm_writeNonPerspective(vec4 value, float w) {\n\ return value * w;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var CzmBuiltins = { czm_degreesPerRadian : czm_degreesPerRadian, czm_depthRange : czm_depthRange, czm_epsilon1 : czm_epsilon1, czm_epsilon2 : czm_epsilon2, czm_epsilon3 : czm_epsilon3, czm_epsilon4 : czm_epsilon4, czm_epsilon5 : czm_epsilon5, czm_epsilon6 : czm_epsilon6, czm_epsilon7 : czm_epsilon7, czm_infinity : czm_infinity, czm_oneOverPi : czm_oneOverPi, czm_oneOverTwoPi : czm_oneOverTwoPi, czm_passCesium3DTile : czm_passCesium3DTile, czm_passCesium3DTileClassification : czm_passCesium3DTileClassification, czm_passCesium3DTileClassificationIgnoreShow : czm_passCesium3DTileClassificationIgnoreShow, czm_passClassification : czm_passClassification, czm_passCompute : czm_passCompute, czm_passEnvironment : czm_passEnvironment, czm_passGlobe : czm_passGlobe, czm_passOpaque : czm_passOpaque, czm_passOverlay : czm_passOverlay, czm_passTerrainClassification : czm_passTerrainClassification, czm_passTranslucent : czm_passTranslucent, czm_pi : czm_pi, czm_piOverFour : czm_piOverFour, czm_piOverSix : czm_piOverSix, czm_piOverThree : czm_piOverThree, czm_piOverTwo : czm_piOverTwo, czm_radiansPerDegree : czm_radiansPerDegree, czm_sceneMode2D : czm_sceneMode2D, czm_sceneMode3D : czm_sceneMode3D, czm_sceneModeColumbusView : czm_sceneModeColumbusView, czm_sceneModeMorphing : czm_sceneModeMorphing, czm_solarRadius : czm_solarRadius, czm_threePiOver2 : czm_threePiOver2, czm_twoPi : czm_twoPi, czm_webMercatorMaxLatitude : czm_webMercatorMaxLatitude, czm_depthRangeStruct : czm_depthRangeStruct, czm_material : czm_material, czm_materialInput : czm_materialInput, czm_ray : czm_ray, czm_raySegment : czm_raySegment, czm_shadowParameters : czm_shadowParameters, czm_HSBToRGB : czm_HSBToRGB, czm_HSLToRGB : czm_HSLToRGB, czm_RGBToHSB : czm_RGBToHSB, czm_RGBToHSL : czm_RGBToHSL, czm_RGBToXYZ : czm_RGBToXYZ, czm_XYZToRGB : czm_XYZToRGB, czm_acesTonemapping : czm_acesTonemapping, czm_alphaWeight : czm_alphaWeight, czm_antialias : czm_antialias, czm_approximateSphericalCoordinates : czm_approximateSphericalCoordinates, czm_backFacing : czm_backFacing, czm_branchFreeTernary : czm_branchFreeTernary, czm_cascadeColor : czm_cascadeColor, czm_cascadeDistance : czm_cascadeDistance, czm_cascadeMatrix : czm_cascadeMatrix, czm_cascadeWeights : czm_cascadeWeights, czm_columbusViewMorph : czm_columbusViewMorph, czm_computePosition : czm_computePosition, czm_cosineAndSine : czm_cosineAndSine, czm_decompressTextureCoordinates : czm_decompressTextureCoordinates, czm_depthClamp : czm_depthClamp, czm_eastNorthUpToEyeCoordinates : czm_eastNorthUpToEyeCoordinates, czm_ellipsoidContainsPoint : czm_ellipsoidContainsPoint, czm_ellipsoidWgs84TextureCoordinates : czm_ellipsoidWgs84TextureCoordinates, czm_equalsEpsilon : czm_equalsEpsilon, czm_eyeOffset : czm_eyeOffset, czm_eyeToWindowCoordinates : czm_eyeToWindowCoordinates, czm_fastApproximateAtan : czm_fastApproximateAtan, czm_fog : czm_fog, czm_gammaCorrect : czm_gammaCorrect, czm_geodeticSurfaceNormal : czm_geodeticSurfaceNormal, czm_getDefaultMaterial : czm_getDefaultMaterial, czm_getLambertDiffuse : czm_getLambertDiffuse, czm_getSpecular : czm_getSpecular, czm_getWaterNoise : czm_getWaterNoise, czm_hue : czm_hue, czm_inverseGamma : czm_inverseGamma, czm_isEmpty : czm_isEmpty, czm_isFull : czm_isFull, czm_latitudeToWebMercatorFraction : czm_latitudeToWebMercatorFraction, czm_lineDistance : czm_lineDistance, czm_luminance : czm_luminance, czm_metersPerPixel : czm_metersPerPixel, czm_modelToWindowCoordinates : czm_modelToWindowCoordinates, czm_multiplyWithColorBalance : czm_multiplyWithColorBalance, czm_nearFarScalar : czm_nearFarScalar, czm_octDecode : czm_octDecode, czm_packDepth : czm_packDepth, czm_phong : czm_phong, czm_planeDistance : czm_planeDistance, czm_pointAlongRay : czm_pointAlongRay, czm_rayEllipsoidIntersectionInterval : czm_rayEllipsoidIntersectionInterval, czm_readDepth : czm_readDepth, czm_readNonPerspective : czm_readNonPerspective, czm_reverseLogDepth : czm_reverseLogDepth, czm_sampleOctahedralProjection : czm_sampleOctahedralProjection, czm_saturation : czm_saturation, czm_shadowDepthCompare : czm_shadowDepthCompare, czm_shadowVisibility : czm_shadowVisibility, czm_signNotZero : czm_signNotZero, czm_sphericalHarmonics : czm_sphericalHarmonics, czm_tangentToEyeSpaceMatrix : czm_tangentToEyeSpaceMatrix, czm_transformPlane : czm_transformPlane, czm_translateRelativeToEye : czm_translateRelativeToEye, czm_translucentPhong : czm_translucentPhong, czm_transpose : czm_transpose, czm_unpackDepth : czm_unpackDepth, czm_unpackFloat : czm_unpackFloat, czm_vertexLogDepth : czm_vertexLogDepth, czm_windowToEyeCoordinates : czm_windowToEyeCoordinates, czm_writeDepthClamp : czm_writeDepthClamp, czm_writeLogDepth : czm_writeLogDepth, czm_writeNonPerspective : czm_writeNonPerspective }; function removeComments(source) { // remove inline comments source = source.replace(/\/\/.*/g, ""); // remove multiline comment block return source.replace(/\/\*\*[\s\S]*?\*\//gm, function (match) { // preserve the number of lines in the comment block so the line numbers will be correct when debugging shaders var numberOfLines = match.match(/\n/gm).length; var replacement = ""; for (var lineNumber = 0; lineNumber < numberOfLines; ++lineNumber) { replacement += "\n"; } return replacement; }); } function getDependencyNode(name, glslSource, nodes) { var dependencyNode; // check if already loaded for (var i = 0; i < nodes.length; ++i) { if (nodes[i].name === name) { dependencyNode = nodes[i]; } } if (!defined(dependencyNode)) { // strip doc comments so we don't accidentally try to determine a dependency for something found // in a comment glslSource = removeComments(glslSource); // create new node dependencyNode = { name: name, glslSource: glslSource, dependsOn: [], requiredBy: [], evaluated: false, }; nodes.push(dependencyNode); } return dependencyNode; } function generateDependencies(currentNode, dependencyNodes) { if (currentNode.evaluated) { return; } currentNode.evaluated = true; // identify all dependencies that are referenced from this glsl source code var czmMatches = currentNode.glslSource.match(/\bczm_[a-zA-Z0-9_]*/g); if (defined(czmMatches) && czmMatches !== null) { // remove duplicates czmMatches = czmMatches.filter(function (elem, pos) { return czmMatches.indexOf(elem) === pos; }); czmMatches.forEach(function (element) { if ( element !== currentNode.name && ShaderSource._czmBuiltinsAndUniforms.hasOwnProperty(element) ) { var referencedNode = getDependencyNode( element, ShaderSource._czmBuiltinsAndUniforms[element], dependencyNodes ); currentNode.dependsOn.push(referencedNode); referencedNode.requiredBy.push(currentNode); // recursive call to find any dependencies of the new node generateDependencies(referencedNode, dependencyNodes); } }); } } function sortDependencies(dependencyNodes) { var nodesWithoutIncomingEdges = []; var allNodes = []; while (dependencyNodes.length > 0) { var node = dependencyNodes.pop(); allNodes.push(node); if (node.requiredBy.length === 0) { nodesWithoutIncomingEdges.push(node); } } while (nodesWithoutIncomingEdges.length > 0) { var currentNode = nodesWithoutIncomingEdges.shift(); dependencyNodes.push(currentNode); for (var i = 0; i < currentNode.dependsOn.length; ++i) { // remove the edge from the graph var referencedNode = currentNode.dependsOn[i]; var index = referencedNode.requiredBy.indexOf(currentNode); referencedNode.requiredBy.splice(index, 1); // if referenced node has no more incoming edges, add to list if (referencedNode.requiredBy.length === 0) { nodesWithoutIncomingEdges.push(referencedNode); } } } // if there are any nodes left with incoming edges, then there was a circular dependency somewhere in the graph var badNodes = []; for (var j = 0; j < allNodes.length; ++j) { if (allNodes[j].requiredBy.length !== 0) { badNodes.push(allNodes[j]); } } //>>includeStart('debug', pragmas.debug); if (badNodes.length !== 0) { var message = "A circular dependency was found in the following built-in functions/structs/constants: \n"; for (var k = 0; k < badNodes.length; ++k) { message = message + badNodes[k].name + "\n"; } throw new DeveloperError(message); } //>>includeEnd('debug'); } function getBuiltinsAndAutomaticUniforms(shaderSource) { // generate a dependency graph for builtin functions var dependencyNodes = []; var root = getDependencyNode("main", shaderSource, dependencyNodes); generateDependencies(root, dependencyNodes); sortDependencies(dependencyNodes); // Concatenate the source code for the function dependencies. // Iterate in reverse so that dependent items are declared before they are used. var builtinsSource = ""; for (var i = dependencyNodes.length - 1; i >= 0; --i) { builtinsSource = builtinsSource + dependencyNodes[i].glslSource + "\n"; } return builtinsSource.replace(root.glslSource, ""); } function combineShader(shaderSource, isFragmentShader, context) { var i; var length; // Combine shader sources, generally for pseudo-polymorphism, e.g., czm_getMaterial. var combinedSources = ""; var sources = shaderSource.sources; if (defined(sources)) { for (i = 0, length = sources.length; i < length; ++i) { // #line needs to be on its own line. combinedSources += "\n#line 0\n" + sources[i]; } } combinedSources = removeComments(combinedSources); // Extract existing shader version from sources var version; combinedSources = combinedSources.replace(/#version\s+(.*?)\n/gm, function ( match, group1 ) { //>>includeStart('debug', pragmas.debug); if (defined(version) && version !== group1) { throw new DeveloperError( "inconsistent versions found: " + version + " and " + group1 ); } //>>includeEnd('debug'); // Extract #version to put at the top version = group1; // Replace original #version directive with a new line so the line numbers // are not off by one. There can be only one #version directive // and it must appear at the top of the source, only preceded by // whitespace and comments. return "\n"; }); // Extract shader extensions from sources var extensions = []; combinedSources = combinedSources.replace(/#extension.*\n/gm, function ( match ) { // Extract extension to put at the top extensions.push(match); // Replace original #extension directive with a new line so the line numbers // are not off by one. return "\n"; }); // Remove precision qualifier combinedSources = combinedSources.replace( /precision\s(lowp|mediump|highp)\s(float|int);/, "" ); // Replace main() for picked if desired. var pickColorQualifier = shaderSource.pickColorQualifier; if (defined(pickColorQualifier)) { combinedSources = ShaderSource.createPickFragmentShaderSource( combinedSources, pickColorQualifier ); } // combine into single string var result = ""; // #version must be first // defaults to #version 100 if not specified if (defined(version)) { result = "#version " + version + "\n"; } var extensionsLength = extensions.length; for (i = 0; i < extensionsLength; i++) { result += extensions[i]; } if (isFragmentShader) { // If high precision isn't support replace occurrences of highp with mediump // The highp keyword is not always available on older mobile devices // See https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#In_WebGL_1_highp_float_support_is_optional_in_fragment_shaders result += "\ #ifdef GL_FRAGMENT_PRECISION_HIGH\n\ precision highp float;\n\ #else\n\ precision mediump float;\n\ #define highp mediump\n\ #endif\n\n"; } // Prepend #defines for uber-shaders var defines = shaderSource.defines; if (defined(defines)) { for (i = 0, length = defines.length; i < length; ++i) { var define = defines[i]; if (define.length !== 0) { result += "#define " + define + "\n"; } } } // GLSLModernizer inserts its own layout qualifiers // at this position in the source if (context.webgl2) { result += "#define OUTPUT_DECLARATION\n\n"; } // Define a constant for the OES_texture_float_linear extension since WebGL does not. if (context.textureFloatLinear) { result += "#define OES_texture_float_linear\n\n"; } // Define a constant for the OES_texture_float extension since WebGL does not. if (context.floatingPointTexture) { result += "#define OES_texture_float\n\n"; } // append built-ins if (shaderSource.includeBuiltIns) { result += getBuiltinsAndAutomaticUniforms(combinedSources); } // reset line number result += "\n#line 0\n"; // append actual source result += combinedSources; // modernize the source if (context.webgl2) { result = modernizeShader(result, isFragmentShader); } return result; } /** * An object containing various inputs that will be combined to form a final GLSL shader string. * * @param {Object} [options] Object with the following properties: * @param {String[]} [options.sources] An array of strings to combine containing GLSL code for the shader. * @param {String[]} [options.defines] An array of strings containing GLSL identifiers to #define. * @param {String} [options.pickColorQualifier] The GLSL qualifier, uniform or varying, for the input czm_pickColor. When defined, a pick fragment shader is generated. * @param {Boolean} [options.includeBuiltIns=true] If true, referenced built-in functions will be included with the combined shader. Set to false if this shader will become a source in another shader, to avoid duplicating functions. * * @exception {DeveloperError} options.pickColorQualifier must be 'uniform' or 'varying'. * * @example * // 1. Prepend #defines to a shader * var source = new Cesium.ShaderSource({ * defines : ['WHITE'], * sources : ['void main() { \n#ifdef WHITE\n gl_FragColor = vec4(1.0); \n#else\n gl_FragColor = vec4(0.0); \n#endif\n }'] * }); * * // 2. Modify a fragment shader for picking * var source = new Cesium.ShaderSource({ * sources : ['void main() { gl_FragColor = vec4(1.0); }'], * pickColorQualifier : 'uniform' * }); * * @private */ function ShaderSource(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var pickColorQualifier = options.pickColorQualifier; //>>includeStart('debug', pragmas.debug); if ( defined(pickColorQualifier) && pickColorQualifier !== "uniform" && pickColorQualifier !== "varying" ) { throw new DeveloperError( "options.pickColorQualifier must be 'uniform' or 'varying'." ); } //>>includeEnd('debug'); this.defines = defined(options.defines) ? options.defines.slice(0) : []; this.sources = defined(options.sources) ? options.sources.slice(0) : []; this.pickColorQualifier = pickColorQualifier; this.includeBuiltIns = defaultValue(options.includeBuiltIns, true); } ShaderSource.prototype.clone = function () { return new ShaderSource({ sources: this.sources, defines: this.defines, pickColorQualifier: this.pickColorQualifier, includeBuiltIns: this.includeBuiltIns, }); }; ShaderSource.replaceMain = function (source, renamedMain) { renamedMain = "void " + renamedMain + "()"; return source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, renamedMain); }; /** * Create a single string containing the full, combined vertex shader with all dependencies and defines. * * @param {Context} context The current rendering context * * @returns {String} The combined shader string. */ ShaderSource.prototype.createCombinedVertexShader = function (context) { return combineShader(this, false, context); }; /** * Create a single string containing the full, combined fragment shader with all dependencies and defines. * * @param {Context} context The current rendering context * * @returns {String} The combined shader string. */ ShaderSource.prototype.createCombinedFragmentShader = function (context) { return combineShader(this, true, context); }; /** * For ShaderProgram testing * @private */ ShaderSource._czmBuiltinsAndUniforms = {}; // combine automatic uniforms and Cesium built-ins for (var builtinName in CzmBuiltins) { if (CzmBuiltins.hasOwnProperty(builtinName)) { ShaderSource._czmBuiltinsAndUniforms[builtinName] = CzmBuiltins[builtinName]; } } for (var uniformName in AutomaticUniforms) { if (AutomaticUniforms.hasOwnProperty(uniformName)) { var uniform = AutomaticUniforms[uniformName]; if (typeof uniform.getDeclaration === "function") { ShaderSource._czmBuiltinsAndUniforms[ uniformName ] = uniform.getDeclaration(uniformName); } } } ShaderSource.createPickVertexShaderSource = function (vertexShaderSource) { var renamedVS = ShaderSource.replaceMain(vertexShaderSource, "czm_old_main"); var pickMain = "attribute vec4 pickColor; \n" + "varying vec4 czm_pickColor; \n" + "void main() \n" + "{ \n" + " czm_old_main(); \n" + " czm_pickColor = pickColor; \n" + "}"; return renamedVS + "\n" + pickMain; }; ShaderSource.createPickFragmentShaderSource = function ( fragmentShaderSource, pickColorQualifier ) { var renamedFS = ShaderSource.replaceMain( fragmentShaderSource, "czm_old_main" ); var pickMain = pickColorQualifier + " vec4 czm_pickColor; \n" + "void main() \n" + "{ \n" + " czm_old_main(); \n" + " if (gl_FragColor.a == 0.0) { \n" + " discard; \n" + " } \n" + " gl_FragColor = czm_pickColor; \n" + "}"; return renamedFS + "\n" + pickMain; }; ShaderSource.findVarying = function (shaderSource, names) { var sources = shaderSource.sources; var namesLength = names.length; for (var i = 0; i < namesLength; ++i) { var name = names[i]; var sourcesLength = sources.length; for (var j = 0; j < sourcesLength; ++j) { if (sources[j].indexOf(name) !== -1) { return name; } } } return undefined; }; var normalVaryingNames = ["v_normalEC", "v_normal"]; ShaderSource.findNormalVarying = function (shaderSource) { return ShaderSource.findVarying(shaderSource, normalVaryingNames); }; var positionVaryingNames = ["v_positionEC"]; ShaderSource.findPositionVarying = function (shaderSource) { return ShaderSource.findVarying(shaderSource, positionVaryingNames); }; //This file is automatically rebuilt by the Cesium build process. var ShadowVolumeAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute float batchId;\n\ \n\ #ifdef EXTRUDED_GEOMETRY\n\ attribute vec3 extrudeDirection;\n\ \n\ uniform float u_globeMinimumAltitude;\n\ #endif // EXTRUDED_GEOMETRY\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #endif // PER_INSTANCE_COLOR\n\ \n\ #ifdef TEXTURE_COORDINATES\n\ #ifdef SPHERICAL\n\ varying vec4 v_sphericalExtents;\n\ #else // SPHERICAL\n\ varying vec2 v_inversePlaneExtents;\n\ varying vec4 v_westPlane;\n\ varying vec4 v_southPlane;\n\ #endif // SPHERICAL\n\ varying vec3 v_uvMinAndSphericalLongitudeRotation;\n\ varying vec3 v_uMaxAndInverseDistance;\n\ varying vec3 v_vMaxAndInverseDistance;\n\ #endif // TEXTURE_COORDINATES\n\ \n\ void main()\n\ {\n\ vec4 position = czm_computePosition();\n\ \n\ #ifdef EXTRUDED_GEOMETRY\n\ float delta = min(u_globeMinimumAltitude, czm_geometricToleranceOverMeter * length(position.xyz));\n\ delta *= czm_sceneMode == czm_sceneMode3D ? 1.0 : 0.0;\n\ \n\ //extrudeDirection is zero for the top layer\n\ position = position + vec4(extrudeDirection * delta, 0.0);\n\ #endif\n\ \n\ #ifdef TEXTURE_COORDINATES\n\ #ifdef SPHERICAL\n\ v_sphericalExtents = czm_batchTable_sphericalExtents(batchId);\n\ v_uvMinAndSphericalLongitudeRotation.z = czm_batchTable_longitudeRotation(batchId);\n\ #else // SPHERICAL\n\ #ifdef COLUMBUS_VIEW_2D\n\ vec4 planes2D_high = czm_batchTable_planes2D_HIGH(batchId);\n\ vec4 planes2D_low = czm_batchTable_planes2D_LOW(batchId);\n\ \n\ // If the primitive is split across the IDL (planes2D_high.x > planes2D_high.w):\n\ // - If this vertex is on the east side of the IDL (position3DLow.y > 0.0, comparison with position3DHigh may produce artifacts)\n\ // - existing \"east\" is on the wrong side of the world, far away (planes2D_high/low.w)\n\ // - so set \"east\" as beyond the eastmost extent of the projection (idlSplitNewPlaneHiLow)\n\ vec2 idlSplitNewPlaneHiLow = vec2(EAST_MOST_X_HIGH - (WEST_MOST_X_HIGH - planes2D_high.w), EAST_MOST_X_LOW - (WEST_MOST_X_LOW - planes2D_low.w));\n\ bool idlSplit = planes2D_high.x > planes2D_high.w && position3DLow.y > 0.0;\n\ planes2D_high.w = czm_branchFreeTernary(idlSplit, idlSplitNewPlaneHiLow.x, planes2D_high.w);\n\ planes2D_low.w = czm_branchFreeTernary(idlSplit, idlSplitNewPlaneHiLow.y, planes2D_low.w);\n\ \n\ // - else, if this vertex is on the west side of the IDL (position3DLow.y < 0.0)\n\ // - existing \"west\" is on the wrong side of the world, far away (planes2D_high/low.x)\n\ // - so set \"west\" as beyond the westmost extent of the projection (idlSplitNewPlaneHiLow)\n\ idlSplit = planes2D_high.x > planes2D_high.w && position3DLow.y < 0.0;\n\ idlSplitNewPlaneHiLow = vec2(WEST_MOST_X_HIGH - (EAST_MOST_X_HIGH - planes2D_high.x), WEST_MOST_X_LOW - (EAST_MOST_X_LOW - planes2D_low.x));\n\ planes2D_high.x = czm_branchFreeTernary(idlSplit, idlSplitNewPlaneHiLow.x, planes2D_high.x);\n\ planes2D_low.x = czm_branchFreeTernary(idlSplit, idlSplitNewPlaneHiLow.y, planes2D_low.x);\n\ \n\ vec3 southWestCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(vec3(0.0, planes2D_high.xy), vec3(0.0, planes2D_low.xy))).xyz;\n\ vec3 northWestCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(vec3(0.0, planes2D_high.x, planes2D_high.z), vec3(0.0, planes2D_low.x, planes2D_low.z))).xyz;\n\ vec3 southEastCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(vec3(0.0, planes2D_high.w, planes2D_high.y), vec3(0.0, planes2D_low.w, planes2D_low.y))).xyz;\n\ #else // COLUMBUS_VIEW_2D\n\ // 3D case has smaller \"plane extents,\" so planes encoded as a 64 bit position and 2 vec3s for distances/direction\n\ vec3 southWestCorner = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(czm_batchTable_southWest_HIGH(batchId), czm_batchTable_southWest_LOW(batchId))).xyz;\n\ vec3 northWestCorner = czm_normal * czm_batchTable_northward(batchId) + southWestCorner;\n\ vec3 southEastCorner = czm_normal * czm_batchTable_eastward(batchId) + southWestCorner;\n\ #endif // COLUMBUS_VIEW_2D\n\ \n\ vec3 eastWard = southEastCorner - southWestCorner;\n\ float eastExtent = length(eastWard);\n\ eastWard /= eastExtent;\n\ \n\ vec3 northWard = northWestCorner - southWestCorner;\n\ float northExtent = length(northWard);\n\ northWard /= northExtent;\n\ \n\ v_westPlane = vec4(eastWard, -dot(eastWard, southWestCorner));\n\ v_southPlane = vec4(northWard, -dot(northWard, southWestCorner));\n\ v_inversePlaneExtents = vec2(1.0 / eastExtent, 1.0 / northExtent);\n\ #endif // SPHERICAL\n\ vec4 uvMinAndExtents = czm_batchTable_uvMinAndExtents(batchId);\n\ vec4 uMaxVmax = czm_batchTable_uMaxVmax(batchId);\n\ \n\ v_uMaxAndInverseDistance = vec3(uMaxVmax.xy, uvMinAndExtents.z);\n\ v_vMaxAndInverseDistance = vec3(uMaxVmax.zw, uvMinAndExtents.w);\n\ v_uvMinAndSphericalLongitudeRotation.xy = uvMinAndExtents.xy;\n\ #endif // TEXTURE_COORDINATES\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ v_color = czm_batchTable_color(batchId);\n\ #endif\n\ \n\ gl_Position = czm_depthClamp(czm_modelViewProjectionRelativeToEye * position);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var ShadowVolumeFS = "#ifdef GL_EXT_frag_depth\n\ #extension GL_EXT_frag_depth : enable\n\ #endif\n\ \n\ #ifdef VECTOR_TILE\n\ uniform vec4 u_highlightColor;\n\ #endif\n\ \n\ void main(void)\n\ {\n\ #ifdef VECTOR_TILE\n\ gl_FragColor = czm_gammaCorrect(u_highlightColor);\n\ #else\n\ gl_FragColor = vec4(1.0);\n\ #endif\n\ czm_writeDepthClamp();\n\ }\n\ "; /** * Whether a classification affects terrain, 3D Tiles or both. * * @enum {Number} */ var ClassificationType = { /** * Only terrain will be classified. * * @type {Number} * @constant */ TERRAIN: 0, /** * Only 3D Tiles will be classified. * * @type {Number} * @constant */ CESIUM_3D_TILE: 1, /** * Both terrain and 3D Tiles will be classified. * * @type {Number} * @constant */ BOTH: 2, }; /** * @private */ ClassificationType.NUMBER_OF_CLASSIFICATION_TYPES = 3; var ClassificationType$1 = Object.freeze(ClassificationType); /** * Determines the function used to compare two depths for the depth test. * * @enum {Number} */ var DepthFunction = { /** * The depth test never passes. * * @type {Number} * @constant */ NEVER: WebGLConstants$1.NEVER, /** * The depth test passes if the incoming depth is less than the stored depth. * * @type {Number} * @constant */ LESS: WebGLConstants$1.LESS, /** * The depth test passes if the incoming depth is equal to the stored depth. * * @type {Number} * @constant */ EQUAL: WebGLConstants$1.EQUAL, /** * The depth test passes if the incoming depth is less than or equal to the stored depth. * * @type {Number} * @constant */ LESS_OR_EQUAL: WebGLConstants$1.LEQUAL, /** * The depth test passes if the incoming depth is greater than the stored depth. * * @type {Number} * @constant */ GREATER: WebGLConstants$1.GREATER, /** * The depth test passes if the incoming depth is not equal to the stored depth. * * @type {Number} * @constant */ NOT_EQUAL: WebGLConstants$1.NOTEQUAL, /** * The depth test passes if the incoming depth is greater than or equal to the stored depth. * * @type {Number} * @constant */ GREATER_OR_EQUAL: WebGLConstants$1.GEQUAL, /** * The depth test always passes. * * @type {Number} * @constant */ ALWAYS: WebGLConstants$1.ALWAYS, }; var DepthFunction$1 = Object.freeze(DepthFunction); /** * @private */ var BufferUsage = { STREAM_DRAW: WebGLConstants$1.STREAM_DRAW, STATIC_DRAW: WebGLConstants$1.STATIC_DRAW, DYNAMIC_DRAW: WebGLConstants$1.DYNAMIC_DRAW, validate: function (bufferUsage) { return ( bufferUsage === BufferUsage.STREAM_DRAW || bufferUsage === BufferUsage.STATIC_DRAW || bufferUsage === BufferUsage.DYNAMIC_DRAW ); }, }; var BufferUsage$1 = Object.freeze(BufferUsage); /** * @private */ function Buffer$1(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); if (!defined(options.typedArray) && !defined(options.sizeInBytes)) { throw new DeveloperError( "Either options.sizeInBytes or options.typedArray is required." ); } if (defined(options.typedArray) && defined(options.sizeInBytes)) { throw new DeveloperError( "Cannot pass in both options.sizeInBytes and options.typedArray." ); } if (defined(options.typedArray)) { Check.typeOf.object("options.typedArray", options.typedArray); Check.typeOf.number( "options.typedArray.byteLength", options.typedArray.byteLength ); } if (!BufferUsage$1.validate(options.usage)) { throw new DeveloperError("usage is invalid."); } //>>includeEnd('debug'); var gl = options.context._gl; var bufferTarget = options.bufferTarget; var typedArray = options.typedArray; var sizeInBytes = options.sizeInBytes; var usage = options.usage; var hasArray = defined(typedArray); if (hasArray) { sizeInBytes = typedArray.byteLength; } //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThan("sizeInBytes", sizeInBytes, 0); //>>includeEnd('debug'); var buffer = gl.createBuffer(); gl.bindBuffer(bufferTarget, buffer); gl.bufferData(bufferTarget, hasArray ? typedArray : sizeInBytes, usage); gl.bindBuffer(bufferTarget, null); this._gl = gl; this._webgl2 = options.context._webgl2; this._bufferTarget = bufferTarget; this._sizeInBytes = sizeInBytes; this._usage = usage; this._buffer = buffer; this.vertexArrayDestroyable = true; } /** * Creates a vertex buffer, which contains untyped vertex data in GPU-controlled memory. *

* A vertex array defines the actual makeup of a vertex, e.g., positions, normals, texture coordinates, * etc., by interpreting the raw data in one or more vertex buffers. * * @param {Object} options An object containing the following properties: * @param {Context} options.context The context in which to create the buffer * @param {ArrayBufferView} [options.typedArray] A typed array containing the data to copy to the buffer. * @param {Number} [options.sizeInBytes] A Number defining the size of the buffer in bytes. Required if options.typedArray is not given. * @param {BufferUsage} options.usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @returns {VertexBuffer} The vertex buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} Must specify either or , but not both. * @exception {DeveloperError} The buffer size must be greater than zero. * @exception {DeveloperError} Invalid usage. * * * @example * // Example 1. Create a dynamic vertex buffer 16 bytes in size. * var buffer = Buffer.createVertexBuffer({ * context : context, * sizeInBytes : 16, * usage : BufferUsage.DYNAMIC_DRAW * }); * * @example * // Example 2. Create a dynamic vertex buffer from three floating-point values. * // The data copied to the vertex buffer is considered raw bytes until it is * // interpreted as vertices using a vertex array. * var positionBuffer = buffer.createVertexBuffer({ * context : context, * typedArray : new Float32Array([0, 0, 0]), * usage : BufferUsage.STATIC_DRAW * }); * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with ARRAY_BUFFER * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with ARRAY_BUFFER */ Buffer$1.createVertexBuffer = function (options) { //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); return new Buffer$1({ context: options.context, bufferTarget: WebGLConstants$1.ARRAY_BUFFER, typedArray: options.typedArray, sizeInBytes: options.sizeInBytes, usage: options.usage, }); }; /** * Creates an index buffer, which contains typed indices in GPU-controlled memory. *

* An index buffer can be attached to a vertex array to select vertices for rendering. * Context.draw can render using the entire index buffer or a subset * of the index buffer defined by an offset and count. * * @param {Object} options An object containing the following properties: * @param {Context} options.context The context in which to create the buffer * @param {ArrayBufferView} [options.typedArray] A typed array containing the data to copy to the buffer. * @param {Number} [options.sizeInBytes] A Number defining the size of the buffer in bytes. Required if options.typedArray is not given. * @param {BufferUsage} options.usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @param {IndexDatatype} options.indexDatatype The datatype of indices in the buffer. * @returns {IndexBuffer} The index buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} Must specify either or , but not both. * @exception {DeveloperError} IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system. Check context.elementIndexUint. * @exception {DeveloperError} The size in bytes must be greater than zero. * @exception {DeveloperError} Invalid usage. * @exception {DeveloperError} Invalid indexDatatype. * * * @example * // Example 1. Create a stream index buffer of unsigned shorts that is * // 16 bytes in size. * var buffer = Buffer.createIndexBuffer({ * context : context, * sizeInBytes : 16, * usage : BufferUsage.STREAM_DRAW, * indexDatatype : IndexDatatype.UNSIGNED_SHORT * }); * * @example * // Example 2. Create a static index buffer containing three unsigned shorts. * var buffer = Buffer.createIndexBuffer({ * context : context, * typedArray : new Uint16Array([0, 1, 2]), * usage : BufferUsage.STATIC_DRAW, * indexDatatype : IndexDatatype.UNSIGNED_SHORT * }); * * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with ELEMENT_ARRAY_BUFFER * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with ELEMENT_ARRAY_BUFFER */ Buffer$1.createIndexBuffer = function (options) { //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); if (!IndexDatatype$1.validate(options.indexDatatype)) { throw new DeveloperError("Invalid indexDatatype."); } if ( options.indexDatatype === IndexDatatype$1.UNSIGNED_INT && !options.context.elementIndexUint ) { throw new DeveloperError( "IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system. Check context.elementIndexUint." ); } //>>includeEnd('debug'); var context = options.context; var indexDatatype = options.indexDatatype; var bytesPerIndex = IndexDatatype$1.getSizeInBytes(indexDatatype); var buffer = new Buffer$1({ context: context, bufferTarget: WebGLConstants$1.ELEMENT_ARRAY_BUFFER, typedArray: options.typedArray, sizeInBytes: options.sizeInBytes, usage: options.usage, }); var numberOfIndices = buffer.sizeInBytes / bytesPerIndex; Object.defineProperties(buffer, { indexDatatype: { get: function () { return indexDatatype; }, }, bytesPerIndex: { get: function () { return bytesPerIndex; }, }, numberOfIndices: { get: function () { return numberOfIndices; }, }, }); return buffer; }; Object.defineProperties(Buffer$1.prototype, { sizeInBytes: { get: function () { return this._sizeInBytes; }, }, usage: { get: function () { return this._usage; }, }, }); Buffer$1.prototype._getBuffer = function () { return this._buffer; }; Buffer$1.prototype.copyFromArrayView = function (arrayView, offsetInBytes) { offsetInBytes = defaultValue(offsetInBytes, 0); //>>includeStart('debug', pragmas.debug); Check.defined("arrayView", arrayView); Check.typeOf.number.lessThanOrEquals( "offsetInBytes + arrayView.byteLength", offsetInBytes + arrayView.byteLength, this._sizeInBytes ); //>>includeEnd('debug'); var gl = this._gl; var target = this._bufferTarget; gl.bindBuffer(target, this._buffer); gl.bufferSubData(target, offsetInBytes, arrayView); gl.bindBuffer(target, null); }; Buffer$1.prototype.copyFromBuffer = function ( readBuffer, readOffset, writeOffset, sizeInBytes ) { //>>includeStart('debug', pragmas.debug); if (!this._webgl2) { throw new DeveloperError("A WebGL 2 context is required."); } if (!defined(readBuffer)) { throw new DeveloperError("readBuffer must be defined."); } if (!defined(sizeInBytes) || sizeInBytes <= 0) { throw new DeveloperError( "sizeInBytes must be defined and be greater than zero." ); } if ( !defined(readOffset) || readOffset < 0 || readOffset + sizeInBytes > readBuffer._sizeInBytes ) { throw new DeveloperError( "readOffset must be greater than or equal to zero and readOffset + sizeInBytes must be less than of equal to readBuffer.sizeInBytes." ); } if ( !defined(writeOffset) || writeOffset < 0 || writeOffset + sizeInBytes > this._sizeInBytes ) { throw new DeveloperError( "writeOffset must be greater than or equal to zero and writeOffset + sizeInBytes must be less than of equal to this.sizeInBytes." ); } if ( this._buffer === readBuffer._buffer && ((writeOffset >= readOffset && writeOffset < readOffset + sizeInBytes) || (readOffset > writeOffset && readOffset < writeOffset + sizeInBytes)) ) { throw new DeveloperError( "When readBuffer is equal to this, the ranges [readOffset + sizeInBytes) and [writeOffset, writeOffset + sizeInBytes) must not overlap." ); } if ( (this._bufferTarget === WebGLConstants$1.ELEMENT_ARRAY_BUFFER && readBuffer._bufferTarget !== WebGLConstants$1.ELEMENT_ARRAY_BUFFER) || (this._bufferTarget !== WebGLConstants$1.ELEMENT_ARRAY_BUFFER && readBuffer._bufferTarget === WebGLConstants$1.ELEMENT_ARRAY_BUFFER) ) { throw new DeveloperError( "Can not copy an index buffer into another buffer type." ); } //>>includeEnd('debug'); var readTarget = WebGLConstants$1.COPY_READ_BUFFER; var writeTarget = WebGLConstants$1.COPY_WRITE_BUFFER; var gl = this._gl; gl.bindBuffer(writeTarget, this._buffer); gl.bindBuffer(readTarget, readBuffer._buffer); gl.copyBufferSubData( readTarget, writeTarget, readOffset, writeOffset, sizeInBytes ); gl.bindBuffer(writeTarget, null); gl.bindBuffer(readTarget, null); }; Buffer$1.prototype.getBufferData = function ( arrayView, sourceOffset, destinationOffset, length ) { sourceOffset = defaultValue(sourceOffset, 0); destinationOffset = defaultValue(destinationOffset, 0); //>>includeStart('debug', pragmas.debug); if (!this._webgl2) { throw new DeveloperError("A WebGL 2 context is required."); } if (!defined(arrayView)) { throw new DeveloperError("arrayView is required."); } var copyLength; var elementSize; var arrayLength = arrayView.byteLength; if (!defined(length)) { if (defined(arrayLength)) { copyLength = arrayLength - destinationOffset; elementSize = 1; } else { arrayLength = arrayView.length; copyLength = arrayLength - destinationOffset; elementSize = arrayView.BYTES_PER_ELEMENT; } } else { copyLength = length; if (defined(arrayLength)) { elementSize = 1; } else { arrayLength = arrayView.length; elementSize = arrayView.BYTES_PER_ELEMENT; } } if (destinationOffset < 0 || destinationOffset > arrayLength) { throw new DeveloperError( "destinationOffset must be greater than zero and less than the arrayView length." ); } if (destinationOffset + copyLength > arrayLength) { throw new DeveloperError( "destinationOffset + length must be less than or equal to the arrayViewLength." ); } if (sourceOffset < 0 || sourceOffset > this._sizeInBytes) { throw new DeveloperError( "sourceOffset must be greater than zero and less than the buffers size." ); } if (sourceOffset + copyLength * elementSize > this._sizeInBytes) { throw new DeveloperError( "sourceOffset + length must be less than the buffers size." ); } //>>includeEnd('debug'); var gl = this._gl; var target = WebGLConstants$1.COPY_READ_BUFFER; gl.bindBuffer(target, this._buffer); gl.getBufferSubData( target, sourceOffset, arrayView, destinationOffset, length ); gl.bindBuffer(target, null); }; Buffer$1.prototype.isDestroyed = function () { return false; }; Buffer$1.prototype.destroy = function () { this._gl.deleteBuffer(this._buffer); return destroyObject(this); }; function addAttribute(attributes, attribute, index, context) { var hasVertexBuffer = defined(attribute.vertexBuffer); var hasValue = defined(attribute.value); var componentsPerAttribute = attribute.value ? attribute.value.length : attribute.componentsPerAttribute; //>>includeStart('debug', pragmas.debug); if (!hasVertexBuffer && !hasValue) { throw new DeveloperError("attribute must have a vertexBuffer or a value."); } if (hasVertexBuffer && hasValue) { throw new DeveloperError( "attribute cannot have both a vertexBuffer and a value. It must have either a vertexBuffer property defining per-vertex data or a value property defining data for all vertices." ); } if ( componentsPerAttribute !== 1 && componentsPerAttribute !== 2 && componentsPerAttribute !== 3 && componentsPerAttribute !== 4 ) { if (hasValue) { throw new DeveloperError( "attribute.value.length must be in the range [1, 4]." ); } throw new DeveloperError( "attribute.componentsPerAttribute must be in the range [1, 4]." ); } if ( defined(attribute.componentDatatype) && !ComponentDatatype$1.validate(attribute.componentDatatype) ) { throw new DeveloperError( "attribute must have a valid componentDatatype or not specify it." ); } if (defined(attribute.strideInBytes) && attribute.strideInBytes > 255) { // WebGL limit. Not in GL ES. throw new DeveloperError( "attribute must have a strideInBytes less than or equal to 255 or not specify it." ); } if ( defined(attribute.instanceDivisor) && attribute.instanceDivisor > 0 && !context.instancedArrays ) { throw new DeveloperError("instanced arrays is not supported"); } if (defined(attribute.instanceDivisor) && attribute.instanceDivisor < 0) { throw new DeveloperError( "attribute must have an instanceDivisor greater than or equal to zero" ); } if (defined(attribute.instanceDivisor) && hasValue) { throw new DeveloperError( "attribute cannot have have an instanceDivisor if it is not backed by a buffer" ); } if ( defined(attribute.instanceDivisor) && attribute.instanceDivisor > 0 && attribute.index === 0 ) { throw new DeveloperError( "attribute zero cannot have an instanceDivisor greater than 0" ); } //>>includeEnd('debug'); // Shallow copy the attribute; we do not want to copy the vertex buffer. var attr = { index: defaultValue(attribute.index, index), enabled: defaultValue(attribute.enabled, true), vertexBuffer: attribute.vertexBuffer, value: hasValue ? attribute.value.slice(0) : undefined, componentsPerAttribute: componentsPerAttribute, componentDatatype: defaultValue( attribute.componentDatatype, ComponentDatatype$1.FLOAT ), normalize: defaultValue(attribute.normalize, false), offsetInBytes: defaultValue(attribute.offsetInBytes, 0), strideInBytes: defaultValue(attribute.strideInBytes, 0), instanceDivisor: defaultValue(attribute.instanceDivisor, 0), }; if (hasVertexBuffer) { // Common case: vertex buffer for per-vertex data attr.vertexAttrib = function (gl) { var index = this.index; gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer._getBuffer()); gl.vertexAttribPointer( index, this.componentsPerAttribute, this.componentDatatype, this.normalize, this.strideInBytes, this.offsetInBytes ); gl.enableVertexAttribArray(index); if (this.instanceDivisor > 0) { context.glVertexAttribDivisor(index, this.instanceDivisor); context._vertexAttribDivisors[index] = this.instanceDivisor; context._previousDrawInstanced = true; } }; attr.disableVertexAttribArray = function (gl) { gl.disableVertexAttribArray(this.index); if (this.instanceDivisor > 0) { context.glVertexAttribDivisor(index, 0); } }; } else { // Less common case: value array for the same data for each vertex switch (attr.componentsPerAttribute) { case 1: attr.vertexAttrib = function (gl) { gl.vertexAttrib1fv(this.index, this.value); }; break; case 2: attr.vertexAttrib = function (gl) { gl.vertexAttrib2fv(this.index, this.value); }; break; case 3: attr.vertexAttrib = function (gl) { gl.vertexAttrib3fv(this.index, this.value); }; break; case 4: attr.vertexAttrib = function (gl) { gl.vertexAttrib4fv(this.index, this.value); }; break; } attr.disableVertexAttribArray = function (gl) {}; } attributes.push(attr); } function bind(gl, attributes, indexBuffer) { for (var i = 0; i < attributes.length; ++i) { var attribute = attributes[i]; if (attribute.enabled) { attribute.vertexAttrib(gl); } } if (defined(indexBuffer)) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer._getBuffer()); } } /** * Creates a vertex array, which defines the attributes making up a vertex, and contains an optional index buffer * to select vertices for rendering. Attributes are defined using object literals as shown in Example 1 below. * * @param {Object} options Object with the following properties: * @param {Context} options.context The context in which the VertexArray gets created. * @param {Object[]} options.attributes An array of attributes. * @param {IndexBuffer} [options.indexBuffer] An optional index buffer. * * @returns {VertexArray} The vertex array, ready for use with drawing. * * @exception {DeveloperError} Attribute must have a vertexBuffer. * @exception {DeveloperError} Attribute must have a componentsPerAttribute. * @exception {DeveloperError} Attribute must have a valid componentDatatype or not specify it. * @exception {DeveloperError} Attribute must have a strideInBytes less than or equal to 255 or not specify it. * @exception {DeveloperError} Index n is used by more than one attribute. * * * @example * // Example 1. Create a vertex array with vertices made up of three floating point * // values, e.g., a position, from a single vertex buffer. No index buffer is used. * var positionBuffer = Buffer.createVertexBuffer({ * context : context, * sizeInBytes : 12, * usage : BufferUsage.STATIC_DRAW * }); * var attributes = [ * { * index : 0, * enabled : true, * vertexBuffer : positionBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * normalize : false, * offsetInBytes : 0, * strideInBytes : 0 // tightly packed * instanceDivisor : 0 // not instanced * } * ]; * var va = new VertexArray({ * context : context, * attributes : attributes * }); * * @example * // Example 2. Create a vertex array with vertices from two different vertex buffers. * // Each vertex has a three-component position and three-component normal. * var positionBuffer = Buffer.createVertexBuffer({ * context : context, * sizeInBytes : 12, * usage : BufferUsage.STATIC_DRAW * }); * var normalBuffer = Buffer.createVertexBuffer({ * context : context, * sizeInBytes : 12, * usage : BufferUsage.STATIC_DRAW * }); * var attributes = [ * { * index : 0, * vertexBuffer : positionBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT * }, * { * index : 1, * vertexBuffer : normalBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT * } * ]; * var va = new VertexArray({ * context : context, * attributes : attributes * }); * * @example * // Example 3. Creates the same vertex layout as Example 2 using a single * // vertex buffer, instead of two. * var buffer = Buffer.createVertexBuffer({ * context : context, * sizeInBytes : 24, * usage : BufferUsage.STATIC_DRAW * }); * var attributes = [ * { * vertexBuffer : buffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * offsetInBytes : 0, * strideInBytes : 24 * }, * { * vertexBuffer : buffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * normalize : true, * offsetInBytes : 12, * strideInBytes : 24 * } * ]; * var va = new VertexArray({ * context : context, * attributes : attributes * }); * * @see Buffer#createVertexBuffer * @see Buffer#createIndexBuffer * @see Context#draw * * @private */ function VertexArray(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); Check.defined("options.attributes", options.attributes); //>>includeEnd('debug'); var context = options.context; var gl = context._gl; var attributes = options.attributes; var indexBuffer = options.indexBuffer; var i; var vaAttributes = []; var numberOfVertices = 1; // if every attribute is backed by a single value var hasInstancedAttributes = false; var hasConstantAttributes = false; var length = attributes.length; for (i = 0; i < length; ++i) { addAttribute(vaAttributes, attributes[i], i, context); } length = vaAttributes.length; for (i = 0; i < length; ++i) { var attribute = vaAttributes[i]; if (defined(attribute.vertexBuffer) && attribute.instanceDivisor === 0) { // This assumes that each vertex buffer in the vertex array has the same number of vertices. var bytes = attribute.strideInBytes || attribute.componentsPerAttribute * ComponentDatatype$1.getSizeInBytes(attribute.componentDatatype); numberOfVertices = attribute.vertexBuffer.sizeInBytes / bytes; break; } } for (i = 0; i < length; ++i) { if (vaAttributes[i].instanceDivisor > 0) { hasInstancedAttributes = true; } if (defined(vaAttributes[i].value)) { hasConstantAttributes = true; } } //>>includeStart('debug', pragmas.debug); // Verify all attribute names are unique var uniqueIndices = {}; for (i = 0; i < length; ++i) { var index = vaAttributes[i].index; if (uniqueIndices[index]) { throw new DeveloperError( "Index " + index + " is used by more than one attribute." ); } uniqueIndices[index] = true; } //>>includeEnd('debug'); var vao; // Setup VAO if supported if (context.vertexArrayObject) { vao = context.glCreateVertexArray(); context.glBindVertexArray(vao); bind(gl, vaAttributes, indexBuffer); context.glBindVertexArray(null); } this._numberOfVertices = numberOfVertices; this._hasInstancedAttributes = hasInstancedAttributes; this._hasConstantAttributes = hasConstantAttributes; this._context = context; this._gl = gl; this._vao = vao; this._attributes = vaAttributes; this._indexBuffer = indexBuffer; } function computeNumberOfVertices(attribute) { return attribute.values.length / attribute.componentsPerAttribute; } function computeAttributeSizeInBytes(attribute) { return ( ComponentDatatype$1.getSizeInBytes(attribute.componentDatatype) * attribute.componentsPerAttribute ); } function interleaveAttributes(attributes) { var j; var name; var attribute; // Extract attribute names. var names = []; for (name in attributes) { // Attribute needs to have per-vertex values; not a constant value for all vertices. if ( attributes.hasOwnProperty(name) && defined(attributes[name]) && defined(attributes[name].values) ) { names.push(name); if (attributes[name].componentDatatype === ComponentDatatype$1.DOUBLE) { attributes[name].componentDatatype = ComponentDatatype$1.FLOAT; attributes[name].values = ComponentDatatype$1.createTypedArray( ComponentDatatype$1.FLOAT, attributes[name].values ); } } } // Validation. Compute number of vertices. var numberOfVertices; var namesLength = names.length; if (namesLength > 0) { numberOfVertices = computeNumberOfVertices(attributes[names[0]]); for (j = 1; j < namesLength; ++j) { var currentNumberOfVertices = computeNumberOfVertices( attributes[names[j]] ); if (currentNumberOfVertices !== numberOfVertices) { throw new RuntimeError( "Each attribute list must have the same number of vertices. " + "Attribute " + names[j] + " has a different number of vertices " + "(" + currentNumberOfVertices.toString() + ")" + " than attribute " + names[0] + " (" + numberOfVertices.toString() + ")." ); } } } // Sort attributes by the size of their components. From left to right, a vertex stores floats, shorts, and then bytes. names.sort(function (left, right) { return ( ComponentDatatype$1.getSizeInBytes(attributes[right].componentDatatype) - ComponentDatatype$1.getSizeInBytes(attributes[left].componentDatatype) ); }); // Compute sizes and strides. var vertexSizeInBytes = 0; var offsetsInBytes = {}; for (j = 0; j < namesLength; ++j) { name = names[j]; attribute = attributes[name]; offsetsInBytes[name] = vertexSizeInBytes; vertexSizeInBytes += computeAttributeSizeInBytes(attribute); } if (vertexSizeInBytes > 0) { // Pad each vertex to be a multiple of the largest component datatype so each // attribute can be addressed using typed arrays. var maxComponentSizeInBytes = ComponentDatatype$1.getSizeInBytes( attributes[names[0]].componentDatatype ); // Sorted large to small var remainder = vertexSizeInBytes % maxComponentSizeInBytes; if (remainder !== 0) { vertexSizeInBytes += maxComponentSizeInBytes - remainder; } // Total vertex buffer size in bytes, including per-vertex padding. var vertexBufferSizeInBytes = numberOfVertices * vertexSizeInBytes; // Create array for interleaved vertices. Each attribute has a different view (pointer) into the array. var buffer = new ArrayBuffer(vertexBufferSizeInBytes); var views = {}; for (j = 0; j < namesLength; ++j) { name = names[j]; var sizeInBytes = ComponentDatatype$1.getSizeInBytes( attributes[name].componentDatatype ); views[name] = { pointer: ComponentDatatype$1.createTypedArray( attributes[name].componentDatatype, buffer ), index: offsetsInBytes[name] / sizeInBytes, // Offset in ComponentType strideInComponentType: vertexSizeInBytes / sizeInBytes, }; } // Copy attributes into one interleaved array. // PERFORMANCE_IDEA: Can we optimize these loops? for (j = 0; j < numberOfVertices; ++j) { for (var n = 0; n < namesLength; ++n) { name = names[n]; attribute = attributes[name]; var values = attribute.values; var view = views[name]; var pointer = view.pointer; var numberOfComponents = attribute.componentsPerAttribute; for (var k = 0; k < numberOfComponents; ++k) { pointer[view.index + k] = values[j * numberOfComponents + k]; } view.index += view.strideInComponentType; } } return { buffer: buffer, offsetsInBytes: offsetsInBytes, vertexSizeInBytes: vertexSizeInBytes, }; } // No attributes to interleave. return undefined; } /** * Creates a vertex array from a geometry. A geometry contains vertex attributes and optional index data * in system memory, whereas a vertex array contains vertex buffers and an optional index buffer in WebGL * memory for use with rendering. *

* The geometry argument should use the standard layout like the geometry returned by {@link BoxGeometry}. *

* options can have four properties: *
    *
  • geometry: The source geometry containing data used to create the vertex array.
  • *
  • attributeLocations: An object that maps geometry attribute names to vertex shader attribute locations.
  • *
  • bufferUsage: The expected usage pattern of the vertex array's buffers. On some WebGL implementations, this can significantly affect performance. See {@link BufferUsage}. Default: BufferUsage.DYNAMIC_DRAW.
  • *
  • interleave: Determines if all attributes are interleaved in a single vertex buffer or if each attribute is stored in a separate vertex buffer. Default: false.
  • *
*
* If options is not specified or the geometry contains no data, the returned vertex array is empty. * * @param {Object} options An object defining the geometry, attribute indices, buffer usage, and vertex layout used to create the vertex array. * * @exception {RuntimeError} Each attribute list must have the same number of vertices. * @exception {DeveloperError} The geometry must have zero or one index lists. * @exception {DeveloperError} Index n is used by more than one attribute. * * * @example * // Example 1. Creates a vertex array for rendering a box. The default dynamic draw * // usage is used for the created vertex and index buffer. The attributes are not * // interleaved by default. * var geometry = new BoxGeometry(); * var va = VertexArray.fromGeometry({ * context : context, * geometry : geometry, * attributeLocations : GeometryPipeline.createAttributeLocations(geometry), * }); * * @example * // Example 2. Creates a vertex array with interleaved attributes in a * // single vertex buffer. The vertex and index buffer have static draw usage. * var va = VertexArray.fromGeometry({ * context : context, * geometry : geometry, * attributeLocations : GeometryPipeline.createAttributeLocations(geometry), * bufferUsage : BufferUsage.STATIC_DRAW, * interleave : true * }); * * @example * // Example 3. When the caller destroys the vertex array, it also destroys the * // attached vertex buffer(s) and index buffer. * va = va.destroy(); * * @see Buffer#createVertexBuffer * @see Buffer#createIndexBuffer * @see GeometryPipeline.createAttributeLocations * @see ShaderProgram */ VertexArray.fromGeometry = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.context", options.context); //>>includeEnd('debug'); var context = options.context; var geometry = defaultValue(options.geometry, defaultValue.EMPTY_OBJECT); var bufferUsage = defaultValue(options.bufferUsage, BufferUsage$1.DYNAMIC_DRAW); var attributeLocations = defaultValue( options.attributeLocations, defaultValue.EMPTY_OBJECT ); var interleave = defaultValue(options.interleave, false); var createdVAAttributes = options.vertexArrayAttributes; var name; var attribute; var vertexBuffer; var vaAttributes = defined(createdVAAttributes) ? createdVAAttributes : []; var attributes = geometry.attributes; if (interleave) { // Use a single vertex buffer with interleaved vertices. var interleavedAttributes = interleaveAttributes(attributes); if (defined(interleavedAttributes)) { vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: interleavedAttributes.buffer, usage: bufferUsage, }); var offsetsInBytes = interleavedAttributes.offsetsInBytes; var strideInBytes = interleavedAttributes.vertexSizeInBytes; for (name in attributes) { if (attributes.hasOwnProperty(name) && defined(attributes[name])) { attribute = attributes[name]; if (defined(attribute.values)) { // Common case: per-vertex attributes vaAttributes.push({ index: attributeLocations[name], vertexBuffer: vertexBuffer, componentDatatype: attribute.componentDatatype, componentsPerAttribute: attribute.componentsPerAttribute, normalize: attribute.normalize, offsetInBytes: offsetsInBytes[name], strideInBytes: strideInBytes, }); } else { // Constant attribute for all vertices vaAttributes.push({ index: attributeLocations[name], value: attribute.value, componentDatatype: attribute.componentDatatype, normalize: attribute.normalize, }); } } } } } else { // One vertex buffer per attribute. for (name in attributes) { if (attributes.hasOwnProperty(name) && defined(attributes[name])) { attribute = attributes[name]; var componentDatatype = attribute.componentDatatype; if (componentDatatype === ComponentDatatype$1.DOUBLE) { componentDatatype = ComponentDatatype$1.FLOAT; } vertexBuffer = undefined; if (defined(attribute.values)) { vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: ComponentDatatype$1.createTypedArray( componentDatatype, attribute.values ), usage: bufferUsage, }); } vaAttributes.push({ index: attributeLocations[name], vertexBuffer: vertexBuffer, value: attribute.value, componentDatatype: componentDatatype, componentsPerAttribute: attribute.componentsPerAttribute, normalize: attribute.normalize, }); } } } var indexBuffer; var indices = geometry.indices; if (defined(indices)) { if ( Geometry.computeNumberOfVertices(geometry) >= CesiumMath.SIXTY_FOUR_KILOBYTES && context.elementIndexUint ) { indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: new Uint32Array(indices), usage: bufferUsage, indexDatatype: IndexDatatype$1.UNSIGNED_INT, }); } else { indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: new Uint16Array(indices), usage: bufferUsage, indexDatatype: IndexDatatype$1.UNSIGNED_SHORT, }); } } return new VertexArray({ context: context, attributes: vaAttributes, indexBuffer: indexBuffer, }); }; Object.defineProperties(VertexArray.prototype, { numberOfAttributes: { get: function () { return this._attributes.length; }, }, numberOfVertices: { get: function () { return this._numberOfVertices; }, }, indexBuffer: { get: function () { return this._indexBuffer; }, }, }); /** * index is the location in the array of attributes, not the index property of an attribute. */ VertexArray.prototype.getAttribute = function (index) { //>>includeStart('debug', pragmas.debug); Check.defined("index", index); //>>includeEnd('debug'); return this._attributes[index]; }; // Workaround for ANGLE, where the attribute divisor seems to be part of the global state instead // of the VAO state. This function is called when the vao is bound, and should be removed // once the ANGLE issue is resolved. Setting the divisor should normally happen in vertexAttrib and // disableVertexAttribArray. function setVertexAttribDivisor(vertexArray) { var context = vertexArray._context; var hasInstancedAttributes = vertexArray._hasInstancedAttributes; if (!hasInstancedAttributes && !context._previousDrawInstanced) { return; } context._previousDrawInstanced = hasInstancedAttributes; var divisors = context._vertexAttribDivisors; var attributes = vertexArray._attributes; var maxAttributes = ContextLimits.maximumVertexAttributes; var i; if (hasInstancedAttributes) { var length = attributes.length; for (i = 0; i < length; ++i) { var attribute = attributes[i]; if (attribute.enabled) { var divisor = attribute.instanceDivisor; var index = attribute.index; if (divisor !== divisors[index]) { context.glVertexAttribDivisor(index, divisor); divisors[index] = divisor; } } } } else { for (i = 0; i < maxAttributes; ++i) { if (divisors[i] > 0) { context.glVertexAttribDivisor(i, 0); divisors[i] = 0; } } } } // Vertex attributes backed by a constant value go through vertexAttrib[1234]f[v] // which is part of context state rather than VAO state. function setConstantAttributes(vertexArray, gl) { var attributes = vertexArray._attributes; var length = attributes.length; for (var i = 0; i < length; ++i) { var attribute = attributes[i]; if (attribute.enabled && defined(attribute.value)) { attribute.vertexAttrib(gl); } } } VertexArray.prototype._bind = function () { if (defined(this._vao)) { this._context.glBindVertexArray(this._vao); if (this._context.instancedArrays) { setVertexAttribDivisor(this); } if (this._hasConstantAttributes) { setConstantAttributes(this, this._gl); } } else { bind(this._gl, this._attributes, this._indexBuffer); } }; VertexArray.prototype._unBind = function () { if (defined(this._vao)) { this._context.glBindVertexArray(null); } else { var attributes = this._attributes; var gl = this._gl; for (var i = 0; i < attributes.length; ++i) { var attribute = attributes[i]; if (attribute.enabled) { attribute.disableVertexAttribArray(gl); } } if (this._indexBuffer) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); } } }; VertexArray.prototype.isDestroyed = function () { return false; }; VertexArray.prototype.destroy = function () { var attributes = this._attributes; for (var i = 0; i < attributes.length; ++i) { var vertexBuffer = attributes[i].vertexBuffer; if ( defined(vertexBuffer) && !vertexBuffer.isDestroyed() && vertexBuffer.vertexArrayDestroyable ) { vertexBuffer.destroy(); } } var indexBuffer = this._indexBuffer; if ( defined(indexBuffer) && !indexBuffer.isDestroyed() && indexBuffer.vertexArrayDestroyable ) { indexBuffer.destroy(); } if (defined(this._vao)) { this._context.glDeleteVertexArray(this._vao); } return destroyObject(this); }; /** * Creates a texture to look up per instance attributes for batched primitives. For example, store each primitive's pick color in the texture. * * @alias BatchTable * @constructor * @private * * @param {Context} context The context in which the batch table is created. * @param {Object[]} attributes An array of objects describing a per instance attribute. Each object contains a datatype, components per attributes, whether it is normalized and a function name * to retrieve the value in the vertex shader. * @param {Number} numberOfInstances The number of instances in a batch table. * * @example * // create the batch table * var attributes = [{ * functionName : 'getShow', * componentDatatype : ComponentDatatype.UNSIGNED_BYTE, * componentsPerAttribute : 1 * }, { * functionName : 'getPickColor', * componentDatatype : ComponentDatatype.UNSIGNED_BYTE, * componentsPerAttribute : 4, * normalize : true * }]; * var batchTable = new BatchTable(context, attributes, 5); * * // when creating the draw commands, update the uniform map and the vertex shader * vertexShaderSource = batchTable.getVertexShaderCallback()(vertexShaderSource); * var shaderProgram = ShaderProgram.fromCache({ * // ... * vertexShaderSource : vertexShaderSource, * }); * * drawCommand.shaderProgram = shaderProgram; * drawCommand.uniformMap = batchTable.getUniformMapCallback()(uniformMap); * * // use the attribute function names in the shader to retrieve the instance values * // ... * attribute float batchId; * * void main() { * // ... * float show = getShow(batchId); * vec3 pickColor = getPickColor(batchId); * // ... * } */ function BatchTable(context, attributes, numberOfInstances) { //>>includeStart('debug', pragmas.debug); if (!defined(context)) { throw new DeveloperError("context is required"); } if (!defined(attributes)) { throw new DeveloperError("attributes is required"); } if (!defined(numberOfInstances)) { throw new DeveloperError("numberOfInstances is required"); } //>>includeEnd('debug'); this._attributes = attributes; this._numberOfInstances = numberOfInstances; if (attributes.length === 0) { return; } // PERFORMANCE_IDEA: We may be able to arrange the attributes so they can be packing into fewer texels. // Right now, an attribute with one component uses an entire texel when 4 single component attributes can // be packed into a texel. // // Packing floats into unsigned byte textures makes the problem worse. A single component float attribute // will be packed into a single texel leaving 3 texels unused. 4 texels are reserved for each float attribute // regardless of how many components it has. var pixelDatatype = getDatatype(attributes); var textureFloatSupported = context.floatingPointTexture; var packFloats = pixelDatatype === PixelDatatype$1.FLOAT && !textureFloatSupported; var offsets = createOffsets(attributes, packFloats); var stride = getStride(offsets, attributes, packFloats); var maxNumberOfInstancesPerRow = Math.floor( ContextLimits.maximumTextureSize / stride ); var instancesPerWidth = Math.min( numberOfInstances, maxNumberOfInstancesPerRow ); var width = stride * instancesPerWidth; var height = Math.ceil(numberOfInstances / instancesPerWidth); var stepX = 1.0 / width; var centerX = stepX * 0.5; var stepY = 1.0 / height; var centerY = stepY * 0.5; this._textureDimensions = new Cartesian2(width, height); this._textureStep = new Cartesian4(stepX, centerX, stepY, centerY); this._pixelDatatype = !packFloats ? pixelDatatype : PixelDatatype$1.UNSIGNED_BYTE; this._packFloats = packFloats; this._offsets = offsets; this._stride = stride; this._texture = undefined; var batchLength = 4 * width * height; this._batchValues = pixelDatatype === PixelDatatype$1.FLOAT && !packFloats ? new Float32Array(batchLength) : new Uint8Array(batchLength); this._batchValuesDirty = false; } Object.defineProperties(BatchTable.prototype, { /** * The attribute descriptions. * @memberOf BatchTable.prototype * @type {Object[]} * @readonly */ attributes: { get: function () { return this._attributes; }, }, /** * The number of instances. * @memberOf BatchTable.prototype * @type {Number} * @readonly */ numberOfInstances: { get: function () { return this._numberOfInstances; }, }, }); function getDatatype(attributes) { var foundFloatDatatype = false; var length = attributes.length; for (var i = 0; i < length; ++i) { if (attributes[i].componentDatatype !== ComponentDatatype$1.UNSIGNED_BYTE) { foundFloatDatatype = true; break; } } return foundFloatDatatype ? PixelDatatype$1.FLOAT : PixelDatatype$1.UNSIGNED_BYTE; } function getAttributeType(attributes, attributeIndex) { var componentsPerAttribute = attributes[attributeIndex].componentsPerAttribute; if (componentsPerAttribute === 2) { return Cartesian2; } else if (componentsPerAttribute === 3) { return Cartesian3; } else if (componentsPerAttribute === 4) { return Cartesian4; } return Number; } function createOffsets(attributes, packFloats) { var offsets = new Array(attributes.length); var currentOffset = 0; var attributesLength = attributes.length; for (var i = 0; i < attributesLength; ++i) { var attribute = attributes[i]; var componentDatatype = attribute.componentDatatype; offsets[i] = currentOffset; if (componentDatatype !== ComponentDatatype$1.UNSIGNED_BYTE && packFloats) { currentOffset += 4; } else { ++currentOffset; } } return offsets; } function getStride(offsets, attributes, packFloats) { var length = offsets.length; var lastOffset = offsets[length - 1]; var lastAttribute = attributes[length - 1]; var componentDatatype = lastAttribute.componentDatatype; if (componentDatatype !== ComponentDatatype$1.UNSIGNED_BYTE && packFloats) { return lastOffset + 4; } return lastOffset + 1; } var scratchPackedFloatCartesian4 = new Cartesian4(); function getPackedFloat(array, index, result) { var packed = Cartesian4.unpack(array, index, scratchPackedFloatCartesian4); var x = Cartesian4.unpackFloat(packed); packed = Cartesian4.unpack(array, index + 4, scratchPackedFloatCartesian4); var y = Cartesian4.unpackFloat(packed); packed = Cartesian4.unpack(array, index + 8, scratchPackedFloatCartesian4); var z = Cartesian4.unpackFloat(packed); packed = Cartesian4.unpack(array, index + 12, scratchPackedFloatCartesian4); var w = Cartesian4.unpackFloat(packed); return Cartesian4.fromElements(x, y, z, w, result); } function setPackedAttribute(value, array, index) { var packed = Cartesian4.packFloat(value.x, scratchPackedFloatCartesian4); Cartesian4.pack(packed, array, index); packed = Cartesian4.packFloat(value.y, packed); Cartesian4.pack(packed, array, index + 4); packed = Cartesian4.packFloat(value.z, packed); Cartesian4.pack(packed, array, index + 8); packed = Cartesian4.packFloat(value.w, packed); Cartesian4.pack(packed, array, index + 12); } var scratchGetAttributeCartesian4$1 = new Cartesian4(); /** * Gets the value of an attribute in the table. * * @param {Number} instanceIndex The index of the instance. * @param {Number} attributeIndex The index of the attribute. * @param {undefined|Cartesian2|Cartesian3|Cartesian4} [result] The object onto which to store the result. The type is dependent on the attribute's number of components. * @returns {Number|Cartesian2|Cartesian3|Cartesian4} The attribute value stored for the instance. * * @exception {DeveloperError} instanceIndex is out of range. * @exception {DeveloperError} attributeIndex is out of range. */ BatchTable.prototype.getBatchedAttribute = function ( instanceIndex, attributeIndex, result ) { //>>includeStart('debug', pragmas.debug); if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) { throw new DeveloperError("instanceIndex is out of range."); } if (attributeIndex < 0 || attributeIndex >= this._attributes.length) { throw new DeveloperError("attributeIndex is out of range"); } //>>includeEnd('debug'); var attributes = this._attributes; var offset = this._offsets[attributeIndex]; var stride = this._stride; var index = 4 * stride * instanceIndex + 4 * offset; var value; if ( this._packFloats && attributes[attributeIndex].componentDatatype !== PixelDatatype$1.UNSIGNED_BYTE ) { value = getPackedFloat( this._batchValues, index, scratchGetAttributeCartesian4$1 ); } else { value = Cartesian4.unpack( this._batchValues, index, scratchGetAttributeCartesian4$1 ); } var attributeType = getAttributeType(attributes, attributeIndex); if (defined(attributeType.fromCartesian4)) { return attributeType.fromCartesian4(value, result); } else if (defined(attributeType.clone)) { return attributeType.clone(value, result); } return value.x; }; var setAttributeScratchValues = [ undefined, undefined, new Cartesian2(), new Cartesian3(), new Cartesian4(), ]; var setAttributeScratchCartesian4 = new Cartesian4(); /** * Sets the value of an attribute in the table. * * @param {Number} instanceIndex The index of the instance. * @param {Number} attributeIndex The index of the attribute. * @param {Number|Cartesian2|Cartesian3|Cartesian4} value The value to be stored in the table. The type of value will depend on the number of components of the attribute. * * @exception {DeveloperError} instanceIndex is out of range. * @exception {DeveloperError} attributeIndex is out of range. */ BatchTable.prototype.setBatchedAttribute = function ( instanceIndex, attributeIndex, value ) { //>>includeStart('debug', pragmas.debug); if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) { throw new DeveloperError("instanceIndex is out of range."); } if (attributeIndex < 0 || attributeIndex >= this._attributes.length) { throw new DeveloperError("attributeIndex is out of range"); } if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var attributes = this._attributes; var result = setAttributeScratchValues[ attributes[attributeIndex].componentsPerAttribute ]; var currentAttribute = this.getBatchedAttribute( instanceIndex, attributeIndex, result ); var attributeType = getAttributeType(this._attributes, attributeIndex); var entriesEqual = defined(attributeType.equals) ? attributeType.equals(currentAttribute, value) : currentAttribute === value; if (entriesEqual) { return; } var attributeValue = setAttributeScratchCartesian4; attributeValue.x = defined(value.x) ? value.x : value; attributeValue.y = defined(value.y) ? value.y : 0.0; attributeValue.z = defined(value.z) ? value.z : 0.0; attributeValue.w = defined(value.w) ? value.w : 0.0; var offset = this._offsets[attributeIndex]; var stride = this._stride; var index = 4 * stride * instanceIndex + 4 * offset; if ( this._packFloats && attributes[attributeIndex].componentDatatype !== PixelDatatype$1.UNSIGNED_BYTE ) { setPackedAttribute(attributeValue, this._batchValues, index); } else { Cartesian4.pack(attributeValue, this._batchValues, index); } this._batchValuesDirty = true; }; function createTexture$3(batchTable, context) { var dimensions = batchTable._textureDimensions; batchTable._texture = new Texture({ context: context, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: batchTable._pixelDatatype, width: dimensions.x, height: dimensions.y, sampler: Sampler.NEAREST, flipY: false, }); } function updateTexture(batchTable) { var dimensions = batchTable._textureDimensions; batchTable._texture.copyFrom({ width: dimensions.x, height: dimensions.y, arrayBufferView: batchTable._batchValues, }); } /** * Creates/updates the batch table texture. * @param {FrameState} frameState The frame state. * * @exception {RuntimeError} The floating point texture extension is required but not supported. */ BatchTable.prototype.update = function (frameState) { if ( (defined(this._texture) && !this._batchValuesDirty) || this._attributes.length === 0 ) { return; } this._batchValuesDirty = false; if (!defined(this._texture)) { createTexture$3(this, frameState.context); } updateTexture(this); }; /** * Gets a function that will update a uniform map to contain values for looking up values in the batch table. * * @returns {BatchTable.updateUniformMapCallback} A callback for updating uniform maps. */ BatchTable.prototype.getUniformMapCallback = function () { var that = this; return function (uniformMap) { if (that._attributes.length === 0) { return uniformMap; } var batchUniformMap = { batchTexture: function () { return that._texture; }, batchTextureDimensions: function () { return that._textureDimensions; }, batchTextureStep: function () { return that._textureStep; }, }; return combine$2(uniformMap, batchUniformMap); }; }; function getGlslComputeSt$1(batchTable) { var stride = batchTable._stride; // GLSL batchId is zero-based: [0, numberOfInstances - 1] if (batchTable._textureDimensions.y === 1) { return ( "uniform vec4 batchTextureStep; \n" + "vec2 computeSt(float batchId) \n" + "{ \n" + " float stepX = batchTextureStep.x; \n" + " float centerX = batchTextureStep.y; \n" + " float numberOfAttributes = float(" + stride + "); \n" + " return vec2(centerX + (batchId * numberOfAttributes * stepX), 0.5); \n" + "} \n" ); } return ( "uniform vec4 batchTextureStep; \n" + "uniform vec2 batchTextureDimensions; \n" + "vec2 computeSt(float batchId) \n" + "{ \n" + " float stepX = batchTextureStep.x; \n" + " float centerX = batchTextureStep.y; \n" + " float stepY = batchTextureStep.z; \n" + " float centerY = batchTextureStep.w; \n" + " float numberOfAttributes = float(" + stride + "); \n" + " float xId = mod(batchId * numberOfAttributes, batchTextureDimensions.x); \n" + " float yId = floor(batchId * numberOfAttributes / batchTextureDimensions.x); \n" + " return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n" + "} \n" ); } function getComponentType(componentsPerAttribute) { if (componentsPerAttribute === 1) { return "float"; } return "vec" + componentsPerAttribute; } function getComponentSwizzle(componentsPerAttribute) { if (componentsPerAttribute === 1) { return ".x"; } else if (componentsPerAttribute === 2) { return ".xy"; } else if (componentsPerAttribute === 3) { return ".xyz"; } return ""; } function getGlslAttributeFunction(batchTable, attributeIndex) { var attributes = batchTable._attributes; var attribute = attributes[attributeIndex]; var componentsPerAttribute = attribute.componentsPerAttribute; var functionName = attribute.functionName; var functionReturnType = getComponentType(componentsPerAttribute); var functionReturnValue = getComponentSwizzle(componentsPerAttribute); var offset = batchTable._offsets[attributeIndex]; var glslFunction = functionReturnType + " " + functionName + "(float batchId) \n" + "{ \n" + " vec2 st = computeSt(batchId); \n" + " st.x += batchTextureStep.x * float(" + offset + "); \n"; if ( batchTable._packFloats && attribute.componentDatatype !== PixelDatatype$1.UNSIGNED_BYTE ) { glslFunction += "vec4 textureValue; \n" + "textureValue.x = czm_unpackFloat(texture2D(batchTexture, st)); \n" + "textureValue.y = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x, 0.0))); \n" + "textureValue.z = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 2.0, 0.0))); \n" + "textureValue.w = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 3.0, 0.0))); \n"; } else { glslFunction += " vec4 textureValue = texture2D(batchTexture, st); \n"; } glslFunction += " " + functionReturnType + " value = textureValue" + functionReturnValue + "; \n"; if ( batchTable._pixelDatatype === PixelDatatype$1.UNSIGNED_BYTE && attribute.componentDatatype === ComponentDatatype$1.UNSIGNED_BYTE && !attribute.normalize ) { glslFunction += "value *= 255.0; \n"; } else if ( batchTable._pixelDatatype === PixelDatatype$1.FLOAT && attribute.componentDatatype === ComponentDatatype$1.UNSIGNED_BYTE && attribute.normalize ) { glslFunction += "value /= 255.0; \n"; } glslFunction += " return value; \n" + "} \n"; return glslFunction; } /** * Gets a function that will update a vertex shader to contain functions for looking up values in the batch table. * * @returns {BatchTable.updateVertexShaderSourceCallback} A callback for updating a vertex shader source. */ BatchTable.prototype.getVertexShaderCallback = function () { var attributes = this._attributes; if (attributes.length === 0) { return function (source) { return source; }; } var batchTableShader = "uniform highp sampler2D batchTexture; \n"; batchTableShader += getGlslComputeSt$1(this) + "\n"; var length = attributes.length; for (var i = 0; i < length; ++i) { batchTableShader += getGlslAttributeFunction(this, i); } return function (source) { var mainIndex = source.indexOf("void main"); var beforeMain = source.substring(0, mainIndex); var afterMain = source.substring(mainIndex); return beforeMain + "\n" + batchTableShader + "\n" + afterMain; }; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see BatchTable#destroy */ BatchTable.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see BatchTable#isDestroyed */ BatchTable.prototype.destroy = function () { this._texture = this._texture && this._texture.destroy(); return destroyObject(this); }; function transformToWorldCoordinates( instances, primitiveModelMatrix, scene3DOnly ) { var toWorld = !scene3DOnly; 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) { if (defined(instances[i].geometry)) { GeometryPipeline.transformToWorldCoordinates(instances[i]); } } } else { // Leave geometry in local coordinate system; auto update model-matrix. Matrix4.multiplyTransformation( primitiveModelMatrix, instances[0].modelMatrix, primitiveModelMatrix ); } } function addGeometryBatchId(geometry, batchId) { var attributes = geometry.attributes; var positionAttr = attributes.position; var numberOfComponents = positionAttr.values.length / positionAttr.componentsPerAttribute; attributes.batchId = new GeometryAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 1, values: new Float32Array(numberOfComponents), }); var values = attributes.batchId.values; for (var j = 0; j < numberOfComponents; ++j) { values[j] = batchId; } } function addBatchIds(instances) { var length = instances.length; for (var i = 0; i < length; ++i) { var instance = instances[i]; if (defined(instance.geometry)) { addGeometryBatchId(instance.geometry, i); } else if ( defined(instance.westHemisphereGeometry) && defined(instance.eastHemisphereGeometry) ) { addGeometryBatchId(instance.westHemisphereGeometry, i); addGeometryBatchId(instance.eastHemisphereGeometry, i); } } } function geometryPipeline(parameters) { var instances = parameters.instances; var projection = parameters.projection; var uintIndexSupport = parameters.elementIndexUintSupported; var scene3DOnly = parameters.scene3DOnly; var vertexCacheOptimize = parameters.vertexCacheOptimize; var compressVertices = parameters.compressVertices; var modelMatrix = parameters.modelMatrix; var i; var geometry; var primitiveType; var length = instances.length; for (i = 0; i < length; ++i) { if (defined(instances[i].geometry)) { primitiveType = instances[i].geometry.primitiveType; break; } } //>>includeStart('debug', pragmas.debug); for (i = 1; i < length; ++i) { if ( defined(instances[i].geometry) && instances[i].geometry.primitiveType !== primitiveType ) { throw new DeveloperError( "All instance geometries must have the same primitiveType." ); } } //>>includeEnd('debug'); // Unify to world coordinates before combining. transformToWorldCoordinates(instances, modelMatrix, scene3DOnly); // Clip to IDL if (!scene3DOnly) { for (i = 0; i < length; ++i) { if (defined(instances[i].geometry)) { GeometryPipeline.splitLongitude(instances[i]); } } } addBatchIds(instances); // Optimize for vertex shader caches if (vertexCacheOptimize) { for (i = 0; i < length; ++i) { var instance = instances[i]; if (defined(instance.geometry)) { GeometryPipeline.reorderForPostVertexCache(instance.geometry); GeometryPipeline.reorderForPreVertexCache(instance.geometry); } else if ( defined(instance.westHemisphereGeometry) && defined(instance.eastHemisphereGeometry) ) { GeometryPipeline.reorderForPostVertexCache( instance.westHemisphereGeometry ); GeometryPipeline.reorderForPreVertexCache( instance.westHemisphereGeometry ); GeometryPipeline.reorderForPostVertexCache( instance.eastHemisphereGeometry ); GeometryPipeline.reorderForPreVertexCache( instance.eastHemisphereGeometry ); } } } // Combine into single geometry for better rendering performance. var geometries = GeometryPipeline.combineInstances(instances); length = geometries.length; for (i = 0; i < length; ++i) { geometry = geometries[i]; // Split positions for GPU RTE var attributes = geometry.attributes; var name; if (!scene3DOnly) { for (name in attributes) { if ( attributes.hasOwnProperty(name) && attributes[name].componentDatatype === ComponentDatatype$1.DOUBLE ) { var name3D = name + "3D"; var name2D = name + "2D"; // Compute 2D positions GeometryPipeline.projectTo2D( geometry, name, name3D, name2D, projection ); if (defined(geometry.boundingSphere) && name === "position") { geometry.boundingSphereCV = BoundingSphere.fromVertices( geometry.attributes.position2D.values ); } 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 === ComponentDatatype$1.DOUBLE ) { GeometryPipeline.encodeAttribute( geometry, name, name + "3DHigh", name + "3DLow" ); } } } // oct encode and pack normals, compress texture coordinates if (compressVertices) { GeometryPipeline.compressVertices(geometry); } } if (!uintIndexSupport) { // Break into multiple geometries to fit within unsigned short indices if needed var splitGeometries = []; length = geometries.length; for (i = 0; i < length; ++i) { geometry = geometries[i]; splitGeometries = splitGeometries.concat( GeometryPipeline.fitToUnsignedShortIndices(geometry) ); } geometries = splitGeometries; } return geometries; } function createPickOffsets(instances, geometryName, geometries, pickOffsets) { var offset; var indexCount; var geometryIndex; var offsetIndex = pickOffsets.length - 1; if (offsetIndex >= 0) { var pickOffset = pickOffsets[offsetIndex]; offset = pickOffset.offset + pickOffset.count; geometryIndex = pickOffset.index; indexCount = geometries[geometryIndex].indices.length; } else { offset = 0; geometryIndex = 0; indexCount = geometries[geometryIndex].indices.length; } var length = instances.length; for (var i = 0; i < length; ++i) { var instance = instances[i]; var geometry = instance[geometryName]; if (!defined(geometry)) { continue; } var count = geometry.indices.length; if (offset + count > indexCount) { offset = 0; indexCount = geometries[++geometryIndex].indices.length; } pickOffsets.push({ index: geometryIndex, offset: offset, count: count, }); offset += count; } } function createInstancePickOffsets(instances, geometries) { var pickOffsets = []; createPickOffsets(instances, "geometry", geometries, pickOffsets); createPickOffsets( instances, "westHemisphereGeometry", geometries, pickOffsets ); createPickOffsets( instances, "eastHemisphereGeometry", geometries, pickOffsets ); return pickOffsets; } /** * @private */ var PrimitivePipeline = {}; /** * @private */ PrimitivePipeline.combineGeometry = function (parameters) { var geometries; var attributeLocations; var instances = parameters.instances; var length = instances.length; var pickOffsets; var offsetInstanceExtend; var hasOffset = false; if (length > 0) { geometries = geometryPipeline(parameters); if (geometries.length > 0) { attributeLocations = GeometryPipeline.createAttributeLocations( geometries[0] ); if (parameters.createPickOffsets) { pickOffsets = createInstancePickOffsets(instances, geometries); } } if ( defined(instances[0].attributes) && defined(instances[0].attributes.offset) ) { offsetInstanceExtend = new Array(length); hasOffset = true; } } var boundingSpheres = new Array(length); var boundingSpheresCV = new Array(length); for (var i = 0; i < length; ++i) { var instance = instances[i]; var geometry = instance.geometry; if (defined(geometry)) { boundingSpheres[i] = geometry.boundingSphere; boundingSpheresCV[i] = geometry.boundingSphereCV; if (hasOffset) { offsetInstanceExtend[i] = instance.geometry.offsetAttribute; } } var eastHemisphereGeometry = instance.eastHemisphereGeometry; var westHemisphereGeometry = instance.westHemisphereGeometry; if (defined(eastHemisphereGeometry) && defined(westHemisphereGeometry)) { if ( defined(eastHemisphereGeometry.boundingSphere) && defined(westHemisphereGeometry.boundingSphere) ) { boundingSpheres[i] = BoundingSphere.union( eastHemisphereGeometry.boundingSphere, westHemisphereGeometry.boundingSphere ); } if ( defined(eastHemisphereGeometry.boundingSphereCV) && defined(westHemisphereGeometry.boundingSphereCV) ) { boundingSpheresCV[i] = BoundingSphere.union( eastHemisphereGeometry.boundingSphereCV, westHemisphereGeometry.boundingSphereCV ); } } } return { geometries: geometries, modelMatrix: parameters.modelMatrix, attributeLocations: attributeLocations, pickOffsets: pickOffsets, offsetInstanceExtend: offsetInstanceExtend, boundingSpheres: boundingSpheres, boundingSpheresCV: boundingSpheresCV, }; }; function transferGeometry(geometry, transferableObjects) { var attributes = geometry.attributes; for (var name in attributes) { if (attributes.hasOwnProperty(name)) { var attribute = attributes[name]; if (defined(attribute) && defined(attribute.values)) { transferableObjects.push(attribute.values.buffer); } } } if (defined(geometry.indices)) { transferableObjects.push(geometry.indices.buffer); } } function transferGeometries(geometries, transferableObjects) { var length = geometries.length; for (var i = 0; i < length; ++i) { transferGeometry(geometries[i], transferableObjects); } } // This function was created by simplifying packCreateGeometryResults into a count-only operation. function countCreateGeometryResults(items) { var count = 1; var length = items.length; for (var i = 0; i < length; i++) { var geometry = items[i]; ++count; if (!defined(geometry)) { continue; } var attributes = geometry.attributes; count += 7 + 2 * BoundingSphere.packedLength + (defined(geometry.indices) ? geometry.indices.length : 0); for (var property in attributes) { if ( attributes.hasOwnProperty(property) && defined(attributes[property]) ) { var attribute = attributes[property]; count += 5 + attribute.values.length; } } } return count; } /** * @private */ PrimitivePipeline.packCreateGeometryResults = function ( items, transferableObjects ) { var packedData = new Float64Array(countCreateGeometryResults(items)); var stringTable = []; var stringHash = {}; var length = items.length; var count = 0; packedData[count++] = length; for (var i = 0; i < length; i++) { var geometry = items[i]; var validGeometry = defined(geometry); packedData[count++] = validGeometry ? 1.0 : 0.0; if (!validGeometry) { continue; } packedData[count++] = geometry.primitiveType; packedData[count++] = geometry.geometryType; packedData[count++] = defaultValue(geometry.offsetAttribute, -1); var validBoundingSphere = defined(geometry.boundingSphere) ? 1.0 : 0.0; packedData[count++] = validBoundingSphere; if (validBoundingSphere) { BoundingSphere.pack(geometry.boundingSphere, packedData, count); } count += BoundingSphere.packedLength; var validBoundingSphereCV = defined(geometry.boundingSphereCV) ? 1.0 : 0.0; packedData[count++] = validBoundingSphereCV; if (validBoundingSphereCV) { BoundingSphere.pack(geometry.boundingSphereCV, packedData, count); } count += BoundingSphere.packedLength; var attributes = geometry.attributes; var attributesToWrite = []; for (var property in attributes) { if ( attributes.hasOwnProperty(property) && defined(attributes[property]) ) { attributesToWrite.push(property); if (!defined(stringHash[property])) { stringHash[property] = stringTable.length; stringTable.push(property); } } } packedData[count++] = attributesToWrite.length; for (var q = 0; q < attributesToWrite.length; q++) { var name = attributesToWrite[q]; var attribute = attributes[name]; packedData[count++] = stringHash[name]; packedData[count++] = attribute.componentDatatype; packedData[count++] = attribute.componentsPerAttribute; packedData[count++] = attribute.normalize ? 1 : 0; packedData[count++] = attribute.values.length; packedData.set(attribute.values, count); count += attribute.values.length; } var indicesLength = defined(geometry.indices) ? geometry.indices.length : 0; packedData[count++] = indicesLength; if (indicesLength > 0) { packedData.set(geometry.indices, count); count += indicesLength; } } transferableObjects.push(packedData.buffer); return { stringTable: stringTable, packedData: packedData, }; }; /** * @private */ PrimitivePipeline.unpackCreateGeometryResults = function ( createGeometryResult ) { var stringTable = createGeometryResult.stringTable; var packedGeometry = createGeometryResult.packedData; var i; var result = new Array(packedGeometry[0]); var resultIndex = 0; var packedGeometryIndex = 1; while (packedGeometryIndex < packedGeometry.length) { var valid = packedGeometry[packedGeometryIndex++] === 1.0; if (!valid) { result[resultIndex++] = undefined; continue; } var primitiveType = packedGeometry[packedGeometryIndex++]; var geometryType = packedGeometry[packedGeometryIndex++]; var offsetAttribute = packedGeometry[packedGeometryIndex++]; if (offsetAttribute === -1) { offsetAttribute = undefined; } var boundingSphere; var boundingSphereCV; var validBoundingSphere = packedGeometry[packedGeometryIndex++] === 1.0; if (validBoundingSphere) { boundingSphere = BoundingSphere.unpack( packedGeometry, packedGeometryIndex ); } packedGeometryIndex += BoundingSphere.packedLength; var validBoundingSphereCV = packedGeometry[packedGeometryIndex++] === 1.0; if (validBoundingSphereCV) { boundingSphereCV = BoundingSphere.unpack( packedGeometry, packedGeometryIndex ); } packedGeometryIndex += BoundingSphere.packedLength; var length; var values; var componentsPerAttribute; var attributes = new GeometryAttributes(); var numAttributes = packedGeometry[packedGeometryIndex++]; for (i = 0; i < numAttributes; i++) { var name = stringTable[packedGeometry[packedGeometryIndex++]]; var componentDatatype = packedGeometry[packedGeometryIndex++]; componentsPerAttribute = packedGeometry[packedGeometryIndex++]; var normalize = packedGeometry[packedGeometryIndex++] !== 0; length = packedGeometry[packedGeometryIndex++]; values = ComponentDatatype$1.createTypedArray(componentDatatype, length); for (var valuesIndex = 0; valuesIndex < length; valuesIndex++) { values[valuesIndex] = packedGeometry[packedGeometryIndex++]; } attributes[name] = new GeometryAttribute({ componentDatatype: componentDatatype, componentsPerAttribute: componentsPerAttribute, normalize: normalize, values: values, }); } var indices; length = packedGeometry[packedGeometryIndex++]; if (length > 0) { var numberOfVertices = values.length / componentsPerAttribute; indices = IndexDatatype$1.createTypedArray(numberOfVertices, length); for (i = 0; i < length; i++) { indices[i] = packedGeometry[packedGeometryIndex++]; } } result[resultIndex++] = new Geometry({ primitiveType: primitiveType, geometryType: geometryType, boundingSphere: boundingSphere, boundingSphereCV: boundingSphereCV, indices: indices, attributes: attributes, offsetAttribute: offsetAttribute, }); } return result; }; function packInstancesForCombine(instances, transferableObjects) { var length = instances.length; var packedData = new Float64Array(1 + length * 19); var count = 0; packedData[count++] = length; for (var i = 0; i < length; i++) { var instance = instances[i]; Matrix4.pack(instance.modelMatrix, packedData, count); count += Matrix4.packedLength; if (defined(instance.attributes) && defined(instance.attributes.offset)) { var values = instance.attributes.offset.value; packedData[count] = values[0]; packedData[count + 1] = values[1]; packedData[count + 2] = values[2]; } count += 3; } transferableObjects.push(packedData.buffer); return packedData; } function unpackInstancesForCombine(data) { var packedInstances = data; var result = new Array(packedInstances[0]); var count = 0; var i = 1; while (i < packedInstances.length) { var modelMatrix = Matrix4.unpack(packedInstances, i); var attributes; i += Matrix4.packedLength; if (defined(packedInstances[i])) { attributes = { offset: new OffsetGeometryInstanceAttribute( packedInstances[i], packedInstances[i + 1], packedInstances[i + 2] ), }; } i += 3; result[count++] = { modelMatrix: modelMatrix, attributes: attributes, }; } return result; } /** * @private */ PrimitivePipeline.packCombineGeometryParameters = function ( parameters, transferableObjects ) { var createGeometryResults = parameters.createGeometryResults; var length = createGeometryResults.length; for (var i = 0; i < length; i++) { transferableObjects.push(createGeometryResults[i].packedData.buffer); } return { createGeometryResults: parameters.createGeometryResults, packedInstances: packInstancesForCombine( parameters.instances, transferableObjects ), ellipsoid: parameters.ellipsoid, isGeographic: parameters.projection instanceof GeographicProjection, elementIndexUintSupported: parameters.elementIndexUintSupported, scene3DOnly: parameters.scene3DOnly, vertexCacheOptimize: parameters.vertexCacheOptimize, compressVertices: parameters.compressVertices, modelMatrix: parameters.modelMatrix, createPickOffsets: parameters.createPickOffsets, }; }; /** * @private */ PrimitivePipeline.unpackCombineGeometryParameters = function ( packedParameters ) { var instances = unpackInstancesForCombine(packedParameters.packedInstances); var createGeometryResults = packedParameters.createGeometryResults; var length = createGeometryResults.length; var instanceIndex = 0; for (var resultIndex = 0; resultIndex < length; resultIndex++) { var geometries = PrimitivePipeline.unpackCreateGeometryResults( createGeometryResults[resultIndex] ); var geometriesLength = geometries.length; for ( var geometryIndex = 0; geometryIndex < geometriesLength; geometryIndex++ ) { var geometry = geometries[geometryIndex]; var instance = instances[instanceIndex]; instance.geometry = geometry; ++instanceIndex; } } var ellipsoid = Ellipsoid.clone(packedParameters.ellipsoid); var projection = packedParameters.isGeographic ? new GeographicProjection(ellipsoid) : new WebMercatorProjection(ellipsoid); return { instances: instances, ellipsoid: ellipsoid, projection: projection, elementIndexUintSupported: packedParameters.elementIndexUintSupported, scene3DOnly: packedParameters.scene3DOnly, vertexCacheOptimize: packedParameters.vertexCacheOptimize, compressVertices: packedParameters.compressVertices, modelMatrix: Matrix4.clone(packedParameters.modelMatrix), createPickOffsets: packedParameters.createPickOffsets, }; }; function packBoundingSpheres(boundingSpheres) { var length = boundingSpheres.length; var bufferLength = 1 + (BoundingSphere.packedLength + 1) * length; var buffer = new Float32Array(bufferLength); var bufferIndex = 0; buffer[bufferIndex++] = length; for (var i = 0; i < length; ++i) { var bs = boundingSpheres[i]; if (!defined(bs)) { buffer[bufferIndex++] = 0.0; } else { buffer[bufferIndex++] = 1.0; BoundingSphere.pack(boundingSpheres[i], buffer, bufferIndex); } bufferIndex += BoundingSphere.packedLength; } return buffer; } function unpackBoundingSpheres(buffer) { var result = new Array(buffer[0]); var count = 0; var i = 1; while (i < buffer.length) { if (buffer[i++] === 1.0) { result[count] = BoundingSphere.unpack(buffer, i); } ++count; i += BoundingSphere.packedLength; } return result; } /** * @private */ PrimitivePipeline.packCombineGeometryResults = function ( results, transferableObjects ) { if (defined(results.geometries)) { transferGeometries(results.geometries, transferableObjects); } var packedBoundingSpheres = packBoundingSpheres(results.boundingSpheres); var packedBoundingSpheresCV = packBoundingSpheres(results.boundingSpheresCV); transferableObjects.push( packedBoundingSpheres.buffer, packedBoundingSpheresCV.buffer ); return { geometries: results.geometries, attributeLocations: results.attributeLocations, modelMatrix: results.modelMatrix, pickOffsets: results.pickOffsets, offsetInstanceExtend: results.offsetInstanceExtend, boundingSpheres: packedBoundingSpheres, boundingSpheresCV: packedBoundingSpheresCV, }; }; /** * @private */ PrimitivePipeline.unpackCombineGeometryResults = function (packedResult) { return { geometries: packedResult.geometries, attributeLocations: packedResult.attributeLocations, modelMatrix: packedResult.modelMatrix, pickOffsets: packedResult.pickOffsets, offsetInstanceExtend: packedResult.offsetInstanceExtend, boundingSpheres: unpackBoundingSpheres(packedResult.boundingSpheres), boundingSpheresCV: unpackBoundingSpheres(packedResult.boundingSpheresCV), }; }; /** * @private */ var PrimitiveState = { READY: 0, CREATING: 1, CREATED: 2, COMBINING: 3, COMBINED: 4, COMPLETE: 5, FAILED: 6, }; var PrimitiveState$1 = Object.freeze(PrimitiveState); /** * Indicates if the scene is viewed in 3D, 2D, or 2.5D Columbus view. * * @enum {Number} * @see Scene#mode */ var SceneMode = { /** * Morphing between mode, e.g., 3D to 2D. * * @type {Number} * @constant */ MORPHING: 0, /** * Columbus View mode. A 2.5D perspective view where the map is laid out * flat and objects with non-zero height are drawn above it. * * @type {Number} * @constant */ COLUMBUS_VIEW: 1, /** * 2D mode. The map is viewed top-down with an orthographic projection. * * @type {Number} * @constant */ SCENE2D: 2, /** * 3D mode. A traditional 3D perspective view of the globe. * * @type {Number} * @constant */ SCENE3D: 3, }; /** * Returns the morph time for the given scene mode. * * @param {SceneMode} value The scene mode * @returns {Number} The morph time */ SceneMode.getMorphTime = function (value) { if (value === SceneMode.SCENE3D) { return 1.0; } else if (value === SceneMode.MORPHING) { return undefined; } return 0.0; }; var SceneMode$1 = Object.freeze(SceneMode); /** * Specifies whether the object casts or receives shadows from light sources when * shadows are enabled. * * @enum {Number} */ var ShadowMode = { /** * The object does not cast or receive shadows. * * @type {Number} * @constant */ DISABLED: 0, /** * The object casts and receives shadows. * * @type {Number} * @constant */ ENABLED: 1, /** * The object casts shadows only. * * @type {Number} * @constant */ CAST_ONLY: 2, /** * The object receives shadows only. * * @type {Number} * @constant */ RECEIVE_ONLY: 3, }; /** * @private */ ShadowMode.NUMBER_OF_SHADOW_MODES = 4; /** * @private */ ShadowMode.castShadows = function (shadowMode) { return ( shadowMode === ShadowMode.ENABLED || shadowMode === ShadowMode.CAST_ONLY ); }; /** * @private */ ShadowMode.receiveShadows = function (shadowMode) { return ( shadowMode === ShadowMode.ENABLED || shadowMode === ShadowMode.RECEIVE_ONLY ); }; /** * @private */ ShadowMode.fromCastReceive = function (castShadows, receiveShadows) { if (castShadows && receiveShadows) { return ShadowMode.ENABLED; } else if (castShadows) { return ShadowMode.CAST_ONLY; } else if (receiveShadows) { return ShadowMode.RECEIVE_ONLY; } return ShadowMode.DISABLED; }; var ShadowMode$1 = Object.freeze(ShadowMode); /** * A primitive represents geometry in the {@link Scene}. The geometry can be from a single {@link GeometryInstance} * as shown in example 1 below, or from an array of instances, even if the geometry is from different * geometry types, e.g., an {@link RectangleGeometry} and an {@link EllipsoidGeometry} as shown in Code Example 2. *

* A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement, * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix * and match most of them and add a new geometry or appearance independently of each other. *

*

* Combining multiple instances into one primitive is called batching, and significantly improves performance for static data. * Instances can be individually picked; {@link Scene#pick} returns their {@link GeometryInstance#id}. Using * per-instance appearances like {@link PerInstanceColorAppearance}, each instance can also have a unique color. *

*

* {@link Geometry} can either be created and batched on a web worker or the main thread. The first two examples * show geometry that will be created on a web worker by using the descriptions of the geometry. The third example * shows how to create the geometry on the main thread by explicitly calling the createGeometry method. *

* * @alias Primitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {GeometryInstance[]|GeometryInstance} [options.geometryInstances] The geometry instances - or a single geometry instance - to render. * @param {Appearance} [options.appearance] The appearance used to render the primitive. * @param {Appearance} [options.depthFailAppearance] The appearance used to shade this primitive when it fails the depth test. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates. * @param {Boolean} [options.vertexCacheOptimize=false] When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * @param {Boolean} [options.interleave=false] When true, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.compressVertices=true] When true, the geometry vertices are compressed, which will save memory. * @param {Boolean} [options.releaseGeometryInstances=true] When true, the primitive does not keep a reference to the input geometryInstances to save memory. * @param {Boolean} [options.allowPicking=true] When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * @param {Boolean} [options.cull=true] When true, the renderer frustum culls and horizon culls the primitive's commands based on their bounding volume. Set this to false for a small performance gain if you are manually culling the primitive. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {ShadowMode} [options.shadows=ShadowMode.DISABLED] Determines whether this primitive casts or receives shadows from light sources. * * @example * // 1. Draw a translucent ellipse on the surface with a checkerboard pattern * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.EllipseGeometry({ * center : Cesium.Cartesian3.fromDegrees(-100.0, 20.0), * semiMinorAxis : 500000.0, * semiMajorAxis : 1000000.0, * rotation : Cesium.Math.PI_OVER_FOUR, * vertexFormat : Cesium.VertexFormat.POSITION_AND_ST * }), * id : 'object returned when this instance is picked and to get/set per-instance attributes' * }); * scene.primitives.add(new Cesium.Primitive({ * geometryInstances : instance, * appearance : new Cesium.EllipsoidSurfaceAppearance({ * material : Cesium.Material.fromType('Checkerboard') * }) * })); * * @example * // 2. Draw different instances each with a unique color * var rectangleInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.RectangleGeometry({ * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0), * vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT * }), * id : 'rectangle', * attributes : { * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5) * } * }); * var ellipsoidInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.EllipsoidGeometry({ * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0), * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL * }), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()), * id : 'ellipsoid', * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA) * } * }); * scene.primitives.add(new Cesium.Primitive({ * geometryInstances : [rectangleInstance, ellipsoidInstance], * appearance : new Cesium.PerInstanceColorAppearance() * })); * * @example * // 3. Create the geometry on the main thread. * scene.primitives.add(new Cesium.Primitive({ * geometryInstances : new Cesium.GeometryInstance({ * geometry : Cesium.EllipsoidGeometry.createGeometry(new Cesium.EllipsoidGeometry({ * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0), * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL * })), * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame( * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()), * id : 'ellipsoid', * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA) * } * }), * appearance : new Cesium.PerInstanceColorAppearance() * })); * * @see GeometryInstance * @see Appearance * @see ClassificationPrimitive * @see GroundPrimitive */ function Primitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The geometry instances rendered with this primitive. This may * be undefined if options.releaseGeometryInstances * is true when the primitive is constructed. *

* Changing this property after the primitive is rendered has no effect. *

* * @readonly * @type GeometryInstance[]|GeometryInstance * * @default undefined */ this.geometryInstances = options.geometryInstances; /** * The {@link Appearance} used to shade this primitive. Each geometry * instance is shaded with the same appearance. Some appearances, like * {@link PerInstanceColorAppearance} allow giving each instance unique * properties. * * @type Appearance * * @default undefined */ this.appearance = options.appearance; this._appearance = undefined; this._material = undefined; /** * The {@link Appearance} used to shade this primitive when it fails the depth test. Each geometry * instance is shaded with the same appearance. Some appearances, like * {@link PerInstanceColorAppearance} allow giving each instance unique * properties. * *

* When using an appearance that requires a color attribute, like PerInstanceColorAppearance, * add a depthFailColor per-instance attribute instead. *

* *

* Requires the EXT_frag_depth WebGL extension to render properly. If the extension is not supported, * there may be artifacts. *

* @type Appearance * * @default undefined */ this.depthFailAppearance = options.depthFailAppearance; this._depthFailAppearance = undefined; this._depthFailMaterial = undefined; /** * The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates. * When this is the identity matrix, the primitive is drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * *

* This property is only supported in 3D mode. *

* * @type Matrix4 * * @default Matrix4.IDENTITY * * @example * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0); * p.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin); */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = new Matrix4(); /** * Determines if the primitive will be shown. This affects all geometry * instances in the primitive. * * @type Boolean * * @default true */ this.show = defaultValue(options.show, true); this._vertexCacheOptimize = defaultValue(options.vertexCacheOptimize, false); this._interleave = defaultValue(options.interleave, false); this._releaseGeometryInstances = defaultValue( options.releaseGeometryInstances, true ); this._allowPicking = defaultValue(options.allowPicking, true); this._asynchronous = defaultValue(options.asynchronous, true); this._compressVertices = defaultValue(options.compressVertices, true); /** * When true, the renderer frustum culls and horizon culls the primitive's commands * based on their bounding volume. Set this to false for a small performance gain * if you are manually culling the primitive. * * @type {Boolean} * * @default true */ this.cull = defaultValue(options.cull, true); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * @private */ this.rtcCenter = options.rtcCenter; //>>includeStart('debug', pragmas.debug); if ( defined(this.rtcCenter) && (!defined(this.geometryInstances) || (Array.isArray(this.geometryInstances) && this.geometryInstances.length !== 1)) ) { throw new DeveloperError( "Relative-to-center rendering only supports one geometry instance." ); } //>>includeEnd('debug'); /** * Determines whether this primitive casts or receives shadows from light sources. * * @type {ShadowMode} * * @default ShadowMode.DISABLED */ this.shadows = defaultValue(options.shadows, ShadowMode$1.DISABLED); this._translucent = undefined; this._state = PrimitiveState$1.READY; this._geometries = []; this._error = undefined; this._numberOfInstances = 0; this._boundingSpheres = []; this._boundingSphereWC = []; this._boundingSphereCV = []; this._boundingSphere2D = []; this._boundingSphereMorph = []; this._perInstanceAttributeCache = []; this._instanceIds = []; this._lastPerInstanceAttributeIndex = 0; this._va = []; this._attributeLocations = undefined; this._primitiveType = undefined; this._frontFaceRS = undefined; this._backFaceRS = undefined; this._sp = undefined; this._depthFailAppearance = undefined; this._spDepthFail = undefined; this._frontFaceDepthFailRS = undefined; this._backFaceDepthFailRS = undefined; this._pickIds = []; this._colorCommands = []; this._pickCommands = []; this._createBoundingVolumeFunction = options._createBoundingVolumeFunction; this._createRenderStatesFunction = options._createRenderStatesFunction; this._createShaderProgramFunction = options._createShaderProgramFunction; this._createCommandsFunction = options._createCommandsFunction; this._updateAndQueueCommandsFunction = options._updateAndQueueCommandsFunction; this._createPickOffsets = options._createPickOffsets; this._pickOffsets = undefined; this._createGeometryResults = undefined; this._ready = false; this._readyPromise = when.defer(); this._batchTable = undefined; this._batchTableAttributeIndices = undefined; this._offsetInstanceExtend = undefined; this._batchTableOffsetAttribute2DIndex = undefined; this._batchTableOffsetsUpdated = false; this._instanceBoundingSpheres = undefined; this._instanceBoundingSpheresCV = undefined; this._tempBoundingSpheres = undefined; this._recomputeBoundingSpheres = false; this._batchTableBoundingSpheresUpdated = false; this._batchTableBoundingSphereAttributeIndices = undefined; } Object.defineProperties(Primitive.prototype, { /** * When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default true */ vertexCacheOptimize: { get: function () { return this._vertexCacheOptimize; }, }, /** * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default false */ interleave: { get: function () { return this._interleave; }, }, /** * When true, the primitive does not keep a reference to the input geometryInstances to save memory. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default true */ releaseGeometryInstances: { get: function () { return this._releaseGeometryInstances; }, }, /** * When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking: { get: function () { return this._allowPicking; }, }, /** * Determines if the geometry instances will be created and batched on a web worker. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous: { get: function () { return this._asynchronous; }, }, /** * When true, geometry vertices are compressed, which will save memory. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly * * @default true */ compressVertices: { get: function () { return this._compressVertices; }, }, /** * Determines if the primitive is complete and ready to render. If this property is * true, the primitive will be rendered the next time that {@link Primitive#update} * is called. * * @memberof Primitive.prototype * * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Primitive.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); function getCommonPerInstanceAttributeNames(instances) { var length = instances.length; var attributesInAllInstances = []; var attributes0 = instances[0].attributes; var name; for (name in attributes0) { if (attributes0.hasOwnProperty(name) && defined(attributes0[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 !== otherAttribute.componentDatatype || attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute || attribute.normalize !== otherAttribute.normalize ) { inAllInstances = false; break; } } if (inAllInstances) { attributesInAllInstances.push(name); } } } return attributesInAllInstances; } var scratchGetAttributeCartesian2 = new Cartesian2(); var scratchGetAttributeCartesian3 = new Cartesian3(); var scratchGetAttributeCartesian4 = new Cartesian4(); function getAttributeValue(value) { var componentsPerAttribute = value.length; if (componentsPerAttribute === 1) { return value[0]; } else if (componentsPerAttribute === 2) { return Cartesian2.unpack(value, 0, scratchGetAttributeCartesian2); } else if (componentsPerAttribute === 3) { return Cartesian3.unpack(value, 0, scratchGetAttributeCartesian3); } else if (componentsPerAttribute === 4) { return Cartesian4.unpack(value, 0, scratchGetAttributeCartesian4); } } function createBatchTable$1(primitive, context) { var geometryInstances = primitive.geometryInstances; var instances = Array.isArray(geometryInstances) ? geometryInstances : [geometryInstances]; var numberOfInstances = instances.length; if (numberOfInstances === 0) { return; } var names = getCommonPerInstanceAttributeNames(instances); var length = names.length; var attributes = []; var attributeIndices = {}; var boundingSphereAttributeIndices = {}; var offset2DIndex; var firstInstance = instances[0]; var instanceAttributes = firstInstance.attributes; var i; var name; var attribute; for (i = 0; i < length; ++i) { name = names[i]; attribute = instanceAttributes[name]; attributeIndices[name] = i; attributes.push({ functionName: "czm_batchTable_" + name, componentDatatype: attribute.componentDatatype, componentsPerAttribute: attribute.componentsPerAttribute, normalize: attribute.normalize, }); } if (names.indexOf("distanceDisplayCondition") !== -1) { attributes.push( { functionName: "czm_batchTable_boundingSphereCenter3DHigh", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { functionName: "czm_batchTable_boundingSphereCenter3DLow", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { functionName: "czm_batchTable_boundingSphereCenter2DHigh", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { functionName: "czm_batchTable_boundingSphereCenter2DLow", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { functionName: "czm_batchTable_boundingSphereRadius", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 1, } ); boundingSphereAttributeIndices.center3DHigh = attributes.length - 5; boundingSphereAttributeIndices.center3DLow = attributes.length - 4; boundingSphereAttributeIndices.center2DHigh = attributes.length - 3; boundingSphereAttributeIndices.center2DLow = attributes.length - 2; boundingSphereAttributeIndices.radius = attributes.length - 1; } if (names.indexOf("offset") !== -1) { attributes.push({ functionName: "czm_batchTable_offset2D", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }); offset2DIndex = attributes.length - 1; } attributes.push({ functionName: "czm_batchTable_pickColor", componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 4, normalize: true, }); var attributesLength = attributes.length; var batchTable = new BatchTable(context, attributes, numberOfInstances); for (i = 0; i < numberOfInstances; ++i) { var instance = instances[i]; instanceAttributes = instance.attributes; for (var j = 0; j < length; ++j) { name = names[j]; attribute = instanceAttributes[name]; var value = getAttributeValue(attribute.value); var attributeIndex = attributeIndices[name]; batchTable.setBatchedAttribute(i, attributeIndex, value); } var pickObject = { primitive: defaultValue(instance.pickPrimitive, primitive), }; if (defined(instance.id)) { pickObject.id = instance.id; } var pickId = context.createPickId(pickObject); primitive._pickIds.push(pickId); var pickColor = pickId.color; var color = scratchGetAttributeCartesian4; color.x = Color.floatToByte(pickColor.red); color.y = Color.floatToByte(pickColor.green); color.z = Color.floatToByte(pickColor.blue); color.w = Color.floatToByte(pickColor.alpha); batchTable.setBatchedAttribute(i, attributesLength - 1, color); } primitive._batchTable = batchTable; primitive._batchTableAttributeIndices = attributeIndices; primitive._batchTableBoundingSphereAttributeIndices = boundingSphereAttributeIndices; primitive._batchTableOffsetAttribute2DIndex = offset2DIndex; } function cloneAttribute(attribute) { var clonedValues; if (Array.isArray(attribute.values)) { clonedValues = attribute.values.slice(0); } else { clonedValues = new attribute.values.constructor(attribute.values); } return new GeometryAttribute({ componentDatatype: attribute.componentDatatype, componentsPerAttribute: attribute.componentsPerAttribute, normalize: attribute.normalize, values: clonedValues, }); } function cloneGeometry(geometry) { var attributes = geometry.attributes; var newAttributes = new GeometryAttributes(); for (var property in attributes) { if (attributes.hasOwnProperty(property) && defined(attributes[property])) { newAttributes[property] = cloneAttribute(attributes[property]); } } var indices; if (defined(geometry.indices)) { var sourceValues = geometry.indices; if (Array.isArray(sourceValues)) { indices = sourceValues.slice(0); } else { indices = new sourceValues.constructor(sourceValues); } } return new Geometry({ attributes: newAttributes, indices: indices, primitiveType: geometry.primitiveType, boundingSphere: BoundingSphere.clone(geometry.boundingSphere), }); } function cloneInstance(instance, geometry) { return { geometry: geometry, attributes: instance.attributes, modelMatrix: Matrix4.clone(instance.modelMatrix), pickPrimitive: instance.pickPrimitive, id: instance.id, }; } var positionRegex = /attribute\s+vec(?:3|4)\s+(.*)3DHigh;/g; Primitive._modifyShaderPosition = function ( primitive, vertexShaderSource, scene3DOnly ) { var match; var forwardDecl = ""; var attributes = ""; var computeFunctions = ""; while ((match = positionRegex.exec(vertexShaderSource)) !== null) { var name = match[1]; var functionName = "vec4 czm_compute" + name[0].toUpperCase() + name.substr(1) + "()"; // Don't forward-declare czm_computePosition because computePosition.glsl already does. if (functionName !== "vec4 czm_computePosition()") { forwardDecl += functionName + ";\n"; } if (!defined(primitive.rtcCenter)) { // Use GPU RTE if (!scene3DOnly) { attributes += "attribute vec3 " + name + "2DHigh;\n" + "attribute vec3 " + name + "2DLow;\n"; computeFunctions += functionName + "\n" + "{\n" + " vec4 p;\n" + " if (czm_morphTime == 1.0)\n" + " {\n" + " p = czm_translateRelativeToEye(" + name + "3DHigh, " + name + "3DLow);\n" + " }\n" + " else if (czm_morphTime == 0.0)\n" + " {\n" + " p = czm_translateRelativeToEye(" + name + "2DHigh.zxy, " + name + "2DLow.zxy);\n" + " }\n" + " else\n" + " {\n" + " p = czm_columbusViewMorph(\n" + " czm_translateRelativeToEye(" + name + "2DHigh.zxy, " + name + "2DLow.zxy),\n" + " czm_translateRelativeToEye(" + name + "3DHigh, " + name + "3DLow),\n" + " czm_morphTime);\n" + " }\n" + " return p;\n" + "}\n\n"; } else { computeFunctions += functionName + "\n" + "{\n" + " return czm_translateRelativeToEye(" + name + "3DHigh, " + name + "3DLow);\n" + "}\n\n"; } } else { // Use RTC vertexShaderSource = vertexShaderSource.replace( /attribute\s+vec(?:3|4)\s+position3DHigh;/g, "" ); vertexShaderSource = vertexShaderSource.replace( /attribute\s+vec(?:3|4)\s+position3DLow;/g, "" ); forwardDecl += "uniform mat4 u_modifiedModelView;\n"; attributes += "attribute vec4 position;\n"; computeFunctions += functionName + "\n" + "{\n" + " return u_modifiedModelView * position;\n" + "}\n\n"; vertexShaderSource = vertexShaderSource.replace( /czm_modelViewRelativeToEye\s+\*\s+/g, "" ); vertexShaderSource = vertexShaderSource.replace( /czm_modelViewProjectionRelativeToEye/g, "czm_projection" ); } } return [forwardDecl, attributes, vertexShaderSource, computeFunctions].join( "\n" ); }; Primitive._appendShowToShader = function (primitive, vertexShaderSource) { if (!defined(primitive._batchTableAttributeIndices.show)) { return vertexShaderSource; } var renamedVS = ShaderSource.replaceMain( vertexShaderSource, "czm_non_show_main" ); var showMain = "void main() \n" + "{ \n" + " czm_non_show_main(); \n" + " gl_Position *= czm_batchTable_show(batchId); \n" + "}"; return renamedVS + "\n" + showMain; }; Primitive._updateColorAttribute = function ( primitive, vertexShaderSource, isDepthFail ) { // some appearances have a color attribute for per vertex color. // only remove if color is a per instance attribute. if ( !defined(primitive._batchTableAttributeIndices.color) && !defined(primitive._batchTableAttributeIndices.depthFailColor) ) { return vertexShaderSource; } if (vertexShaderSource.search(/attribute\s+vec4\s+color;/g) === -1) { return vertexShaderSource; } //>>includeStart('debug', pragmas.debug); if ( isDepthFail && !defined(primitive._batchTableAttributeIndices.depthFailColor) ) { throw new DeveloperError( "A depthFailColor per-instance attribute is required when using a depth fail appearance that uses a color attribute." ); } //>>includeEnd('debug'); var modifiedVS = vertexShaderSource; modifiedVS = modifiedVS.replace(/attribute\s+vec4\s+color;/g, ""); if (!isDepthFail) { modifiedVS = modifiedVS.replace( /(\b)color(\b)/g, "$1czm_batchTable_color(batchId)$2" ); } else { modifiedVS = modifiedVS.replace( /(\b)color(\b)/g, "$1czm_batchTable_depthFailColor(batchId)$2" ); } return modifiedVS; }; function appendPickToVertexShader(source) { var renamedVS = ShaderSource.replaceMain(source, "czm_non_pick_main"); var pickMain = "varying vec4 v_pickColor; \n" + "void main() \n" + "{ \n" + " czm_non_pick_main(); \n" + " v_pickColor = czm_batchTable_pickColor(batchId); \n" + "}"; return renamedVS + "\n" + pickMain; } function appendPickToFragmentShader(source) { return "varying vec4 v_pickColor;\n" + source; } Primitive._updatePickColorAttribute = function (source) { var vsPick = source.replace(/attribute\s+vec4\s+pickColor;/g, ""); vsPick = vsPick.replace( /(\b)pickColor(\b)/g, "$1czm_batchTable_pickColor(batchId)$2" ); return vsPick; }; Primitive._appendOffsetToShader = function (primitive, vertexShaderSource) { if (!defined(primitive._batchTableAttributeIndices.offset)) { return vertexShaderSource; } var attr = "attribute float batchId;\n"; attr += "attribute float applyOffset;"; var modifiedShader = vertexShaderSource.replace( /attribute\s+float\s+batchId;/g, attr ); var str = "vec4 $1 = czm_computePosition();\n"; str += " if (czm_sceneMode == czm_sceneMode3D)\n"; str += " {\n"; str += " $1 = $1 + vec4(czm_batchTable_offset(batchId) * applyOffset, 0.0);"; str += " }\n"; str += " else\n"; str += " {\n"; str += " $1 = $1 + vec4(czm_batchTable_offset2D(batchId) * applyOffset, 0.0);"; str += " }\n"; modifiedShader = modifiedShader.replace( /vec4\s+([A-Za-z0-9_]+)\s+=\s+czm_computePosition\(\);/g, str ); return modifiedShader; }; Primitive._appendDistanceDisplayConditionToShader = function ( primitive, vertexShaderSource, scene3DOnly ) { if ( !defined(primitive._batchTableAttributeIndices.distanceDisplayCondition) ) { return vertexShaderSource; } var renamedVS = ShaderSource.replaceMain( vertexShaderSource, "czm_non_distanceDisplayCondition_main" ); var distanceDisplayConditionMain = "void main() \n" + "{ \n" + " czm_non_distanceDisplayCondition_main(); \n" + " vec2 distanceDisplayCondition = czm_batchTable_distanceDisplayCondition(batchId);\n" + " vec3 boundingSphereCenter3DHigh = czm_batchTable_boundingSphereCenter3DHigh(batchId);\n" + " vec3 boundingSphereCenter3DLow = czm_batchTable_boundingSphereCenter3DLow(batchId);\n" + " float boundingSphereRadius = czm_batchTable_boundingSphereRadius(batchId);\n"; if (!scene3DOnly) { distanceDisplayConditionMain += " vec3 boundingSphereCenter2DHigh = czm_batchTable_boundingSphereCenter2DHigh(batchId);\n" + " vec3 boundingSphereCenter2DLow = czm_batchTable_boundingSphereCenter2DLow(batchId);\n" + " vec4 centerRTE;\n" + " if (czm_morphTime == 1.0)\n" + " {\n" + " centerRTE = czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow);\n" + " }\n" + " else if (czm_morphTime == 0.0)\n" + " {\n" + " centerRTE = czm_translateRelativeToEye(boundingSphereCenter2DHigh.zxy, boundingSphereCenter2DLow.zxy);\n" + " }\n" + " else\n" + " {\n" + " centerRTE = czm_columbusViewMorph(\n" + " czm_translateRelativeToEye(boundingSphereCenter2DHigh.zxy, boundingSphereCenter2DLow.zxy),\n" + " czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow),\n" + " czm_morphTime);\n" + " }\n"; } else { distanceDisplayConditionMain += " vec4 centerRTE = czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow);\n"; } distanceDisplayConditionMain += " float radiusSq = boundingSphereRadius * boundingSphereRadius; \n" + " float distanceSq; \n" + " if (czm_sceneMode == czm_sceneMode2D) \n" + " { \n" + " distanceSq = czm_eyeHeight2D.y - radiusSq; \n" + " } \n" + " else \n" + " { \n" + " distanceSq = dot(centerRTE.xyz, centerRTE.xyz) - radiusSq; \n" + " } \n" + " distanceSq = max(distanceSq, 0.0); \n" + " float nearSq = distanceDisplayCondition.x * distanceDisplayCondition.x; \n" + " float farSq = distanceDisplayCondition.y * distanceDisplayCondition.y; \n" + " float show = (distanceSq >= nearSq && distanceSq <= farSq) ? 1.0 : 0.0; \n" + " gl_Position *= show; \n" + "}"; return renamedVS + "\n" + distanceDisplayConditionMain; }; function modifyForEncodedNormals$1(primitive, vertexShaderSource) { if (!primitive.compressVertices) { return vertexShaderSource; } var containsNormal = vertexShaderSource.search(/attribute\s+vec3\s+normal;/g) !== -1; var containsSt = vertexShaderSource.search(/attribute\s+vec2\s+st;/g) !== -1; if (!containsNormal && !containsSt) { return vertexShaderSource; } var containsTangent = vertexShaderSource.search(/attribute\s+vec3\s+tangent;/g) !== -1; var containsBitangent = vertexShaderSource.search(/attribute\s+vec3\s+bitangent;/g) !== -1; var numComponents = containsSt && containsNormal ? 2.0 : 1.0; numComponents += containsTangent || containsBitangent ? 1 : 0; var type = numComponents > 1 ? "vec" + numComponents : "float"; var attributeName = "compressedAttributes"; var attributeDecl = "attribute " + type + " " + attributeName + ";"; var globalDecl = ""; var decode = ""; if (containsSt) { globalDecl += "vec2 st;\n"; var stComponent = numComponents > 1 ? attributeName + ".x" : attributeName; decode += " st = czm_decompressTextureCoordinates(" + stComponent + ");\n"; } if (containsNormal && containsTangent && containsBitangent) { globalDecl += "vec3 normal;\n" + "vec3 tangent;\n" + "vec3 bitangent;\n"; decode += " czm_octDecode(" + attributeName + "." + (containsSt ? "yz" : "xy") + ", normal, tangent, bitangent);\n"; } else { if (containsNormal) { globalDecl += "vec3 normal;\n"; decode += " normal = czm_octDecode(" + attributeName + (numComponents > 1 ? "." + (containsSt ? "y" : "x") : "") + ");\n"; } if (containsTangent) { globalDecl += "vec3 tangent;\n"; decode += " tangent = czm_octDecode(" + attributeName + "." + (containsSt && containsNormal ? "z" : "y") + ");\n"; } if (containsBitangent) { globalDecl += "vec3 bitangent;\n"; decode += " bitangent = czm_octDecode(" + attributeName + "." + (containsSt && containsNormal ? "z" : "y") + ");\n"; } } var modifiedVS = vertexShaderSource; modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+normal;/g, ""); modifiedVS = modifiedVS.replace(/attribute\s+vec2\s+st;/g, ""); modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+tangent;/g, ""); modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+bitangent;/g, ""); modifiedVS = ShaderSource.replaceMain(modifiedVS, "czm_non_compressed_main"); var compressedMain = "void main() \n" + "{ \n" + decode + " czm_non_compressed_main(); \n" + "}"; return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n"); } function depthClampVS(vertexShaderSource) { var modifiedVS = ShaderSource.replaceMain( vertexShaderSource, "czm_non_depth_clamp_main" ); modifiedVS += "void main() {\n" + " czm_non_depth_clamp_main();\n" + " gl_Position = czm_depthClamp(gl_Position);" + "}\n"; return modifiedVS; } function depthClampFS(fragmentShaderSource) { var modifiedFS = ShaderSource.replaceMain( fragmentShaderSource, "czm_non_depth_clamp_main" ); modifiedFS += "void main() {\n" + " czm_non_depth_clamp_main();\n" + "#if defined(GL_EXT_frag_depth)\n" + " #if defined(LOG_DEPTH)\n" + " czm_writeLogDepth();\n" + " #else\n" + " czm_writeDepthClamp();\n" + " #endif\n" + "#endif\n" + "}\n"; modifiedFS = "#ifdef GL_EXT_frag_depth\n" + "#extension GL_EXT_frag_depth : enable\n" + "#endif\n" + modifiedFS; return modifiedFS; } function validateShaderMatching(shaderProgram, attributeLocations) { // For a VAO and shader program to be compatible, the VAO must have // all active attribute in the shader program. The VAO may have // extra attributes with the only concern being a potential // performance hit due to extra memory bandwidth and cache pollution. // The shader source could have extra attributes that are not used, // but there is no guarantee they will be optimized out. // // Here, we validate that the VAO has all attributes required // to match the shader program. var shaderAttributes = shaderProgram.vertexAttributes; //>>includeStart('debug', pragmas.debug); for (var name in shaderAttributes) { if (shaderAttributes.hasOwnProperty(name)) { if (!defined(attributeLocations[name])) { throw new DeveloperError( "Appearance/Geometry mismatch. The appearance requires vertex shader attribute input '" + name + "', which was not computed as part of the Geometry. Use the appearance's vertexFormat property when constructing the geometry." ); } } } //>>includeEnd('debug'); } function getUniformFunction(uniforms, name) { return function () { return uniforms[name]; }; } var numberOfCreationWorkers = Math.max( FeatureDetection.hardwareConcurrency - 1, 1 ); var createGeometryTaskProcessors; var combineGeometryTaskProcessor = new TaskProcessor("combineGeometry"); function loadAsynchronous(primitive, frameState) { var instances; var geometry; var i; var j; var instanceIds = primitive._instanceIds; if (primitive._state === PrimitiveState$1.READY) { instances = Array.isArray(primitive.geometryInstances) ? primitive.geometryInstances : [primitive.geometryInstances]; var length = (primitive._numberOfInstances = instances.length); var promises = []; var subTasks = []; for (i = 0; i < length; ++i) { geometry = instances[i].geometry; instanceIds.push(instances[i].id); //>>includeStart('debug', pragmas.debug); if (!defined(geometry._workerName)) { throw new DeveloperError( "_workerName must be defined for asynchronous geometry." ); } //>>includeEnd('debug'); subTasks.push({ moduleName: geometry._workerName, geometry: geometry, }); } if (!defined(createGeometryTaskProcessors)) { createGeometryTaskProcessors = new Array(numberOfCreationWorkers); for (i = 0; i < numberOfCreationWorkers; i++) { createGeometryTaskProcessors[i] = new TaskProcessor("createGeometry"); } } var subTask; subTasks = subdivideArray(subTasks, numberOfCreationWorkers); for (i = 0; i < subTasks.length; i++) { var packedLength = 0; var workerSubTasks = subTasks[i]; var workerSubTasksLength = workerSubTasks.length; for (j = 0; j < workerSubTasksLength; ++j) { subTask = workerSubTasks[j]; geometry = subTask.geometry; if (defined(geometry.constructor.pack)) { subTask.offset = packedLength; packedLength += defaultValue( geometry.constructor.packedLength, geometry.packedLength ); } } var subTaskTransferableObjects; if (packedLength > 0) { var array = new Float64Array(packedLength); subTaskTransferableObjects = [array.buffer]; for (j = 0; j < workerSubTasksLength; ++j) { subTask = workerSubTasks[j]; geometry = subTask.geometry; if (defined(geometry.constructor.pack)) { geometry.constructor.pack(geometry, array, subTask.offset); subTask.geometry = array; } } } promises.push( createGeometryTaskProcessors[i].scheduleTask( { subTasks: subTasks[i], }, subTaskTransferableObjects ) ); } primitive._state = PrimitiveState$1.CREATING; when .all(promises, function (results) { primitive._createGeometryResults = results; primitive._state = PrimitiveState$1.CREATED; }) .otherwise(function (error) { setReady(primitive, frameState, PrimitiveState$1.FAILED, error); }); } else if (primitive._state === PrimitiveState$1.CREATED) { var transferableObjects = []; instances = Array.isArray(primitive.geometryInstances) ? primitive.geometryInstances : [primitive.geometryInstances]; var scene3DOnly = frameState.scene3DOnly; var projection = frameState.mapProjection; var promise = combineGeometryTaskProcessor.scheduleTask( PrimitivePipeline.packCombineGeometryParameters( { createGeometryResults: primitive._createGeometryResults, instances: instances, ellipsoid: projection.ellipsoid, projection: projection, elementIndexUintSupported: frameState.context.elementIndexUint, scene3DOnly: scene3DOnly, vertexCacheOptimize: primitive.vertexCacheOptimize, compressVertices: primitive.compressVertices, modelMatrix: primitive.modelMatrix, createPickOffsets: primitive._createPickOffsets, }, transferableObjects ), transferableObjects ); primitive._createGeometryResults = undefined; primitive._state = PrimitiveState$1.COMBINING; when(promise, function (packedResult) { var result = PrimitivePipeline.unpackCombineGeometryResults(packedResult); primitive._geometries = result.geometries; primitive._attributeLocations = result.attributeLocations; primitive.modelMatrix = Matrix4.clone( result.modelMatrix, primitive.modelMatrix ); primitive._pickOffsets = result.pickOffsets; primitive._offsetInstanceExtend = result.offsetInstanceExtend; primitive._instanceBoundingSpheres = result.boundingSpheres; primitive._instanceBoundingSpheresCV = result.boundingSpheresCV; if (defined(primitive._geometries) && primitive._geometries.length > 0) { primitive._recomputeBoundingSpheres = true; primitive._state = PrimitiveState$1.COMBINED; } else { setReady(primitive, frameState, PrimitiveState$1.FAILED, undefined); } }).otherwise(function (error) { setReady(primitive, frameState, PrimitiveState$1.FAILED, error); }); } } function loadSynchronous(primitive, frameState) { var instances = Array.isArray(primitive.geometryInstances) ? primitive.geometryInstances : [primitive.geometryInstances]; var length = (primitive._numberOfInstances = instances.length); var clonedInstances = new Array(length); var instanceIds = primitive._instanceIds; var instance; var i; var geometryIndex = 0; for (i = 0; i < length; i++) { instance = instances[i]; var geometry = instance.geometry; var createdGeometry; if (defined(geometry.attributes) && defined(geometry.primitiveType)) { createdGeometry = cloneGeometry(geometry); } else { createdGeometry = geometry.constructor.createGeometry(geometry); } clonedInstances[geometryIndex++] = cloneInstance(instance, createdGeometry); instanceIds.push(instance.id); } clonedInstances.length = geometryIndex; var scene3DOnly = frameState.scene3DOnly; var projection = frameState.mapProjection; var result = PrimitivePipeline.combineGeometry({ instances: clonedInstances, ellipsoid: projection.ellipsoid, projection: projection, elementIndexUintSupported: frameState.context.elementIndexUint, scene3DOnly: scene3DOnly, vertexCacheOptimize: primitive.vertexCacheOptimize, compressVertices: primitive.compressVertices, modelMatrix: primitive.modelMatrix, createPickOffsets: primitive._createPickOffsets, }); primitive._geometries = result.geometries; primitive._attributeLocations = result.attributeLocations; primitive.modelMatrix = Matrix4.clone( result.modelMatrix, primitive.modelMatrix ); primitive._pickOffsets = result.pickOffsets; primitive._offsetInstanceExtend = result.offsetInstanceExtend; primitive._instanceBoundingSpheres = result.boundingSpheres; primitive._instanceBoundingSpheresCV = result.boundingSpheresCV; if (defined(primitive._geometries) && primitive._geometries.length > 0) { primitive._recomputeBoundingSpheres = true; primitive._state = PrimitiveState$1.COMBINED; } else { setReady(primitive, frameState, PrimitiveState$1.FAILED, undefined); } } function recomputeBoundingSpheres(primitive, frameState) { var offsetIndex = primitive._batchTableAttributeIndices.offset; if (!primitive._recomputeBoundingSpheres || !defined(offsetIndex)) { primitive._recomputeBoundingSpheres = false; return; } var i; var offsetInstanceExtend = primitive._offsetInstanceExtend; var boundingSpheres = primitive._instanceBoundingSpheres; var length = boundingSpheres.length; var newBoundingSpheres = primitive._tempBoundingSpheres; if (!defined(newBoundingSpheres)) { newBoundingSpheres = new Array(length); for (i = 0; i < length; i++) { newBoundingSpheres[i] = new BoundingSphere(); } primitive._tempBoundingSpheres = newBoundingSpheres; } for (i = 0; i < length; ++i) { var newBS = newBoundingSpheres[i]; var offset = primitive._batchTable.getBatchedAttribute( i, offsetIndex, new Cartesian3() ); newBS = boundingSpheres[i].clone(newBS); transformBoundingSphere(newBS, offset, offsetInstanceExtend[i]); } var combinedBS = []; var combinedWestBS = []; var combinedEastBS = []; for (i = 0; i < length; ++i) { var bs = newBoundingSpheres[i]; var minX = bs.center.x - bs.radius; if ( minX > 0 || BoundingSphere.intersectPlane(bs, Plane.ORIGIN_ZX_PLANE) !== Intersect$1.INTERSECTING ) { combinedBS.push(bs); } else { combinedWestBS.push(bs); combinedEastBS.push(bs); } } var resultBS1 = combinedBS[0]; var resultBS2 = combinedEastBS[0]; var resultBS3 = combinedWestBS[0]; for (i = 1; i < combinedBS.length; i++) { resultBS1 = BoundingSphere.union(resultBS1, combinedBS[i]); } for (i = 1; i < combinedEastBS.length; i++) { resultBS2 = BoundingSphere.union(resultBS2, combinedEastBS[i]); } for (i = 1; i < combinedWestBS.length; i++) { resultBS3 = BoundingSphere.union(resultBS3, combinedWestBS[i]); } var result = []; if (defined(resultBS1)) { result.push(resultBS1); } if (defined(resultBS2)) { result.push(resultBS2); } if (defined(resultBS3)) { result.push(resultBS3); } for (i = 0; i < result.length; i++) { var boundingSphere = result[i].clone(primitive._boundingSpheres[i]); primitive._boundingSpheres[i] = boundingSphere; primitive._boundingSphereCV[i] = BoundingSphere.projectTo2D( boundingSphere, frameState.mapProjection, primitive._boundingSphereCV[i] ); } Primitive._updateBoundingVolumes( primitive, frameState, primitive.modelMatrix, true ); primitive._recomputeBoundingSpheres = false; } var scratchBoundingSphereCenterEncoded = new EncodedCartesian3(); var scratchBoundingSphereCartographic = new Cartographic(); var scratchBoundingSphereCenter2D = new Cartesian3(); var scratchBoundingSphere$2 = new BoundingSphere(); function updateBatchTableBoundingSpheres(primitive, frameState) { var hasDistanceDisplayCondition = defined( primitive._batchTableAttributeIndices.distanceDisplayCondition ); if ( !hasDistanceDisplayCondition || primitive._batchTableBoundingSpheresUpdated ) { return; } var indices = primitive._batchTableBoundingSphereAttributeIndices; var center3DHighIndex = indices.center3DHigh; var center3DLowIndex = indices.center3DLow; var center2DHighIndex = indices.center2DHigh; var center2DLowIndex = indices.center2DLow; var radiusIndex = indices.radius; var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var batchTable = primitive._batchTable; var boundingSpheres = primitive._instanceBoundingSpheres; var length = boundingSpheres.length; for (var i = 0; i < length; ++i) { var boundingSphere = boundingSpheres[i]; if (!defined(boundingSphere)) { continue; } var modelMatrix = primitive.modelMatrix; if (defined(modelMatrix)) { boundingSphere = BoundingSphere.transform( boundingSphere, modelMatrix, scratchBoundingSphere$2 ); } var center = boundingSphere.center; var radius = boundingSphere.radius; var encodedCenter = EncodedCartesian3.fromCartesian( center, scratchBoundingSphereCenterEncoded ); batchTable.setBatchedAttribute(i, center3DHighIndex, encodedCenter.high); batchTable.setBatchedAttribute(i, center3DLowIndex, encodedCenter.low); if (!frameState.scene3DOnly) { var cartographic = ellipsoid.cartesianToCartographic( center, scratchBoundingSphereCartographic ); var center2D = projection.project( cartographic, scratchBoundingSphereCenter2D ); encodedCenter = EncodedCartesian3.fromCartesian( center2D, scratchBoundingSphereCenterEncoded ); batchTable.setBatchedAttribute(i, center2DHighIndex, encodedCenter.high); batchTable.setBatchedAttribute(i, center2DLowIndex, encodedCenter.low); } batchTable.setBatchedAttribute(i, radiusIndex, radius); } primitive._batchTableBoundingSpheresUpdated = true; } var offsetScratchCartesian = new Cartesian3(); var offsetCenterScratch = new Cartesian3(); function updateBatchTableOffsets(primitive, frameState) { var hasOffset = defined(primitive._batchTableAttributeIndices.offset); if ( !hasOffset || primitive._batchTableOffsetsUpdated || frameState.scene3DOnly ) { return; } var index2D = primitive._batchTableOffsetAttribute2DIndex; var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var batchTable = primitive._batchTable; var boundingSpheres = primitive._instanceBoundingSpheres; var length = boundingSpheres.length; for (var i = 0; i < length; ++i) { var boundingSphere = boundingSpheres[i]; if (!defined(boundingSphere)) { continue; } var offset = batchTable.getBatchedAttribute( i, primitive._batchTableAttributeIndices.offset ); if (Cartesian3.equals(offset, Cartesian3.ZERO)) { batchTable.setBatchedAttribute(i, index2D, Cartesian3.ZERO); continue; } var modelMatrix = primitive.modelMatrix; if (defined(modelMatrix)) { boundingSphere = BoundingSphere.transform( boundingSphere, modelMatrix, scratchBoundingSphere$2 ); } var center = boundingSphere.center; center = ellipsoid.scaleToGeodeticSurface(center, offsetCenterScratch); var cartographic = ellipsoid.cartesianToCartographic( center, scratchBoundingSphereCartographic ); var center2D = projection.project( cartographic, scratchBoundingSphereCenter2D ); var newPoint = Cartesian3.add(offset, center, offsetScratchCartesian); cartographic = ellipsoid.cartesianToCartographic(newPoint, cartographic); var newPointProjected = projection.project( cartographic, offsetScratchCartesian ); var newVector = Cartesian3.subtract( newPointProjected, center2D, offsetScratchCartesian ); var x = newVector.x; newVector.x = newVector.z; newVector.z = newVector.y; newVector.y = x; batchTable.setBatchedAttribute(i, index2D, newVector); } primitive._batchTableOffsetsUpdated = true; } function createVertexArray$4(primitive, frameState) { var attributeLocations = primitive._attributeLocations; var geometries = primitive._geometries; var scene3DOnly = frameState.scene3DOnly; var context = frameState.context; var va = []; var length = geometries.length; for (var i = 0; i < length; ++i) { var geometry = geometries[i]; va.push( VertexArray.fromGeometry({ context: context, geometry: geometry, attributeLocations: attributeLocations, bufferUsage: BufferUsage$1.STATIC_DRAW, interleave: primitive._interleave, }) ); if (defined(primitive._createBoundingVolumeFunction)) { primitive._createBoundingVolumeFunction(frameState, geometry); } else { primitive._boundingSpheres.push( BoundingSphere.clone(geometry.boundingSphere) ); primitive._boundingSphereWC.push(new BoundingSphere()); if (!scene3DOnly) { var center = geometry.boundingSphereCV.center; var x = center.x; var y = center.y; var z = center.z; center.x = z; center.y = x; center.z = y; primitive._boundingSphereCV.push( BoundingSphere.clone(geometry.boundingSphereCV) ); primitive._boundingSphere2D.push(new BoundingSphere()); primitive._boundingSphereMorph.push(new BoundingSphere()); } } } primitive._va = va; primitive._primitiveType = geometries[0].primitiveType; if (primitive.releaseGeometryInstances) { primitive.geometryInstances = undefined; } primitive._geometries = undefined; setReady(primitive, frameState, PrimitiveState$1.COMPLETE, undefined); } function createRenderStates$5(primitive, context, appearance, twoPasses) { var renderState = appearance.getRenderState(); var rs; if (twoPasses) { rs = clone$1(renderState, false); rs.cull = { enabled: true, face: CullFace$1.BACK, }; primitive._frontFaceRS = RenderState.fromCache(rs); rs.cull.face = CullFace$1.FRONT; primitive._backFaceRS = RenderState.fromCache(rs); } else { primitive._frontFaceRS = RenderState.fromCache(renderState); primitive._backFaceRS = primitive._frontFaceRS; } rs = clone$1(renderState, false); if (defined(primitive._depthFailAppearance)) { rs.depthTest.enabled = false; } if (defined(primitive._depthFailAppearance)) { renderState = primitive._depthFailAppearance.getRenderState(); rs = clone$1(renderState, false); rs.depthTest.func = DepthFunction$1.GREATER; if (twoPasses) { rs.cull = { enabled: true, face: CullFace$1.BACK, }; primitive._frontFaceDepthFailRS = RenderState.fromCache(rs); rs.cull.face = CullFace$1.FRONT; primitive._backFaceDepthFailRS = RenderState.fromCache(rs); } else { primitive._frontFaceDepthFailRS = RenderState.fromCache(rs); primitive._backFaceDepthFailRS = primitive._frontFaceRS; } } } function createShaderProgram$2(primitive, frameState, appearance) { var context = frameState.context; var attributeLocations = primitive._attributeLocations; var vs = primitive._batchTable.getVertexShaderCallback()( appearance.vertexShaderSource ); vs = Primitive._appendOffsetToShader(primitive, vs); vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader( primitive, vs, frameState.scene3DOnly ); vs = appendPickToVertexShader(vs); vs = Primitive._updateColorAttribute(primitive, vs, false); vs = modifyForEncodedNormals$1(primitive, vs); vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly); var fs = appearance.getFragmentShaderSource(); fs = appendPickToFragmentShader(fs); primitive._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: primitive._sp, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); validateShaderMatching(primitive._sp, attributeLocations); if (defined(primitive._depthFailAppearance)) { vs = primitive._batchTable.getVertexShaderCallback()( primitive._depthFailAppearance.vertexShaderSource ); vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader( primitive, vs, frameState.scene3DOnly ); vs = appendPickToVertexShader(vs); vs = Primitive._updateColorAttribute(primitive, vs, true); vs = modifyForEncodedNormals$1(primitive, vs); vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly); vs = depthClampVS(vs); fs = primitive._depthFailAppearance.getFragmentShaderSource(); fs = appendPickToFragmentShader(fs); fs = depthClampFS(fs); primitive._spDepthFail = ShaderProgram.replaceCache({ context: context, shaderProgram: primitive._spDepthFail, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); validateShaderMatching(primitive._spDepthFail, attributeLocations); } } var modifiedModelViewScratch$3 = new Matrix4(); var rtcScratch$3 = new Cartesian3(); function getUniforms(primitive, appearance, material, frameState) { // Create uniform map by combining uniforms from the appearance and material if either have uniforms. var materialUniformMap = defined(material) ? material._uniforms : undefined; var appearanceUniformMap = {}; var appearanceUniforms = appearance.uniforms; if (defined(appearanceUniforms)) { // Convert to uniform map of functions for the renderer for (var name in appearanceUniforms) { if (appearanceUniforms.hasOwnProperty(name)) { //>>includeStart('debug', pragmas.debug); if (defined(materialUniformMap) && defined(materialUniformMap[name])) { // Later, we could rename uniforms behind-the-scenes if needed. throw new DeveloperError( "Appearance and material have a uniform with the same name: " + name ); } //>>includeEnd('debug'); appearanceUniformMap[name] = getUniformFunction( appearanceUniforms, name ); } } } var uniforms = combine$2(appearanceUniformMap, materialUniformMap); uniforms = primitive._batchTable.getUniformMapCallback()(uniforms); if (defined(primitive.rtcCenter)) { uniforms.u_modifiedModelView = function () { var viewMatrix = frameState.context.uniformState.view; Matrix4.multiply( viewMatrix, primitive._modelMatrix, modifiedModelViewScratch$3 ); Matrix4.multiplyByPoint( modifiedModelViewScratch$3, primitive.rtcCenter, rtcScratch$3 ); Matrix4.setTranslation( modifiedModelViewScratch$3, rtcScratch$3, modifiedModelViewScratch$3 ); return modifiedModelViewScratch$3; }; } return uniforms; } function createCommands$5( primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands, frameState ) { var uniforms = getUniforms(primitive, appearance, material, frameState); var depthFailUniforms; if (defined(primitive._depthFailAppearance)) { depthFailUniforms = getUniforms( primitive, primitive._depthFailAppearance, primitive._depthFailAppearance.material, frameState ); } var pass = translucent ? Pass$1.TRANSLUCENT : Pass$1.OPAQUE; var multiplier = twoPasses ? 2 : 1; multiplier *= defined(primitive._depthFailAppearance) ? 2 : 1; colorCommands.length = primitive._va.length * multiplier; var length = colorCommands.length; var vaIndex = 0; for (var i = 0; i < length; ++i) { var colorCommand; if (twoPasses) { colorCommand = colorCommands[i]; if (!defined(colorCommand)) { colorCommand = colorCommands[i] = new DrawCommand({ owner: primitive, primitiveType: primitive._primitiveType, }); } colorCommand.vertexArray = primitive._va[vaIndex]; colorCommand.renderState = primitive._backFaceRS; colorCommand.shaderProgram = primitive._sp; colorCommand.uniformMap = uniforms; colorCommand.pass = pass; ++i; } colorCommand = colorCommands[i]; if (!defined(colorCommand)) { colorCommand = colorCommands[i] = new DrawCommand({ owner: primitive, primitiveType: primitive._primitiveType, }); } colorCommand.vertexArray = primitive._va[vaIndex]; colorCommand.renderState = primitive._frontFaceRS; colorCommand.shaderProgram = primitive._sp; colorCommand.uniformMap = uniforms; colorCommand.pass = pass; if (defined(primitive._depthFailAppearance)) { if (twoPasses) { ++i; colorCommand = colorCommands[i]; if (!defined(colorCommand)) { colorCommand = colorCommands[i] = new DrawCommand({ owner: primitive, primitiveType: primitive._primitiveType, }); } colorCommand.vertexArray = primitive._va[vaIndex]; colorCommand.renderState = primitive._backFaceDepthFailRS; colorCommand.shaderProgram = primitive._spDepthFail; colorCommand.uniformMap = depthFailUniforms; colorCommand.pass = pass; } ++i; colorCommand = colorCommands[i]; if (!defined(colorCommand)) { colorCommand = colorCommands[i] = new DrawCommand({ owner: primitive, primitiveType: primitive._primitiveType, }); } colorCommand.vertexArray = primitive._va[vaIndex]; colorCommand.renderState = primitive._frontFaceDepthFailRS; colorCommand.shaderProgram = primitive._spDepthFail; colorCommand.uniformMap = depthFailUniforms; colorCommand.pass = pass; } ++vaIndex; } } Primitive._updateBoundingVolumes = function ( primitive, frameState, modelMatrix, forceUpdate ) { var i; var length; var boundingSphere; if (forceUpdate || !Matrix4.equals(modelMatrix, primitive._modelMatrix)) { Matrix4.clone(modelMatrix, primitive._modelMatrix); length = primitive._boundingSpheres.length; for (i = 0; i < length; ++i) { boundingSphere = primitive._boundingSpheres[i]; if (defined(boundingSphere)) { primitive._boundingSphereWC[i] = BoundingSphere.transform( boundingSphere, modelMatrix, primitive._boundingSphereWC[i] ); if (!frameState.scene3DOnly) { primitive._boundingSphere2D[i] = BoundingSphere.clone( primitive._boundingSphereCV[i], primitive._boundingSphere2D[i] ); primitive._boundingSphere2D[i].center.x = 0.0; primitive._boundingSphereMorph[i] = BoundingSphere.union( primitive._boundingSphereWC[i], primitive._boundingSphereCV[i] ); } } } } // Update bounding volumes for primitives that are sized in pixels. // The pixel size in meters varies based on the distance from the camera. var pixelSize = primitive.appearance.pixelSize; if (defined(pixelSize)) { length = primitive._boundingSpheres.length; for (i = 0; i < length; ++i) { boundingSphere = primitive._boundingSpheres[i]; var boundingSphereWC = primitive._boundingSphereWC[i]; var pixelSizeInMeters = frameState.camera.getPixelSize( boundingSphere, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight ); var sizeInMeters = pixelSizeInMeters * pixelSize; boundingSphereWC.radius = boundingSphere.radius + sizeInMeters; } } }; function updateAndQueueCommands$3( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { //>>includeStart('debug', pragmas.debug); if ( frameState.mode !== SceneMode$1.SCENE3D && !Matrix4.equals(modelMatrix, Matrix4.IDENTITY) ) { throw new DeveloperError( "Primitive.modelMatrix is only supported in 3D mode." ); } //>>includeEnd('debug'); Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); var boundingSpheres; if (frameState.mode === SceneMode$1.SCENE3D) { boundingSpheres = primitive._boundingSphereWC; } else if (frameState.mode === SceneMode$1.COLUMBUS_VIEW) { boundingSpheres = primitive._boundingSphereCV; } else if ( frameState.mode === SceneMode$1.SCENE2D && defined(primitive._boundingSphere2D) ) { boundingSpheres = primitive._boundingSphere2D; } else if (defined(primitive._boundingSphereMorph)) { boundingSpheres = primitive._boundingSphereMorph; } var commandList = frameState.commandList; var passes = frameState.passes; if (passes.render || passes.pick) { var allowPicking = primitive.allowPicking; var castShadows = ShadowMode$1.castShadows(primitive.shadows); var receiveShadows = ShadowMode$1.receiveShadows(primitive.shadows); var colorLength = colorCommands.length; var factor = twoPasses ? 2 : 1; factor *= defined(primitive._depthFailAppearance) ? 2 : 1; for (var j = 0; j < colorLength; ++j) { var sphereIndex = Math.floor(j / factor); var colorCommand = colorCommands[j]; colorCommand.modelMatrix = modelMatrix; colorCommand.boundingVolume = boundingSpheres[sphereIndex]; colorCommand.cull = cull; colorCommand.debugShowBoundingVolume = debugShowBoundingVolume; colorCommand.castShadows = castShadows; colorCommand.receiveShadows = receiveShadows; if (allowPicking) { colorCommand.pickId = "v_pickColor"; } else { colorCommand.pickId = undefined; } commandList.push(colorCommand); } } } /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. * @exception {DeveloperError} Primitive.modelMatrix is only supported in 3D mode. * @exception {RuntimeError} Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero. */ Primitive.prototype.update = function (frameState) { if ( (!defined(this.geometryInstances) && this._va.length === 0) || (defined(this.geometryInstances) && Array.isArray(this.geometryInstances) && this.geometryInstances.length === 0) || !defined(this.appearance) || (frameState.mode !== SceneMode$1.SCENE3D && frameState.scene3DOnly) || (!frameState.passes.render && !frameState.passes.pick) ) { return; } if (defined(this._error)) { throw this._error; } //>>includeStart('debug', pragmas.debug); if (defined(this.rtcCenter) && !frameState.scene3DOnly) { throw new DeveloperError( "RTC rendering is only available for 3D only scenes." ); } //>>includeEnd('debug'); if (this._state === PrimitiveState$1.FAILED) { return; } var context = frameState.context; if (!defined(this._batchTable)) { createBatchTable$1(this, context); } if (this._batchTable.attributes.length > 0) { if (ContextLimits.maximumVertexTextureImageUnits === 0) { throw new RuntimeError( "Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero." ); } this._batchTable.update(frameState); } if ( this._state !== PrimitiveState$1.COMPLETE && this._state !== PrimitiveState$1.COMBINED ) { if (this.asynchronous) { loadAsynchronous(this, frameState); } else { loadSynchronous(this, frameState); } } if (this._state === PrimitiveState$1.COMBINED) { updateBatchTableBoundingSpheres(this, frameState); updateBatchTableOffsets(this, frameState); createVertexArray$4(this, frameState); } if (!this.show || this._state !== PrimitiveState$1.COMPLETE) { return; } if (!this._batchTableOffsetsUpdated) { updateBatchTableOffsets(this, frameState); } if (this._recomputeBoundingSpheres) { recomputeBoundingSpheres(this, frameState); } // Create or recreate render state and shader program if appearance/material changed var appearance = this.appearance; var material = appearance.material; var createRS = false; var createSP = false; if (this._appearance !== appearance) { this._appearance = appearance; this._material = material; createRS = true; createSP = true; } else if (this._material !== material) { this._material = material; createSP = true; } var depthFailAppearance = this.depthFailAppearance; var depthFailMaterial = defined(depthFailAppearance) ? depthFailAppearance.material : undefined; if (this._depthFailAppearance !== depthFailAppearance) { this._depthFailAppearance = depthFailAppearance; this._depthFailMaterial = depthFailMaterial; createRS = true; createSP = true; } else if (this._depthFailMaterial !== depthFailMaterial) { this._depthFailMaterial = depthFailMaterial; createSP = true; } var translucent = this._appearance.isTranslucent(); if (this._translucent !== translucent) { this._translucent = translucent; createRS = true; } if (defined(this._material)) { this._material.update(context); } var twoPasses = appearance.closed && translucent; if (createRS) { var rsFunc = defaultValue( this._createRenderStatesFunction, createRenderStates$5 ); rsFunc(this, context, appearance, twoPasses); } if (createSP) { var spFunc = defaultValue( this._createShaderProgramFunction, createShaderProgram$2 ); spFunc(this, frameState, appearance); } if (createRS || createSP) { var commandFunc = defaultValue( this._createCommandsFunction, createCommands$5 ); commandFunc( this, appearance, material, translucent, twoPasses, this._colorCommands, this._pickCommands, frameState ); } var updateAndQueueCommandsFunc = defaultValue( this._updateAndQueueCommandsFunction, updateAndQueueCommands$3 ); updateAndQueueCommandsFunc( this, frameState, this._colorCommands, this._pickCommands, this.modelMatrix, this.cull, this.debugShowBoundingVolume, twoPasses ); }; var offsetBoundingSphereScratch1 = new BoundingSphere(); var offsetBoundingSphereScratch2 = new BoundingSphere(); function transformBoundingSphere(boundingSphere, offset, offsetAttribute) { if (offsetAttribute === GeometryOffsetAttribute$1.TOP) { var origBS = BoundingSphere.clone( boundingSphere, offsetBoundingSphereScratch1 ); var offsetBS = BoundingSphere.clone( boundingSphere, offsetBoundingSphereScratch2 ); offsetBS.center = Cartesian3.add(offsetBS.center, offset, offsetBS.center); boundingSphere = BoundingSphere.union(origBS, offsetBS, boundingSphere); } else if (offsetAttribute === GeometryOffsetAttribute$1.ALL) { boundingSphere.center = Cartesian3.add( boundingSphere.center, offset, boundingSphere.center ); } return boundingSphere; } function createGetFunction(batchTable, instanceIndex, attributeIndex) { return function () { var attributeValue = batchTable.getBatchedAttribute( instanceIndex, attributeIndex ); var attribute = batchTable.attributes[attributeIndex]; var componentsPerAttribute = attribute.componentsPerAttribute; var value = ComponentDatatype$1.createTypedArray( attribute.componentDatatype, componentsPerAttribute ); if (defined(attributeValue.constructor.pack)) { attributeValue.constructor.pack(attributeValue, value, 0); } else { value[0] = attributeValue; } return value; }; } function createSetFunction( batchTable, instanceIndex, attributeIndex, primitive, name ) { return function (value) { //>>includeStart('debug', pragmas.debug); if ( !defined(value) || !defined(value.length) || value.length < 1 || value.length > 4 ) { throw new DeveloperError( "value must be and array with length between 1 and 4." ); } //>>includeEnd('debug'); var attributeValue = getAttributeValue(value); batchTable.setBatchedAttribute( instanceIndex, attributeIndex, attributeValue ); if (name === "offset") { primitive._recomputeBoundingSpheres = true; primitive._batchTableOffsetsUpdated = false; } }; } var offsetScratch$a = new Cartesian3(); function createBoundingSphereProperties(primitive, properties, index) { properties.boundingSphere = { get: function () { var boundingSphere = primitive._instanceBoundingSpheres[index]; if (defined(boundingSphere)) { boundingSphere = boundingSphere.clone(); var modelMatrix = primitive.modelMatrix; var offset = properties.offset; if (defined(offset)) { transformBoundingSphere( boundingSphere, Cartesian3.fromArray(offset.get(), 0, offsetScratch$a), primitive._offsetInstanceExtend[index] ); } if (defined(modelMatrix)) { boundingSphere = BoundingSphere.transform( boundingSphere, modelMatrix ); } } return boundingSphere; }, }; properties.boundingSphereCV = { get: function () { return primitive._instanceBoundingSpheresCV[index]; }, }; } function createPickIdProperty(primitive, properties, index) { properties.pickId = { get: function () { return primitive._pickIds[index]; }, }; } /** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {*} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); * attributes.distanceDisplayCondition = Cesium.DistanceDisplayConditionGeometryInstanceAttribute.toValue(100.0, 10000.0); * attributes.offset = Cesium.OffsetGeometryInstanceAttribute.toValue(Cartesian3.IDENTITY); */ Primitive.prototype.getGeometryInstanceAttributes = function (id) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required"); } if (!defined(this._batchTable)) { throw new DeveloperError( "must call update before calling getGeometryInstanceAttributes" ); } //>>includeEnd('debug'); var index = -1; var lastIndex = this._lastPerInstanceAttributeIndex; var ids = this._instanceIds; var length = ids.length; for (var i = 0; i < length; ++i) { var curIndex = (lastIndex + i) % length; if (id === ids[curIndex]) { index = curIndex; break; } } if (index === -1) { return undefined; } var attributes = this._perInstanceAttributeCache[index]; if (defined(attributes)) { return attributes; } var batchTable = this._batchTable; var perInstanceAttributeIndices = this._batchTableAttributeIndices; attributes = {}; var properties = {}; for (var name in perInstanceAttributeIndices) { if (perInstanceAttributeIndices.hasOwnProperty(name)) { var attributeIndex = perInstanceAttributeIndices[name]; properties[name] = { get: createGetFunction(batchTable, index, attributeIndex), set: createSetFunction(batchTable, index, attributeIndex, this, name), }; } } createBoundingSphereProperties(this, properties, index); createPickIdProperty(this, properties, index); Object.defineProperties(attributes, properties); this._lastPerInstanceAttributeIndex = index; this._perInstanceAttributeCache[index] = attributes; return attributes; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see Primitive#destroy */ Primitive.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * e = e && e.destroy(); * * @see Primitive#isDestroyed */ Primitive.prototype.destroy = function () { var length; var i; this._sp = this._sp && this._sp.destroy(); this._spDepthFail = this._spDepthFail && this._spDepthFail.destroy(); var va = this._va; length = va.length; for (i = 0; i < length; ++i) { va[i].destroy(); } this._va = undefined; var pickIds = this._pickIds; length = pickIds.length; for (i = 0; i < length; ++i) { pickIds[i].destroy(); } this._pickIds = undefined; this._batchTable = this._batchTable && this._batchTable.destroy(); //These objects may be fairly large and reference other large objects (like Entities) //We explicitly set them to undefined here so that the memory can be freed //even if a reference to the destroyed Primitive has been kept around. this._instanceIds = undefined; this._perInstanceAttributeCache = undefined; this._attributeLocations = undefined; return destroyObject(this); }; function setReady(primitive, frameState, state, error) { primitive._error = error; primitive._state = state; frameState.afterRender.push(function () { primitive._ready = primitive._state === PrimitiveState$1.COMPLETE || primitive._state === PrimitiveState$1.FAILED; if (!defined(error)) { primitive._readyPromise.resolve(primitive); } else { primitive._readyPromise.reject(error); } }); } //This file is automatically rebuilt by the Cesium build process. var ShadowVolumeAppearanceFS = "#ifdef GL_EXT_frag_depth\n\ #extension GL_EXT_frag_depth : enable\n\ #endif\n\ \n\ #ifdef TEXTURE_COORDINATES\n\ #ifdef SPHERICAL\n\ varying vec4 v_sphericalExtents;\n\ #else // SPHERICAL\n\ varying vec2 v_inversePlaneExtents;\n\ varying vec4 v_westPlane;\n\ varying vec4 v_southPlane;\n\ #endif // SPHERICAL\n\ varying vec3 v_uvMinAndSphericalLongitudeRotation;\n\ varying vec3 v_uMaxAndInverseDistance;\n\ varying vec3 v_vMaxAndInverseDistance;\n\ #endif // TEXTURE_COORDINATES\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #endif\n\ \n\ #ifdef NORMAL_EC\n\ vec3 getEyeCoordinate3FromWindowCoordinate(vec2 fragCoord, float logDepthOrDepth) {\n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(fragCoord, logDepthOrDepth);\n\ return eyeCoordinate.xyz / eyeCoordinate.w;\n\ }\n\ \n\ vec3 vectorFromOffset(vec4 eyeCoordinate, vec2 positiveOffset) {\n\ vec2 glFragCoordXY = gl_FragCoord.xy;\n\ // Sample depths at both offset and negative offset\n\ float upOrRightLogDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, (glFragCoordXY + positiveOffset) / czm_viewport.zw));\n\ float downOrLeftLogDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, (glFragCoordXY - positiveOffset) / czm_viewport.zw));\n\ // Explicitly evaluate both paths\n\ // Necessary for multifrustum and for edges of the screen\n\ bvec2 upOrRightInBounds = lessThan(glFragCoordXY + positiveOffset, czm_viewport.zw);\n\ float useUpOrRight = float(upOrRightLogDepth > 0.0 && upOrRightInBounds.x && upOrRightInBounds.y);\n\ float useDownOrLeft = float(useUpOrRight == 0.0);\n\ vec3 upOrRightEC = getEyeCoordinate3FromWindowCoordinate(glFragCoordXY + positiveOffset, upOrRightLogDepth);\n\ vec3 downOrLeftEC = getEyeCoordinate3FromWindowCoordinate(glFragCoordXY - positiveOffset, downOrLeftLogDepth);\n\ return (upOrRightEC - (eyeCoordinate.xyz / eyeCoordinate.w)) * useUpOrRight + ((eyeCoordinate.xyz / eyeCoordinate.w) - downOrLeftEC) * useDownOrLeft;\n\ }\n\ #endif // NORMAL_EC\n\ \n\ void main(void)\n\ {\n\ #ifdef REQUIRES_EC\n\ float logDepthOrDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, gl_FragCoord.xy / czm_viewport.zw));\n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, logDepthOrDepth);\n\ #endif\n\ \n\ #ifdef REQUIRES_WC\n\ vec4 worldCoordinate4 = czm_inverseView * eyeCoordinate;\n\ vec3 worldCoordinate = worldCoordinate4.xyz / worldCoordinate4.w;\n\ #endif\n\ \n\ #ifdef TEXTURE_COORDINATES\n\ vec2 uv;\n\ #ifdef SPHERICAL\n\ // Treat world coords as a sphere normal for spherical coordinates\n\ vec2 sphericalLatLong = czm_approximateSphericalCoordinates(worldCoordinate);\n\ sphericalLatLong.y += v_uvMinAndSphericalLongitudeRotation.z;\n\ sphericalLatLong.y = czm_branchFreeTernary(sphericalLatLong.y < czm_pi, sphericalLatLong.y, sphericalLatLong.y - czm_twoPi);\n\ uv.x = (sphericalLatLong.y - v_sphericalExtents.y) * v_sphericalExtents.w;\n\ uv.y = (sphericalLatLong.x - v_sphericalExtents.x) * v_sphericalExtents.z;\n\ #else // SPHERICAL\n\ // Unpack planes and transform to eye space\n\ uv.x = czm_planeDistance(v_westPlane, eyeCoordinate.xyz / eyeCoordinate.w) * v_inversePlaneExtents.x;\n\ uv.y = czm_planeDistance(v_southPlane, eyeCoordinate.xyz / eyeCoordinate.w) * v_inversePlaneExtents.y;\n\ #endif // SPHERICAL\n\ #endif // TEXTURE_COORDINATES\n\ \n\ #ifdef PICK\n\ #ifdef CULL_FRAGMENTS\n\ if (0.0 <= uv.x && uv.x <= 1.0 && 0.0 <= uv.y && uv.y <= 1.0) {\n\ gl_FragColor.a = 1.0; // 0.0 alpha leads to discard from ShaderSource.createPickFragmentShaderSource\n\ czm_writeDepthClamp();\n\ }\n\ #else // CULL_FRAGMENTS\n\ gl_FragColor.a = 1.0;\n\ #endif // CULL_FRAGMENTS\n\ #else // PICK\n\ \n\ #ifdef CULL_FRAGMENTS\n\ if (uv.x <= 0.0 || 1.0 <= uv.x || uv.y <= 0.0 || 1.0 <= uv.y) {\n\ discard;\n\ }\n\ #endif\n\ \n\ #ifdef NORMAL_EC\n\ // Compute normal by sampling adjacent pixels in 2x2 block in screen space\n\ vec3 downUp = vectorFromOffset(eyeCoordinate, vec2(0.0, 1.0));\n\ vec3 leftRight = vectorFromOffset(eyeCoordinate, vec2(1.0, 0.0));\n\ vec3 normalEC = normalize(cross(leftRight, downUp));\n\ #endif\n\ \n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ \n\ vec4 color = czm_gammaCorrect(v_color);\n\ #ifdef FLAT\n\ gl_FragColor = color;\n\ #else // FLAT\n\ czm_materialInput materialInput;\n\ materialInput.normalEC = normalEC;\n\ materialInput.positionToEyeEC = -eyeCoordinate.xyz;\n\ czm_material material = czm_getDefaultMaterial(materialInput);\n\ material.diffuse = color.rgb;\n\ material.alpha = color.a;\n\ \n\ gl_FragColor = czm_phong(normalize(-eyeCoordinate.xyz), material, czm_lightDirectionEC);\n\ #endif // FLAT\n\ \n\ // Premultiply alpha. Required for classification primitives on translucent globe.\n\ gl_FragColor.rgb *= gl_FragColor.a;\n\ \n\ #else // PER_INSTANCE_COLOR\n\ \n\ // Material support.\n\ // USES_ is distinct from REQUIRES_, because some things are dependencies of each other or\n\ // dependencies for culling but might not actually be used by the material.\n\ \n\ czm_materialInput materialInput;\n\ \n\ #ifdef USES_NORMAL_EC\n\ materialInput.normalEC = normalEC;\n\ #endif\n\ \n\ #ifdef USES_POSITION_TO_EYE_EC\n\ materialInput.positionToEyeEC = -eyeCoordinate.xyz;\n\ #endif\n\ \n\ #ifdef USES_TANGENT_TO_EYE\n\ materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(worldCoordinate, normalEC);\n\ #endif\n\ \n\ #ifdef USES_ST\n\ // Remap texture coordinates from computed (approximately aligned with cartographic space) to the desired\n\ // texture coordinate system, which typically forms a tight oriented bounding box around the geometry.\n\ // Shader is provided a set of reference points for remapping.\n\ materialInput.st.x = czm_lineDistance(v_uvMinAndSphericalLongitudeRotation.xy, v_uMaxAndInverseDistance.xy, uv) * v_uMaxAndInverseDistance.z;\n\ materialInput.st.y = czm_lineDistance(v_uvMinAndSphericalLongitudeRotation.xy, v_vMaxAndInverseDistance.xy, uv) * v_vMaxAndInverseDistance.z;\n\ #endif\n\ \n\ czm_material material = czm_getMaterial(materialInput);\n\ \n\ #ifdef FLAT\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #else // FLAT\n\ gl_FragColor = czm_phong(normalize(-eyeCoordinate.xyz), material, czm_lightDirectionEC);\n\ #endif // FLAT\n\ \n\ // Premultiply alpha. Required for classification primitives on translucent globe.\n\ gl_FragColor.rgb *= gl_FragColor.a;\n\ \n\ #endif // PER_INSTANCE_COLOR\n\ czm_writeDepthClamp();\n\ #endif // PICK\n\ }\n\ "; /** * Creates shaders for a ClassificationPrimitive to use a given Appearance, as well as for picking. * * @param {Boolean} extentsCulling Discard fragments outside the instance's texture coordinate extents. * @param {Boolean} planarExtents If true, texture coordinates will be computed using planes instead of spherical coordinates. * @param {Appearance} appearance An Appearance to be used with a ClassificationPrimitive via GroundPrimitive. * @private */ function ShadowVolumeAppearance(extentsCulling, planarExtents, appearance) { //>>includeStart('debug', pragmas.debug); Check.typeOf.bool("extentsCulling", extentsCulling); Check.typeOf.bool("planarExtents", planarExtents); Check.typeOf.object("appearance", appearance); //>>includeEnd('debug'); this._projectionExtentDefines = { eastMostYhighDefine: "", eastMostYlowDefine: "", westMostYhighDefine: "", westMostYlowDefine: "", }; // Compute shader dependencies var colorShaderDependencies = new ShaderDependencies(); colorShaderDependencies.requiresTextureCoordinates = extentsCulling; colorShaderDependencies.requiresEC = !appearance.flat; var pickShaderDependencies = new ShaderDependencies(); pickShaderDependencies.requiresTextureCoordinates = extentsCulling; if (appearance instanceof PerInstanceColorAppearance) { // PerInstanceColorAppearance doesn't have material.shaderSource, instead it has its own vertex and fragment shaders colorShaderDependencies.requiresNormalEC = !appearance.flat; } else { // Scan material source for what hookups are needed. Assume czm_materialInput materialInput. var materialShaderSource = appearance.material.shaderSource + "\n" + appearance.fragmentShaderSource; colorShaderDependencies.normalEC = materialShaderSource.indexOf("materialInput.normalEC") !== -1 || materialShaderSource.indexOf("czm_getDefaultMaterial") !== -1; colorShaderDependencies.positionToEyeEC = materialShaderSource.indexOf("materialInput.positionToEyeEC") !== -1; colorShaderDependencies.tangentToEyeMatrix = materialShaderSource.indexOf("materialInput.tangentToEyeMatrix") !== -1; colorShaderDependencies.st = materialShaderSource.indexOf("materialInput.st") !== -1; } this._colorShaderDependencies = colorShaderDependencies; this._pickShaderDependencies = pickShaderDependencies; this._appearance = appearance; this._extentsCulling = extentsCulling; this._planarExtents = planarExtents; } /** * Create the fragment shader for a ClassificationPrimitive's color pass when rendering for color. * * @param {Boolean} columbusView2D Whether the shader will be used for Columbus View or 2D. * @returns {ShaderSource} Shader source for the fragment shader. */ ShadowVolumeAppearance.prototype.createFragmentShader = function ( columbusView2D ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.bool("columbusView2D", columbusView2D); //>>includeEnd('debug'); var appearance = this._appearance; var dependencies = this._colorShaderDependencies; var defines = []; if (!columbusView2D && !this._planarExtents) { defines.push("SPHERICAL"); } if (dependencies.requiresEC) { defines.push("REQUIRES_EC"); } if (dependencies.requiresWC) { defines.push("REQUIRES_WC"); } if (dependencies.requiresTextureCoordinates) { defines.push("TEXTURE_COORDINATES"); } if (this._extentsCulling) { defines.push("CULL_FRAGMENTS"); } if (dependencies.requiresNormalEC) { defines.push("NORMAL_EC"); } if (appearance instanceof PerInstanceColorAppearance) { defines.push("PER_INSTANCE_COLOR"); } // Material inputs. Use of parameters in the material is different // from requirement of the parameters in the overall shader, for example, // texture coordinates may be used for fragment culling but not for the material itself. if (dependencies.normalEC) { defines.push("USES_NORMAL_EC"); } if (dependencies.positionToEyeEC) { defines.push("USES_POSITION_TO_EYE_EC"); } if (dependencies.tangentToEyeMatrix) { defines.push("USES_TANGENT_TO_EYE"); } if (dependencies.st) { defines.push("USES_ST"); } if (appearance.flat) { defines.push("FLAT"); } var materialSource = ""; if (!(appearance instanceof PerInstanceColorAppearance)) { materialSource = appearance.material.shaderSource; } return new ShaderSource({ defines: defines, sources: [materialSource, ShadowVolumeAppearanceFS], }); }; ShadowVolumeAppearance.prototype.createPickFragmentShader = function ( columbusView2D ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.bool("columbusView2D", columbusView2D); //>>includeEnd('debug'); var dependencies = this._pickShaderDependencies; var defines = ["PICK"]; if (!columbusView2D && !this._planarExtents) { defines.push("SPHERICAL"); } if (dependencies.requiresEC) { defines.push("REQUIRES_EC"); } if (dependencies.requiresWC) { defines.push("REQUIRES_WC"); } if (dependencies.requiresTextureCoordinates) { defines.push("TEXTURE_COORDINATES"); } if (this._extentsCulling) { defines.push("CULL_FRAGMENTS"); } return new ShaderSource({ defines: defines, sources: [ShadowVolumeAppearanceFS], pickColorQualifier: "varying", }); }; /** * Create the vertex shader for a ClassificationPrimitive's color pass on the final of 3 shadow volume passes * * @param {String[]} defines External defines to pass to the vertex shader. * @param {String} vertexShaderSource ShadowVolumeAppearanceVS with any required modifications for computing position. * @param {Boolean} columbusView2D Whether the shader will be used for Columbus View or 2D. * @param {MapProjection} mapProjection Current scene's map projection. * @returns {String} Shader source for the vertex shader. */ ShadowVolumeAppearance.prototype.createVertexShader = function ( defines, vertexShaderSource, columbusView2D, mapProjection ) { //>>includeStart('debug', pragmas.debug); Check.defined("defines", defines); Check.typeOf.string("vertexShaderSource", vertexShaderSource); Check.typeOf.bool("columbusView2D", columbusView2D); Check.defined("mapProjection", mapProjection); //>>includeEnd('debug'); return createShadowVolumeAppearanceVS( this._colorShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, this._appearance, mapProjection, this._projectionExtentDefines ); }; /** * Create the vertex shader for a ClassificationPrimitive's pick pass on the final of 3 shadow volume passes * * @param {String[]} defines External defines to pass to the vertex shader. * @param {String} vertexShaderSource ShadowVolumeAppearanceVS with any required modifications for computing position and picking. * @param {Boolean} columbusView2D Whether the shader will be used for Columbus View or 2D. * @param {MapProjection} mapProjection Current scene's map projection. * @returns {String} Shader source for the vertex shader. */ ShadowVolumeAppearance.prototype.createPickVertexShader = function ( defines, vertexShaderSource, columbusView2D, mapProjection ) { //>>includeStart('debug', pragmas.debug); Check.defined("defines", defines); Check.typeOf.string("vertexShaderSource", vertexShaderSource); Check.typeOf.bool("columbusView2D", columbusView2D); Check.defined("mapProjection", mapProjection); //>>includeEnd('debug'); return createShadowVolumeAppearanceVS( this._pickShaderDependencies, this._planarExtents, columbusView2D, defines, vertexShaderSource, undefined, mapProjection, this._projectionExtentDefines ); }; var longitudeExtentsCartesianScratch = new Cartesian3(); var longitudeExtentsCartographicScratch = new Cartographic(); var longitudeExtentsEncodeScratch = { high: 0.0, low: 0.0, }; function createShadowVolumeAppearanceVS( shaderDependencies, planarExtents, columbusView2D, defines, vertexShaderSource, appearance, mapProjection, projectionExtentDefines ) { var allDefines = defines.slice(); if (projectionExtentDefines.eastMostYhighDefine === "") { var eastMostCartographic = longitudeExtentsCartographicScratch; eastMostCartographic.longitude = CesiumMath.PI; eastMostCartographic.latitude = 0.0; eastMostCartographic.height = 0.0; var eastMostCartesian = mapProjection.project( eastMostCartographic, longitudeExtentsCartesianScratch ); var encoded = EncodedCartesian3.encode( eastMostCartesian.x, longitudeExtentsEncodeScratch ); projectionExtentDefines.eastMostYhighDefine = "EAST_MOST_X_HIGH " + encoded.high.toFixed((encoded.high + "").length + 1); projectionExtentDefines.eastMostYlowDefine = "EAST_MOST_X_LOW " + encoded.low.toFixed((encoded.low + "").length + 1); var westMostCartographic = longitudeExtentsCartographicScratch; westMostCartographic.longitude = -CesiumMath.PI; westMostCartographic.latitude = 0.0; westMostCartographic.height = 0.0; var westMostCartesian = mapProjection.project( westMostCartographic, longitudeExtentsCartesianScratch ); encoded = EncodedCartesian3.encode( westMostCartesian.x, longitudeExtentsEncodeScratch ); projectionExtentDefines.westMostYhighDefine = "WEST_MOST_X_HIGH " + encoded.high.toFixed((encoded.high + "").length + 1); projectionExtentDefines.westMostYlowDefine = "WEST_MOST_X_LOW " + encoded.low.toFixed((encoded.low + "").length + 1); } if (columbusView2D) { allDefines.push(projectionExtentDefines.eastMostYhighDefine); allDefines.push(projectionExtentDefines.eastMostYlowDefine); allDefines.push(projectionExtentDefines.westMostYhighDefine); allDefines.push(projectionExtentDefines.westMostYlowDefine); } if (defined(appearance) && appearance instanceof PerInstanceColorAppearance) { allDefines.push("PER_INSTANCE_COLOR"); } if (shaderDependencies.requiresTextureCoordinates) { allDefines.push("TEXTURE_COORDINATES"); if (!(planarExtents || columbusView2D)) { allDefines.push("SPHERICAL"); } if (columbusView2D) { allDefines.push("COLUMBUS_VIEW_2D"); } } return new ShaderSource({ defines: allDefines, sources: [vertexShaderSource], }); } /** * Tracks shader dependencies. * @private */ function ShaderDependencies() { this._requiresEC = false; this._requiresWC = false; // depends on eye coordinates, needed for material and for phong this._requiresNormalEC = false; // depends on eye coordinates, needed for material this._requiresTextureCoordinates = false; // depends on world coordinates, needed for material and for culling this._usesNormalEC = false; this._usesPositionToEyeEC = false; this._usesTangentToEyeMat = false; this._usesSt = false; } Object.defineProperties(ShaderDependencies.prototype, { // Set when assessing final shading (flat vs. phong) and culling using computed texture coordinates requiresEC: { get: function () { return this._requiresEC; }, set: function (value) { this._requiresEC = value || this._requiresEC; }, }, requiresWC: { get: function () { return this._requiresWC; }, set: function (value) { this._requiresWC = value || this._requiresWC; this.requiresEC = this._requiresWC; }, }, requiresNormalEC: { get: function () { return this._requiresNormalEC; }, set: function (value) { this._requiresNormalEC = value || this._requiresNormalEC; this.requiresEC = this._requiresNormalEC; }, }, requiresTextureCoordinates: { get: function () { return this._requiresTextureCoordinates; }, set: function (value) { this._requiresTextureCoordinates = value || this._requiresTextureCoordinates; this.requiresWC = this._requiresTextureCoordinates; }, }, // Get/Set when assessing material hookups normalEC: { set: function (value) { this.requiresNormalEC = value; this._usesNormalEC = value; }, get: function () { return this._usesNormalEC; }, }, tangentToEyeMatrix: { set: function (value) { this.requiresWC = value; this.requiresNormalEC = value; this._usesTangentToEyeMat = value; }, get: function () { return this._usesTangentToEyeMat; }, }, positionToEyeEC: { set: function (value) { this.requiresEC = value; this._usesPositionToEyeEC = value; }, get: function () { return this._usesPositionToEyeEC; }, }, st: { set: function (value) { this.requiresTextureCoordinates = value; this._usesSt = value; }, get: function () { return this._usesSt; }, }, }); function pointLineDistance(point1, point2, point) { return ( Math.abs( (point2.y - point1.y) * point.x - (point2.x - point1.x) * point.y + point2.x * point1.y - point2.y * point1.x ) / Cartesian2.distance(point2, point1) ); } var points2DScratch = [ new Cartesian2(), new Cartesian2(), new Cartesian2(), new Cartesian2(), ]; // textureCoordinateRotationPoints form 2 lines in the computed UV space that remap to desired texture coordinates. // This allows simulation of baked texture coordinates for EllipseGeometry, RectangleGeometry, and PolygonGeometry. function addTextureCoordinateRotationAttributes( attributes, textureCoordinateRotationPoints ) { var points2D = points2DScratch; var minXYCorner = Cartesian2.unpack( textureCoordinateRotationPoints, 0, points2D[0] ); var maxYCorner = Cartesian2.unpack( textureCoordinateRotationPoints, 2, points2D[1] ); var maxXCorner = Cartesian2.unpack( textureCoordinateRotationPoints, 4, points2D[2] ); attributes.uMaxVmax = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, value: [maxYCorner.x, maxYCorner.y, maxXCorner.x, maxXCorner.y], }); var inverseExtentX = 1.0 / pointLineDistance(minXYCorner, maxYCorner, maxXCorner); var inverseExtentY = 1.0 / pointLineDistance(minXYCorner, maxXCorner, maxYCorner); attributes.uvMinAndExtents = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, value: [minXYCorner.x, minXYCorner.y, inverseExtentX, inverseExtentY], }); } var cartographicScratch$3 = new Cartographic(); var cornerScratch = new Cartesian3(); var northWestScratch = new Cartesian3(); var southEastScratch = new Cartesian3(); var highLowScratch = { high: 0.0, low: 0.0 }; function add2DTextureCoordinateAttributes(rectangle, projection, attributes) { // Compute corner positions in double precision var carto = cartographicScratch$3; carto.height = 0.0; carto.longitude = rectangle.west; carto.latitude = rectangle.south; var southWestCorner = projection.project(carto, cornerScratch); carto.latitude = rectangle.north; var northWest = projection.project(carto, northWestScratch); carto.longitude = rectangle.east; carto.latitude = rectangle.south; var southEast = projection.project(carto, southEastScratch); // Since these positions are all in the 2D plane, there's a lot of zeros // and a lot of repetition. So we only need to encode 4 values. // Encode: // x: x value for southWestCorner // y: y value for southWestCorner // z: y value for northWest // w: x value for southEast var valuesHigh = [0, 0, 0, 0]; var valuesLow = [0, 0, 0, 0]; var encoded = EncodedCartesian3.encode(southWestCorner.x, highLowScratch); valuesHigh[0] = encoded.high; valuesLow[0] = encoded.low; encoded = EncodedCartesian3.encode(southWestCorner.y, highLowScratch); valuesHigh[1] = encoded.high; valuesLow[1] = encoded.low; encoded = EncodedCartesian3.encode(northWest.y, highLowScratch); valuesHigh[2] = encoded.high; valuesLow[2] = encoded.low; encoded = EncodedCartesian3.encode(southEast.x, highLowScratch); valuesHigh[3] = encoded.high; valuesLow[3] = encoded.low; attributes.planes2D_HIGH = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, value: valuesHigh, }); attributes.planes2D_LOW = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, value: valuesLow, }); } var enuMatrixScratch = new Matrix4(); var inverseEnuScratch = new Matrix4(); var rectanglePointCartesianScratch = new Cartesian3(); var rectangleCenterScratch$1 = new Cartographic(); var pointsCartographicScratch = [ new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), new Cartographic(), ]; /** * When computing planes to bound the rectangle, * need to factor in "bulge" and other distortion. * Flatten the ellipsoid-centered corners and edge-centers of the rectangle * into the plane of the local ENU system, compute bounds in 2D, and * project back to ellipsoid-centered. * * @private */ function computeRectangleBounds( rectangle, ellipsoid, height, southWestCornerResult, eastVectorResult, northVectorResult ) { // Compute center of rectangle var centerCartographic = Rectangle.center(rectangle, rectangleCenterScratch$1); centerCartographic.height = height; var centerCartesian = Cartographic.toCartesian( centerCartographic, ellipsoid, rectanglePointCartesianScratch ); var enuMatrix = Transforms.eastNorthUpToFixedFrame( centerCartesian, ellipsoid, enuMatrixScratch ); var inverseEnu = Matrix4.inverse(enuMatrix, inverseEnuScratch); var west = rectangle.west; var east = rectangle.east; var north = rectangle.north; var south = rectangle.south; var cartographics = pointsCartographicScratch; cartographics[0].latitude = south; cartographics[0].longitude = west; cartographics[1].latitude = north; cartographics[1].longitude = west; cartographics[2].latitude = north; cartographics[2].longitude = east; cartographics[3].latitude = south; cartographics[3].longitude = east; var longitudeCenter = (west + east) * 0.5; var latitudeCenter = (north + south) * 0.5; cartographics[4].latitude = south; cartographics[4].longitude = longitudeCenter; cartographics[5].latitude = north; cartographics[5].longitude = longitudeCenter; cartographics[6].latitude = latitudeCenter; cartographics[6].longitude = west; cartographics[7].latitude = latitudeCenter; cartographics[7].longitude = east; var minX = Number.POSITIVE_INFINITY; var maxX = Number.NEGATIVE_INFINITY; var minY = Number.POSITIVE_INFINITY; var maxY = Number.NEGATIVE_INFINITY; for (var i = 0; i < 8; i++) { cartographics[i].height = height; var pointCartesian = Cartographic.toCartesian( cartographics[i], ellipsoid, rectanglePointCartesianScratch ); Matrix4.multiplyByPoint(inverseEnu, pointCartesian, pointCartesian); pointCartesian.z = 0.0; // flatten into XY plane of ENU coordinate system minX = Math.min(minX, pointCartesian.x); maxX = Math.max(maxX, pointCartesian.x); minY = Math.min(minY, pointCartesian.y); maxY = Math.max(maxY, pointCartesian.y); } var southWestCorner = southWestCornerResult; southWestCorner.x = minX; southWestCorner.y = minY; southWestCorner.z = 0.0; Matrix4.multiplyByPoint(enuMatrix, southWestCorner, southWestCorner); var southEastCorner = eastVectorResult; southEastCorner.x = maxX; southEastCorner.y = minY; southEastCorner.z = 0.0; Matrix4.multiplyByPoint(enuMatrix, southEastCorner, southEastCorner); // make eastward vector Cartesian3.subtract(southEastCorner, southWestCorner, eastVectorResult); var northWestCorner = northVectorResult; northWestCorner.x = minX; northWestCorner.y = maxY; northWestCorner.z = 0.0; Matrix4.multiplyByPoint(enuMatrix, northWestCorner, northWestCorner); // make eastward vector Cartesian3.subtract(northWestCorner, southWestCorner, northVectorResult); } var eastwardScratch = new Cartesian3(); var northwardScratch = new Cartesian3(); var encodeScratch = new EncodedCartesian3(); /** * Gets an attributes object containing: * - 3 high-precision points as 6 GeometryInstanceAttributes. These points are used to compute eye-space planes. * - 1 texture coordinate rotation GeometryInstanceAttributes * - 2 GeometryInstanceAttributes used to compute high-precision points in 2D and Columbus View. * These points are used to compute eye-space planes like above. * * Used to compute texture coordinates for small-area ClassificationPrimitives with materials or multiple non-overlapping instances. * * @see ShadowVolumeAppearance * @private * * @param {Rectangle} boundingRectangle Rectangle object that the points will approximately bound * @param {Number[]} textureCoordinateRotationPoints Points in the computed texture coordinate system for remapping texture coordinates * @param {Ellipsoid} ellipsoid Ellipsoid for converting Rectangle points to world coordinates * @param {MapProjection} projection The MapProjection used for 2D and Columbus View. * @param {Number} [height=0] The maximum height for the shadow volume. * @returns {Object} An attributes dictionary containing planar texture coordinate attributes. */ ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes = function ( boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection, height ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("boundingRectangle", boundingRectangle); Check.defined( "textureCoordinateRotationPoints", textureCoordinateRotationPoints ); Check.typeOf.object("ellipsoid", ellipsoid); Check.typeOf.object("projection", projection); //>>includeEnd('debug'); var corner = cornerScratch; var eastward = eastwardScratch; var northward = northwardScratch; computeRectangleBounds( boundingRectangle, ellipsoid, defaultValue(height, 0.0), corner, eastward, northward ); var attributes = {}; addTextureCoordinateRotationAttributes( attributes, textureCoordinateRotationPoints ); var encoded = EncodedCartesian3.fromCartesian(corner, encodeScratch); attributes.southWest_HIGH = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, normalize: false, value: Cartesian3.pack(encoded.high, [0, 0, 0]), }); attributes.southWest_LOW = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, normalize: false, value: Cartesian3.pack(encoded.low, [0, 0, 0]), }); attributes.eastward = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, normalize: false, value: Cartesian3.pack(eastward, [0, 0, 0]), }); attributes.northward = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, normalize: false, value: Cartesian3.pack(northward, [0, 0, 0]), }); add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes); return attributes; }; var spherePointScratch = new Cartesian3(); function latLongToSpherical(latitude, longitude, ellipsoid, result) { var cartographic = cartographicScratch$3; cartographic.latitude = latitude; cartographic.longitude = longitude; cartographic.height = 0.0; var spherePoint = Cartographic.toCartesian( cartographic, ellipsoid, spherePointScratch ); // Project into plane with vertical for latitude var magXY = Math.sqrt( spherePoint.x * spherePoint.x + spherePoint.y * spherePoint.y ); // Use fastApproximateAtan2 for alignment with shader var sphereLatitude = CesiumMath.fastApproximateAtan2(magXY, spherePoint.z); var sphereLongitude = CesiumMath.fastApproximateAtan2( spherePoint.x, spherePoint.y ); result.x = sphereLatitude; result.y = sphereLongitude; return result; } var sphericalScratch = new Cartesian2(); /** * Gets an attributes object containing: * - the southwest corner of a rectangular area in spherical coordinates, as well as the inverse of the latitude/longitude range. * These are computed using the same atan2 approximation used in the shader. * - 1 texture coordinate rotation GeometryInstanceAttributes * - 2 GeometryInstanceAttributes used to compute high-precision points in 2D and Columbus View. * These points are used to compute eye-space planes like above. * * Used when computing texture coordinates for large-area ClassificationPrimitives with materials or * multiple non-overlapping instances. * @see ShadowVolumeAppearance * @private * * @param {Rectangle} boundingRectangle Rectangle object that the spherical extents will approximately bound * @param {Number[]} textureCoordinateRotationPoints Points in the computed texture coordinate system for remapping texture coordinates * @param {Ellipsoid} ellipsoid Ellipsoid for converting Rectangle points to world coordinates * @param {MapProjection} projection The MapProjection used for 2D and Columbus View. * @returns {Object} An attributes dictionary containing spherical texture coordinate attributes. */ ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes = function ( boundingRectangle, textureCoordinateRotationPoints, ellipsoid, projection ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("boundingRectangle", boundingRectangle); Check.defined( "textureCoordinateRotationPoints", textureCoordinateRotationPoints ); Check.typeOf.object("ellipsoid", ellipsoid); Check.typeOf.object("projection", projection); //>>includeEnd('debug'); // rectangle cartographic coords !== spherical because it's on an ellipsoid var southWestExtents = latLongToSpherical( boundingRectangle.south, boundingRectangle.west, ellipsoid, sphericalScratch ); var south = southWestExtents.x; var west = southWestExtents.y; var northEastExtents = latLongToSpherical( boundingRectangle.north, boundingRectangle.east, ellipsoid, sphericalScratch ); var north = northEastExtents.x; var east = northEastExtents.y; // If the bounding rectangle crosses the IDL, rotate the spherical extents so the cross no longer happens. // This rotation must happen in the shader too. var rotationRadians = 0.0; if (west > east) { rotationRadians = CesiumMath.PI - west; west = -CesiumMath.PI; east += rotationRadians; } // Slightly pad extents to avoid floating point error when fragment culling at edges. south -= CesiumMath.EPSILON5; west -= CesiumMath.EPSILON5; north += CesiumMath.EPSILON5; east += CesiumMath.EPSILON5; var longitudeRangeInverse = 1.0 / (east - west); var latitudeRangeInverse = 1.0 / (north - south); var attributes = { sphericalExtents: new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, normalize: false, value: [south, west, latitudeRangeInverse, longitudeRangeInverse], }), longitudeRotation: new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 1, normalize: false, value: [rotationRadians], }), }; addTextureCoordinateRotationAttributes( attributes, textureCoordinateRotationPoints ); add2DTextureCoordinateAttributes(boundingRectangle, projection, attributes); return attributes; }; ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes = function ( attributes ) { return ( defined(attributes.southWest_HIGH) && defined(attributes.southWest_LOW) && defined(attributes.northward) && defined(attributes.eastward) && defined(attributes.planes2D_HIGH) && defined(attributes.planes2D_LOW) && defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents) ); }; ShadowVolumeAppearance.hasAttributesForSphericalExtents = function ( attributes ) { return ( defined(attributes.sphericalExtents) && defined(attributes.longitudeRotation) && defined(attributes.planes2D_HIGH) && defined(attributes.planes2D_LOW) && defined(attributes.uMaxVmax) && defined(attributes.uvMinAndExtents) ); }; function shouldUseSpherical(rectangle) { return ( Math.max(rectangle.width, rectangle.height) > ShadowVolumeAppearance.MAX_WIDTH_FOR_PLANAR_EXTENTS ); } /** * Computes whether the given rectangle is wide enough that texture coordinates * over its area should be computed using spherical extents instead of distance to planes. * * @param {Rectangle} rectangle A rectangle * @private */ ShadowVolumeAppearance.shouldUseSphericalCoordinates = function (rectangle) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("rectangle", rectangle); //>>includeEnd('debug'); return shouldUseSpherical(rectangle); }; /** * Texture coordinates for ground primitives are computed either using spherical coordinates for large areas or * using distance from planes for small areas. * * @type {Number} * @constant * @private */ ShadowVolumeAppearance.MAX_WIDTH_FOR_PLANAR_EXTENTS = CesiumMath.toRadians(1.0); /** * Determines the function used to compare stencil values for the stencil test. * * @enum {Number} */ var StencilFunction = { /** * The stencil test never passes. * * @type {Number} * @constant */ NEVER: WebGLConstants$1.NEVER, /** * The stencil test passes when the masked reference value is less than the masked stencil value. * * @type {Number} * @constant */ LESS: WebGLConstants$1.LESS, /** * The stencil test passes when the masked reference value is equal to the masked stencil value. * * @type {Number} * @constant */ EQUAL: WebGLConstants$1.EQUAL, /** * The stencil test passes when the masked reference value is less than or equal to the masked stencil value. * * @type {Number} * @constant */ LESS_OR_EQUAL: WebGLConstants$1.LEQUAL, /** * The stencil test passes when the masked reference value is greater than the masked stencil value. * * @type {Number} * @constant */ GREATER: WebGLConstants$1.GREATER, /** * The stencil test passes when the masked reference value is not equal to the masked stencil value. * * @type {Number} * @constant */ NOT_EQUAL: WebGLConstants$1.NOTEQUAL, /** * The stencil test passes when the masked reference value is greater than or equal to the masked stencil value. * * @type {Number} * @constant */ GREATER_OR_EQUAL: WebGLConstants$1.GEQUAL, /** * The stencil test always passes. * * @type {Number} * @constant */ ALWAYS: WebGLConstants$1.ALWAYS, }; var StencilFunction$1 = Object.freeze(StencilFunction); /** * Determines the action taken based on the result of the stencil test. * * @enum {Number} */ var StencilOperation = { /** * Sets the stencil buffer value to zero. * * @type {Number} * @constant */ ZERO: WebGLConstants$1.ZERO, /** * Does not change the stencil buffer. * * @type {Number} * @constant */ KEEP: WebGLConstants$1.KEEP, /** * Replaces the stencil buffer value with the reference value. * * @type {Number} * @constant */ REPLACE: WebGLConstants$1.REPLACE, /** * Increments the stencil buffer value, clamping to unsigned byte. * * @type {Number} * @constant */ INCREMENT: WebGLConstants$1.INCR, /** * Decrements the stencil buffer value, clamping to zero. * * @type {Number} * @constant */ DECREMENT: WebGLConstants$1.DECR, /** * Bitwise inverts the existing stencil buffer value. * * @type {Number} * @constant */ INVERT: WebGLConstants$1.INVERT, /** * Increments the stencil buffer value, wrapping to zero when exceeding the unsigned byte range. * * @type {Number} * @constant */ INCREMENT_WRAP: WebGLConstants$1.INCR_WRAP, /** * Decrements the stencil buffer value, wrapping to the maximum unsigned byte instead of going below zero. * * @type {Number} * @constant */ DECREMENT_WRAP: WebGLConstants$1.DECR_WRAP, }; var StencilOperation$1 = Object.freeze(StencilOperation); /** * The most significant bit is used to identify whether the pixel is 3D Tiles. * The next three bits store selection depth for the skip LODs optimization. * The last four bits are for increment/decrement shadow volume operations for classification. * * @private */ var StencilConstants = { CESIUM_3D_TILE_MASK: 0x80, SKIP_LOD_MASK: 0x70, SKIP_LOD_BIT_SHIFT: 4, CLASSIFICATION_MASK: 0x0f, }; StencilConstants.setCesium3DTileBit = function () { return { enabled: true, frontFunction: StencilFunction$1.ALWAYS, frontOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.KEEP, zPass: StencilOperation$1.REPLACE, }, backFunction: StencilFunction$1.ALWAYS, backOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.KEEP, zPass: StencilOperation$1.REPLACE, }, reference: StencilConstants.CESIUM_3D_TILE_MASK, mask: StencilConstants.CESIUM_3D_TILE_MASK, }; }; var StencilConstants$1 = Object.freeze(StencilConstants); /** * A classification primitive represents a volume enclosing geometry in the {@link Scene} to be highlighted. *

* A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement, * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix * and match most of them and add a new geometry or appearance independently of each other. * Only {@link PerInstanceColorAppearance} with the same color across all instances is supported at this time when using * ClassificationPrimitive directly. * For full {@link Appearance} support when classifying terrain or 3D Tiles use {@link GroundPrimitive} instead. *

*

* For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there * will be rendering artifacts for some viewing angles. *

*

* Valid geometries are {@link BoxGeometry}, {@link CylinderGeometry}, {@link EllipsoidGeometry}, {@link PolylineVolumeGeometry}, and {@link SphereGeometry}. *

*

* Geometries that follow the surface of the ellipsoid, such as {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}, * are also valid if they are extruded volumes; otherwise, they will not be rendered. *

* * @alias ClassificationPrimitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. This can either be a single instance or an array of length one. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to PerInstanceColorAppearance when GeometryInstances have a color attribute. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Boolean} [options.vertexCacheOptimize=false] When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * @param {Boolean} [options.interleave=false] When true, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.compressVertices=true] When true, the geometry vertices are compressed, which will save memory. * @param {Boolean} [options.releaseGeometryInstances=true] When true, the primitive does not keep a reference to the input geometryInstances to save memory. * @param {Boolean} [options.allowPicking=true] When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be true on * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be false. * * @see Primitive * @see GroundPrimitive * @see GeometryInstance * @see Appearance */ function ClassificationPrimitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var geometryInstances = options.geometryInstances; /** * The geometry instance rendered with this primitive. This may * be undefined if options.releaseGeometryInstances * is true when the primitive is constructed. *

* Changing this property after the primitive is rendered has no effect. *

*

* Because of the rendering technique used, all geometry instances must be the same color. * If there is an instance with a differing color, a DeveloperError will be thrown * on the first attempt to render. *

* * @readonly * @type {Array|GeometryInstance} * * @default undefined */ this.geometryInstances = geometryInstances; /** * Determines if the primitive will be shown. This affects all geometry * instances in the primitive. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * Determines whether terrain, 3D Tiles or both will be classified. * * @type {ClassificationType} * * @default ClassificationType.BOTH */ this.classificationType = defaultValue( options.classificationType, ClassificationType$1.BOTH ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the shadow volume for each geometry in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowShadowVolume = defaultValue( options.debugShowShadowVolume, false ); this._debugShowShadowVolume = false; // These are used by GroundPrimitive to augment the shader and uniform map. this._extruded = defaultValue(options._extruded, false); this._uniformMap = options._uniformMap; this._sp = undefined; this._spStencil = undefined; this._spPick = undefined; this._spColor = undefined; this._spPick2D = undefined; // only derived if necessary this._spColor2D = undefined; // only derived if necessary this._rsStencilDepthPass = undefined; this._rsStencilDepthPass3DTiles = undefined; this._rsColorPass = undefined; this._rsPickPass = undefined; this._commandsIgnoreShow = []; this._ready = false; this._readyPromise = when.defer(); this._primitive = undefined; this._pickPrimitive = options._pickPrimitive; // Set in update this._hasSphericalExtentsAttribute = false; this._hasPlanarExtentsAttributes = false; this._hasPerColorAttribute = false; this.appearance = options.appearance; this._createBoundingVolumeFunction = options._createBoundingVolumeFunction; this._updateAndQueueCommandsFunction = options._updateAndQueueCommandsFunction; this._usePickOffsets = false; this._primitiveOptions = { geometryInstances: undefined, appearance: undefined, vertexCacheOptimize: defaultValue(options.vertexCacheOptimize, false), interleave: defaultValue(options.interleave, false), releaseGeometryInstances: defaultValue( options.releaseGeometryInstances, true ), allowPicking: defaultValue(options.allowPicking, true), asynchronous: defaultValue(options.asynchronous, true), compressVertices: defaultValue(options.compressVertices, true), _createBoundingVolumeFunction: undefined, _createRenderStatesFunction: undefined, _createShaderProgramFunction: undefined, _createCommandsFunction: undefined, _updateAndQueueCommandsFunction: undefined, _createPickOffsets: true, }; } Object.defineProperties(ClassificationPrimitive.prototype, { /** * When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ vertexCacheOptimize: { get: function () { return this._primitiveOptions.vertexCacheOptimize; }, }, /** * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default false */ interleave: { get: function () { return this._primitiveOptions.interleave; }, }, /** * When true, the primitive does not keep a reference to the input geometryInstances to save memory. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ releaseGeometryInstances: { get: function () { return this._primitiveOptions.releaseGeometryInstances; }, }, /** * When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking: { get: function () { return this._primitiveOptions.allowPicking; }, }, /** * Determines if the geometry instances will be created and batched on a web worker. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous: { get: function () { return this._primitiveOptions.asynchronous; }, }, /** * When true, geometry vertices are compressed, which will save memory. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ compressVertices: { get: function () { return this._primitiveOptions.compressVertices; }, }, /** * Determines if the primitive is complete and ready to render. If this property is * true, the primitive will be rendered the next time that {@link ClassificationPrimitive#update} * is called. * * @memberof ClassificationPrimitive.prototype * * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof ClassificationPrimitive.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * Returns true if the ClassificationPrimitive needs a separate shader and commands for 2D. * This is because texture coordinates on ClassificationPrimitives are computed differently, * and are used for culling when multiple GeometryInstances are batched in one ClassificationPrimitive. * @memberof ClassificationPrimitive.prototype * @type {Boolean} * @readonly * @private */ _needs2DShader: { get: function () { return ( this._hasPlanarExtentsAttributes || this._hasSphericalExtentsAttribute ); }, }, }); /** * Determines if ClassificationPrimitive rendering is supported. * * @param {Scene} scene The scene. * @returns {Boolean} true if ClassificationPrimitives are supported; otherwise, returns false */ ClassificationPrimitive.isSupported = function (scene) { return scene.context.stencilBuffer; }; function getStencilDepthRenderState$1(enableStencil, mask3DTiles) { var stencilFunction = mask3DTiles ? StencilFunction$1.EQUAL : StencilFunction$1.ALWAYS; return { colorMask: { red: false, green: false, blue: false, alpha: false, }, stencilTest: { enabled: enableStencil, frontFunction: stencilFunction, frontOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.DECREMENT_WRAP, zPass: StencilOperation$1.KEEP, }, backFunction: stencilFunction, backOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.INCREMENT_WRAP, zPass: StencilOperation$1.KEEP, }, reference: StencilConstants$1.CESIUM_3D_TILE_MASK, mask: StencilConstants$1.CESIUM_3D_TILE_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: true, func: DepthFunction$1.LESS_OR_EQUAL, }, depthMask: false, }; } function getColorRenderState(enableStencil) { return { stencilTest: { enabled: enableStencil, frontFunction: StencilFunction$1.NOT_EQUAL, frontOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, backFunction: StencilFunction$1.NOT_EQUAL, backOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, reference: 0, mask: StencilConstants$1.CLASSIFICATION_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: false, }, depthMask: false, blending: BlendingState$1.PRE_MULTIPLIED_ALPHA_BLEND, }; } var pickRenderState$1 = { stencilTest: { enabled: true, frontFunction: StencilFunction$1.NOT_EQUAL, frontOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, backFunction: StencilFunction$1.NOT_EQUAL, backOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, reference: 0, mask: StencilConstants$1.CLASSIFICATION_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: false, }, depthMask: false, }; function createRenderStates$4( classificationPrimitive, context, appearance, twoPasses ) { if (defined(classificationPrimitive._rsStencilDepthPass)) { return; } var stencilEnabled = !classificationPrimitive.debugShowShadowVolume; classificationPrimitive._rsStencilDepthPass = RenderState.fromCache( getStencilDepthRenderState$1(stencilEnabled, false) ); classificationPrimitive._rsStencilDepthPass3DTiles = RenderState.fromCache( getStencilDepthRenderState$1(stencilEnabled, true) ); classificationPrimitive._rsColorPass = RenderState.fromCache( getColorRenderState(stencilEnabled) ); classificationPrimitive._rsPickPass = RenderState.fromCache(pickRenderState$1); } function modifyForEncodedNormals(primitive, vertexShaderSource) { if (!primitive.compressVertices) { return vertexShaderSource; } if ( vertexShaderSource.search(/attribute\s+vec3\s+extrudeDirection;/g) !== -1 ) { var attributeName = "compressedAttributes"; //only shadow volumes use extrudeDirection, and shadow volumes use vertexFormat: POSITION_ONLY so we don't need to check other attributes var attributeDecl = "attribute vec2 " + attributeName + ";"; var globalDecl = "vec3 extrudeDirection;\n"; var decode = " extrudeDirection = czm_octDecode(" + attributeName + ", 65535.0);\n"; var modifiedVS = vertexShaderSource; modifiedVS = modifiedVS.replace( /attribute\s+vec3\s+extrudeDirection;/g, "" ); modifiedVS = ShaderSource.replaceMain( modifiedVS, "czm_non_compressed_main" ); var compressedMain = "void main() \n" + "{ \n" + decode + " czm_non_compressed_main(); \n" + "}"; return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n"); } } function createShaderProgram$1(classificationPrimitive, frameState) { var context = frameState.context; var primitive = classificationPrimitive._primitive; var vs = ShadowVolumeAppearanceVS; vs = classificationPrimitive._primitive._batchTable.getVertexShaderCallback()( vs ); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs); vs = Primitive._modifyShaderPosition( classificationPrimitive, vs, frameState.scene3DOnly ); vs = Primitive._updateColorAttribute(primitive, vs); var planarExtents = classificationPrimitive._hasPlanarExtentsAttributes; var cullFragmentsUsingExtents = planarExtents || classificationPrimitive._hasSphericalExtentsAttribute; if (classificationPrimitive._extruded) { vs = modifyForEncodedNormals(primitive, vs); } var extrudedDefine = classificationPrimitive._extruded ? "EXTRUDED_GEOMETRY" : ""; var vsSource = new ShaderSource({ defines: [extrudedDefine], sources: [vs], }); var fsSource = new ShaderSource({ sources: [ShadowVolumeFS], }); var attributeLocations = classificationPrimitive._primitive._attributeLocations; var shadowVolumeAppearance = new ShadowVolumeAppearance( cullFragmentsUsingExtents, planarExtents, classificationPrimitive.appearance ); classificationPrimitive._spStencil = ShaderProgram.replaceCache({ context: context, shaderProgram: classificationPrimitive._spStencil, vertexShaderSource: vsSource, fragmentShaderSource: fsSource, attributeLocations: attributeLocations, }); if (classificationPrimitive._primitive.allowPicking) { var vsPick = ShaderSource.createPickVertexShaderSource(vs); vsPick = Primitive._appendShowToShader(primitive, vsPick); vsPick = Primitive._updatePickColorAttribute(vsPick); var pickFS3D = shadowVolumeAppearance.createPickFragmentShader(false); var pickVS3D = shadowVolumeAppearance.createPickVertexShader( [extrudedDefine], vsPick, false, frameState.mapProjection ); classificationPrimitive._spPick = ShaderProgram.replaceCache({ context: context, shaderProgram: classificationPrimitive._spPick, vertexShaderSource: pickVS3D, fragmentShaderSource: pickFS3D, attributeLocations: attributeLocations, }); // Derive a 2D pick shader if the primitive uses texture coordinate-based fragment culling, // since texture coordinates are computed differently in 2D. if (cullFragmentsUsingExtents) { var pickProgram2D = context.shaderCache.getDerivedShaderProgram( classificationPrimitive._spPick, "2dPick" ); if (!defined(pickProgram2D)) { var pickFS2D = shadowVolumeAppearance.createPickFragmentShader(true); var pickVS2D = shadowVolumeAppearance.createPickVertexShader( [extrudedDefine], vsPick, true, frameState.mapProjection ); pickProgram2D = context.shaderCache.createDerivedShaderProgram( classificationPrimitive._spPick, "2dPick", { vertexShaderSource: pickVS2D, fragmentShaderSource: pickFS2D, attributeLocations: attributeLocations, } ); } classificationPrimitive._spPick2D = pickProgram2D; } } else { classificationPrimitive._spPick = ShaderProgram.fromCache({ context: context, vertexShaderSource: vsSource, fragmentShaderSource: fsSource, attributeLocations: attributeLocations, }); } vs = Primitive._appendShowToShader(primitive, vs); vsSource = new ShaderSource({ defines: [extrudedDefine], sources: [vs], }); classificationPrimitive._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: classificationPrimitive._sp, vertexShaderSource: vsSource, fragmentShaderSource: fsSource, attributeLocations: attributeLocations, }); // Create a fragment shader that computes only required material hookups using screen space techniques var fsColorSource = shadowVolumeAppearance.createFragmentShader(false); var vsColorSource = shadowVolumeAppearance.createVertexShader( [extrudedDefine], vs, false, frameState.mapProjection ); classificationPrimitive._spColor = ShaderProgram.replaceCache({ context: context, shaderProgram: classificationPrimitive._spColor, vertexShaderSource: vsColorSource, fragmentShaderSource: fsColorSource, attributeLocations: attributeLocations, }); // Derive a 2D shader if the primitive uses texture coordinate-based fragment culling, // since texture coordinates are computed differently in 2D. // Any material that uses texture coordinates will also equip texture coordinate-based fragment culling. if (cullFragmentsUsingExtents) { var colorProgram2D = context.shaderCache.getDerivedShaderProgram( classificationPrimitive._spColor, "2dColor" ); if (!defined(colorProgram2D)) { var fsColorSource2D = shadowVolumeAppearance.createFragmentShader(true); var vsColorSource2D = shadowVolumeAppearance.createVertexShader( [extrudedDefine], vs, true, frameState.mapProjection ); colorProgram2D = context.shaderCache.createDerivedShaderProgram( classificationPrimitive._spColor, "2dColor", { vertexShaderSource: vsColorSource2D, fragmentShaderSource: fsColorSource2D, attributeLocations: attributeLocations, } ); } classificationPrimitive._spColor2D = colorProgram2D; } } function createColorCommands$1(classificationPrimitive, colorCommands) { var primitive = classificationPrimitive._primitive; var length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill colorCommands.length = length; var i; var command; var derivedCommand; var vaIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()( classificationPrimitive._uniformMap ); var needs2DShader = classificationPrimitive._needs2DShader; for (i = 0; i < length; i += 2) { var vertexArray = primitive._va[vaIndex++]; // Stencil depth command command = colorCommands[i]; if (!defined(command)) { command = colorCommands[i] = new DrawCommand({ owner: classificationPrimitive, primitiveType: primitive._primitiveType, }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; command.pass = Pass$1.TERRAIN_CLASSIFICATION; derivedCommand = DrawCommand.shallowClone( command, command.derivedCommands.tileset ); derivedCommand.renderState = classificationPrimitive._rsStencilDepthPass3DTiles; derivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; command.derivedCommands.tileset = derivedCommand; // Color command command = colorCommands[i + 1]; if (!defined(command)) { command = colorCommands[i + 1] = new DrawCommand({ owner: classificationPrimitive, primitiveType: primitive._primitiveType, }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsColorPass; command.shaderProgram = classificationPrimitive._spColor; command.pass = Pass$1.TERRAIN_CLASSIFICATION; var appearance = classificationPrimitive.appearance; var material = appearance.material; if (defined(material)) { uniformMap = combine$2(uniformMap, material._uniforms); } command.uniformMap = uniformMap; derivedCommand = DrawCommand.shallowClone( command, command.derivedCommands.tileset ); derivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; command.derivedCommands.tileset = derivedCommand; // Derive for 2D if texture coordinates are ever computed if (needs2DShader) { // First derive from the terrain command var derived2DCommand = DrawCommand.shallowClone( command, command.derivedCommands.appearance2D ); derived2DCommand.shaderProgram = classificationPrimitive._spColor2D; command.derivedCommands.appearance2D = derived2DCommand; // Then derive from the 3D Tiles command derived2DCommand = DrawCommand.shallowClone( derivedCommand, derivedCommand.derivedCommands.appearance2D ); derived2DCommand.shaderProgram = classificationPrimitive._spColor2D; derivedCommand.derivedCommands.appearance2D = derived2DCommand; } } var commandsIgnoreShow = classificationPrimitive._commandsIgnoreShow; var spStencil = classificationPrimitive._spStencil; var commandIndex = 0; length = commandsIgnoreShow.length = length / 2; for (var j = 0; j < length; ++j) { var commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone( colorCommands[commandIndex], commandsIgnoreShow[j] )); commandIgnoreShow.shaderProgram = spStencil; commandIgnoreShow.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; commandIndex += 2; } } function createPickCommands$1(classificationPrimitive, pickCommands) { var usePickOffsets = classificationPrimitive._usePickOffsets; var primitive = classificationPrimitive._primitive; var length = primitive._va.length * 2; // each geometry (pack of vertex attributes) needs 2 commands: front/back stencils and fill // Fallback for batching same-color geometry instances var pickOffsets; var pickIndex = 0; var pickOffset; if (usePickOffsets) { pickOffsets = primitive._pickOffsets; length = pickOffsets.length * 2; } pickCommands.length = length; var j; var command; var derivedCommand; var vaIndex = 0; var uniformMap = primitive._batchTable.getUniformMapCallback()( classificationPrimitive._uniformMap ); var needs2DShader = classificationPrimitive._needs2DShader; for (j = 0; j < length; j += 2) { var vertexArray = primitive._va[vaIndex++]; if (usePickOffsets) { pickOffset = pickOffsets[pickIndex++]; vertexArray = primitive._va[pickOffset.index]; } // Stencil depth command command = pickCommands[j]; if (!defined(command)) { command = pickCommands[j] = new DrawCommand({ owner: classificationPrimitive, primitiveType: primitive._primitiveType, pickOnly: true, }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsStencilDepthPass; command.shaderProgram = classificationPrimitive._sp; command.uniformMap = uniformMap; command.pass = Pass$1.TERRAIN_CLASSIFICATION; if (usePickOffsets) { command.offset = pickOffset.offset; command.count = pickOffset.count; } // Derive for 3D Tiles classification derivedCommand = DrawCommand.shallowClone( command, command.derivedCommands.tileset ); derivedCommand.renderState = classificationPrimitive._rsStencilDepthPass3DTiles; derivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; command.derivedCommands.tileset = derivedCommand; // Pick color command command = pickCommands[j + 1]; if (!defined(command)) { command = pickCommands[j + 1] = new DrawCommand({ owner: classificationPrimitive, primitiveType: primitive._primitiveType, pickOnly: true, }); } command.vertexArray = vertexArray; command.renderState = classificationPrimitive._rsPickPass; command.shaderProgram = classificationPrimitive._spPick; command.uniformMap = uniformMap; command.pass = Pass$1.TERRAIN_CLASSIFICATION; if (usePickOffsets) { command.offset = pickOffset.offset; command.count = pickOffset.count; } derivedCommand = DrawCommand.shallowClone( command, command.derivedCommands.tileset ); derivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; command.derivedCommands.tileset = derivedCommand; // Derive for 2D if texture coordinates are ever computed if (needs2DShader) { // First derive from the terrain command var derived2DCommand = DrawCommand.shallowClone( command, command.derivedCommands.pick2D ); derived2DCommand.shaderProgram = classificationPrimitive._spPick2D; command.derivedCommands.pick2D = derived2DCommand; // Then derive from the 3D Tiles command derived2DCommand = DrawCommand.shallowClone( derivedCommand, derivedCommand.derivedCommands.pick2D ); derived2DCommand.shaderProgram = classificationPrimitive._spPick2D; derivedCommand.derivedCommands.pick2D = derived2DCommand; } } } function createCommands$4( classificationPrimitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands ) { createColorCommands$1(classificationPrimitive, colorCommands); createPickCommands$1(classificationPrimitive, pickCommands); } function boundingVolumeIndex$1(commandIndex, length) { return Math.floor((commandIndex % length) / 2); } function updateAndQueueRenderCommand$1( command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ) { command.modelMatrix = modelMatrix; command.boundingVolume = boundingVolume; command.cull = cull; command.debugShowBoundingVolume = debugShowBoundingVolume; frameState.commandList.push(command); } function updateAndQueuePickCommand$1( command, frameState, modelMatrix, cull, boundingVolume ) { command.modelMatrix = modelMatrix; command.boundingVolume = boundingVolume; command.cull = cull; frameState.commandList.push(command); } function updateAndQueueCommands$2( classificationPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { var primitive = classificationPrimitive._primitive; Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); var boundingVolumes; if (frameState.mode === SceneMode$1.SCENE3D) { boundingVolumes = primitive._boundingSphereWC; } else if (frameState.mode === SceneMode$1.COLUMBUS_VIEW) { boundingVolumes = primitive._boundingSphereCV; } else if ( frameState.mode === SceneMode$1.SCENE2D && defined(primitive._boundingSphere2D) ) { boundingVolumes = primitive._boundingSphere2D; } else if (defined(primitive._boundingSphereMorph)) { boundingVolumes = primitive._boundingSphereMorph; } var classificationType = classificationPrimitive.classificationType; var queueTerrainCommands = classificationType !== ClassificationType$1.CESIUM_3D_TILE; var queue3DTilesCommands = classificationType !== ClassificationType$1.TERRAIN; var passes = frameState.passes; var i; var boundingVolume; var command; if (passes.render) { var colorLength = colorCommands.length; for (i = 0; i < colorLength; ++i) { boundingVolume = boundingVolumes[boundingVolumeIndex$1(i, colorLength)]; if (queueTerrainCommands) { command = colorCommands[i]; updateAndQueueRenderCommand$1( command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } if (queue3DTilesCommands) { command = colorCommands[i].derivedCommands.tileset; updateAndQueueRenderCommand$1( command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } } if (frameState.invertClassification) { var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; var ignoreShowCommandsLength = ignoreShowCommands.length; for (i = 0; i < ignoreShowCommandsLength; ++i) { boundingVolume = boundingVolumes[i]; command = ignoreShowCommands[i]; updateAndQueueRenderCommand$1( command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } } } if (passes.pick) { var pickLength = pickCommands.length; var pickOffsets = primitive._pickOffsets; for (i = 0; i < pickLength; ++i) { var pickOffset = pickOffsets[boundingVolumeIndex$1(i, pickLength)]; boundingVolume = boundingVolumes[pickOffset.index]; if (queueTerrainCommands) { command = pickCommands[i]; updateAndQueuePickCommand$1( command, frameState, modelMatrix, cull, boundingVolume ); } if (queue3DTilesCommands) { command = pickCommands[i].derivedCommands.tileset; updateAndQueuePickCommand$1( command, frameState, modelMatrix, cull, boundingVolume ); } } } } /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. * @exception {DeveloperError} Not all of the geometry instances have the same color attribute. */ ClassificationPrimitive.prototype.update = function (frameState) { if (!defined(this._primitive) && !defined(this.geometryInstances)) { return; } var appearance = this.appearance; if (defined(appearance) && defined(appearance.material)) { appearance.material.update(frameState.context); } var that = this; var primitiveOptions = this._primitiveOptions; if (!defined(this._primitive)) { var instances = Array.isArray(this.geometryInstances) ? this.geometryInstances : [this.geometryInstances]; var length = instances.length; var i; var instance; var attributes; var hasPerColorAttribute = false; var allColorsSame = true; var firstColor; var hasSphericalExtentsAttribute = false; var hasPlanarExtentsAttributes = false; if (length > 0) { attributes = instances[0].attributes; // Not expecting these to be set by users, should only be set via GroundPrimitive. // So don't check for mismatch. hasSphericalExtentsAttribute = ShadowVolumeAppearance.hasAttributesForSphericalExtents( attributes ); hasPlanarExtentsAttributes = ShadowVolumeAppearance.hasAttributesForTextureCoordinatePlanes( attributes ); firstColor = attributes.color; } for (i = 0; i < length; i++) { instance = instances[i]; var color = instance.attributes.color; if (defined(color)) { hasPerColorAttribute = true; } //>>includeStart('debug', pragmas.debug); else if (hasPerColorAttribute) { throw new DeveloperError( "All GeometryInstances must have color attributes to use per-instance color." ); } //>>includeEnd('debug'); allColorsSame = allColorsSame && defined(color) && ColorGeometryInstanceAttribute.equals(firstColor, color); } // If no attributes exist for computing spherical extents or fragment culling, // throw if the colors aren't all the same. if ( !allColorsSame && !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes ) { throw new DeveloperError( "All GeometryInstances must have the same color attribute except via GroundPrimitives" ); } // default to a color appearance if (hasPerColorAttribute && !defined(appearance)) { appearance = new PerInstanceColorAppearance({ flat: true, }); this.appearance = appearance; } //>>includeStart('debug', pragmas.debug); if ( !hasPerColorAttribute && appearance instanceof PerInstanceColorAppearance ) { throw new DeveloperError( "PerInstanceColorAppearance requires color GeometryInstanceAttributes on all GeometryInstances" ); } if ( defined(appearance.material) && !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes ) { throw new DeveloperError( "Materials on ClassificationPrimitives are not supported except via GroundPrimitives" ); } //>>includeEnd('debug'); this._usePickOffsets = !hasSphericalExtentsAttribute && !hasPlanarExtentsAttributes; this._hasSphericalExtentsAttribute = hasSphericalExtentsAttribute; this._hasPlanarExtentsAttributes = hasPlanarExtentsAttributes; this._hasPerColorAttribute = hasPerColorAttribute; var geometryInstances = new Array(length); for (i = 0; i < length; ++i) { instance = instances[i]; geometryInstances[i] = new GeometryInstance({ geometry: instance.geometry, attributes: instance.attributes, modelMatrix: instance.modelMatrix, id: instance.id, pickPrimitive: defaultValue(this._pickPrimitive, that), }); } primitiveOptions.appearance = appearance; primitiveOptions.geometryInstances = geometryInstances; if (defined(this._createBoundingVolumeFunction)) { primitiveOptions._createBoundingVolumeFunction = function ( frameState, geometry ) { that._createBoundingVolumeFunction(frameState, geometry); }; } primitiveOptions._createRenderStatesFunction = function ( primitive, context, appearance, twoPasses ) { createRenderStates$4(that); }; primitiveOptions._createShaderProgramFunction = function ( primitive, frameState, appearance ) { createShaderProgram$1(that, frameState); }; primitiveOptions._createCommandsFunction = function ( primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands ) { createCommands$4( that, undefined, undefined, true, false, colorCommands, pickCommands ); }; if (defined(this._updateAndQueueCommandsFunction)) { primitiveOptions._updateAndQueueCommandsFunction = function ( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { that._updateAndQueueCommandsFunction( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ); }; } else { primitiveOptions._updateAndQueueCommandsFunction = function ( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { updateAndQueueCommands$2( that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume); }; } this._primitive = new Primitive(primitiveOptions); this._primitive.readyPromise.then(function (primitive) { that._ready = true; if (that.releaseGeometryInstances) { that.geometryInstances = undefined; } var error = primitive._error; if (!defined(error)) { that._readyPromise.resolve(that); } else { that._readyPromise.reject(error); } }); } if ( this.debugShowShadowVolume && !this._debugShowShadowVolume && this._ready ) { this._debugShowShadowVolume = true; this._rsStencilDepthPass = RenderState.fromCache( getStencilDepthRenderState$1(false, false) ); this._rsStencilDepthPass3DTiles = RenderState.fromCache( getStencilDepthRenderState$1(false, true) ); this._rsColorPass = RenderState.fromCache(getColorRenderState(false)); } else if (!this.debugShowShadowVolume && this._debugShowShadowVolume) { this._debugShowShadowVolume = false; this._rsStencilDepthPass = RenderState.fromCache( getStencilDepthRenderState$1(true, false) ); this._rsStencilDepthPass3DTiles = RenderState.fromCache( getStencilDepthRenderState$1(true, true) ); this._rsColorPass = RenderState.fromCache(getColorRenderState(true)); } // Update primitive appearance if (this._primitive.appearance !== appearance) { //>>includeStart('debug', pragmas.debug); // Check if the appearance is supported by the geometry attributes if ( !this._hasSphericalExtentsAttribute && !this._hasPlanarExtentsAttributes && defined(appearance.material) ) { throw new DeveloperError( "Materials on ClassificationPrimitives are not supported except via GroundPrimitive" ); } if ( !this._hasPerColorAttribute && appearance instanceof PerInstanceColorAppearance ) { throw new DeveloperError( "PerInstanceColorAppearance requires color GeometryInstanceAttribute" ); } //>>includeEnd('debug'); this._primitive.appearance = appearance; } this._primitive.show = this.show; this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; this._primitive.update(frameState); }; /** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {*} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); */ ClassificationPrimitive.prototype.getGeometryInstanceAttributes = function ( id ) { //>>includeStart('debug', pragmas.debug); if (!defined(this._primitive)) { throw new DeveloperError( "must call update before calling getGeometryInstanceAttributes" ); } //>>includeEnd('debug'); return this._primitive.getGeometryInstanceAttributes(id); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see ClassificationPrimitive#destroy */ ClassificationPrimitive.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * e = e && e.destroy(); * * @see ClassificationPrimitive#isDestroyed */ ClassificationPrimitive.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); this._sp = this._sp && this._sp.destroy(); this._spPick = this._spPick && this._spPick.destroy(); this._spColor = this._spColor && this._spColor.destroy(); // Derived programs, destroyed above if they existed. this._spPick2D = undefined; this._spColor2D = undefined; return destroyObject(this); }; var GroundPrimitiveUniformMap = { u_globeMinimumAltitude: function () { return 55000.0; }, }; /** * A ground primitive represents geometry draped over terrain or 3D Tiles in the {@link Scene}. *

* A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement, * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix * and match most of them and add a new geometry or appearance independently of each other. *

*

* Support for the WEBGL_depth_texture extension is required to use GeometryInstances with different PerInstanceColors * or materials besides PerInstanceColorAppearance. *

*

* Textured GroundPrimitives were designed for notional patterns and are not meant for precisely mapping * textures to terrain - for that use case, use {@link SingleTileImageryProvider}. *

*

* For correct rendering, this feature requires the EXT_frag_depth WebGL extension. For hardware that do not support this extension, there * will be rendering artifacts for some viewing angles. *

*

* Valid geometries are {@link CircleGeometry}, {@link CorridorGeometry}, {@link EllipseGeometry}, {@link PolygonGeometry}, and {@link RectangleGeometry}. *

* * @alias GroundPrimitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] The geometry instances to render. * @param {Appearance} [options.appearance] The appearance used to render the primitive. Defaults to a flat PerInstanceColorAppearance when GeometryInstances have a color attribute. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Boolean} [options.vertexCacheOptimize=false] When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * @param {Boolean} [options.interleave=false] When true, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.compressVertices=true] When true, the geometry vertices are compressed, which will save memory. * @param {Boolean} [options.releaseGeometryInstances=true] When true, the primitive does not keep a reference to the input geometryInstances to save memory. * @param {Boolean} [options.allowPicking=true] When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be true on * creation for the volumes to be created before the geometry is released or options.releaseGeometryInstance must be false. * * @example * // Example 1: Create primitive with a single instance * var rectangleInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.RectangleGeometry({ * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0) * }), * id : 'rectangle', * attributes : { * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5) * } * }); * scene.primitives.add(new Cesium.GroundPrimitive({ * geometryInstances : rectangleInstance * })); * * // Example 2: Batch instances * var color = new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5); // Both instances must have the same color. * var rectangleInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.RectangleGeometry({ * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0) * }), * id : 'rectangle', * attributes : { * color : color * } * }); * var ellipseInstance = new Cesium.GeometryInstance({ * geometry : new Cesium.EllipseGeometry({ * center : Cesium.Cartesian3.fromDegrees(-105.0, 40.0), * semiMinorAxis : 300000.0, * semiMajorAxis : 400000.0 * }), * id : 'ellipse', * attributes : { * color : color * } * }); * scene.primitives.add(new Cesium.GroundPrimitive({ * geometryInstances : [rectangleInstance, ellipseInstance] * })); * * @see Primitive * @see ClassificationPrimitive * @see GeometryInstance * @see Appearance */ function GroundPrimitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var appearance = options.appearance; var geometryInstances = options.geometryInstances; if (!defined(appearance) && defined(geometryInstances)) { var geometryInstancesArray = Array.isArray(geometryInstances) ? geometryInstances : [geometryInstances]; var geometryInstanceCount = geometryInstancesArray.length; for (var i = 0; i < geometryInstanceCount; i++) { var attributes = geometryInstancesArray[i].attributes; if (defined(attributes) && defined(attributes.color)) { appearance = new PerInstanceColorAppearance({ flat: true, }); break; } } } /** * The {@link Appearance} used to shade this primitive. Each geometry * instance is shaded with the same appearance. Some appearances, like * {@link PerInstanceColorAppearance} allow giving each instance unique * properties. * * @type Appearance * * @default undefined */ this.appearance = appearance; /** * The geometry instances rendered with this primitive. This may * be undefined if options.releaseGeometryInstances * is true when the primitive is constructed. *

* Changing this property after the primitive is rendered has no effect. *

* * @readonly * @type {Array|GeometryInstance} * * @default undefined */ this.geometryInstances = options.geometryInstances; /** * Determines if the primitive will be shown. This affects all geometry * instances in the primitive. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * Determines whether terrain, 3D Tiles or both will be classified. * * @type {ClassificationType} * * @default ClassificationType.BOTH */ this.classificationType = defaultValue( options.classificationType, ClassificationType$1.BOTH ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the shadow volume for each geometry in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowShadowVolume = defaultValue( options.debugShowShadowVolume, false ); this._boundingVolumes = []; this._boundingVolumes2D = []; this._ready = false; this._readyPromise = when.defer(); this._primitive = undefined; this._maxHeight = undefined; this._minHeight = undefined; this._maxTerrainHeight = ApproximateTerrainHeights._defaultMaxTerrainHeight; this._minTerrainHeight = ApproximateTerrainHeights._defaultMinTerrainHeight; this._boundingSpheresKeys = []; this._boundingSpheres = []; this._useFragmentCulling = false; // Used when inserting in an OrderedPrimitiveCollection this._zIndex = undefined; var that = this; this._classificationPrimitiveOptions = { geometryInstances: undefined, appearance: undefined, vertexCacheOptimize: defaultValue(options.vertexCacheOptimize, false), interleave: defaultValue(options.interleave, false), releaseGeometryInstances: defaultValue( options.releaseGeometryInstances, true ), allowPicking: defaultValue(options.allowPicking, true), asynchronous: defaultValue(options.asynchronous, true), compressVertices: defaultValue(options.compressVertices, true), _createBoundingVolumeFunction: undefined, _updateAndQueueCommandsFunction: undefined, _pickPrimitive: that, _extruded: true, _uniformMap: GroundPrimitiveUniformMap, }; } Object.defineProperties(GroundPrimitive.prototype, { /** * When true, geometry vertices are optimized for the pre and post-vertex-shader caches. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ vertexCacheOptimize: { get: function () { return this._classificationPrimitiveOptions.vertexCacheOptimize; }, }, /** * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default false */ interleave: { get: function () { return this._classificationPrimitiveOptions.interleave; }, }, /** * When true, the primitive does not keep a reference to the input geometryInstances to save memory. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ releaseGeometryInstances: { get: function () { return this._classificationPrimitiveOptions.releaseGeometryInstances; }, }, /** * When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking: { get: function () { return this._classificationPrimitiveOptions.allowPicking; }, }, /** * Determines if the geometry instances will be created and batched on a web worker. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous: { get: function () { return this._classificationPrimitiveOptions.asynchronous; }, }, /** * When true, geometry vertices are compressed, which will save memory. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ compressVertices: { get: function () { return this._classificationPrimitiveOptions.compressVertices; }, }, /** * Determines if the primitive is complete and ready to render. If this property is * true, the primitive will be rendered the next time that {@link GroundPrimitive#update} * is called. * * @memberof GroundPrimitive.prototype * * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof GroundPrimitive.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); /** * Determines if GroundPrimitive rendering is supported. * * @function * @param {Scene} scene The scene. * @returns {Boolean} true if GroundPrimitives are supported; otherwise, returns false */ GroundPrimitive.isSupported = ClassificationPrimitive.isSupported; function getComputeMaximumHeightFunction(primitive) { return function (granularity, ellipsoid) { var r = ellipsoid.maximumRadius; var delta = r / Math.cos(granularity * 0.5) - r; return primitive._maxHeight + delta; }; } function getComputeMinimumHeightFunction(primitive) { return function (granularity, ellipsoid) { return primitive._minHeight; }; } var scratchBVCartesianHigh = new Cartesian3(); var scratchBVCartesianLow = new Cartesian3(); var scratchBVCartesian = new Cartesian3(); var scratchBVCartographic = new Cartographic(); var scratchBVRectangle = new Rectangle(); function getRectangle(frameState, geometry) { var ellipsoid = frameState.mapProjection.ellipsoid; if ( !defined(geometry.attributes) || !defined(geometry.attributes.position3DHigh) ) { if (defined(geometry.rectangle)) { return geometry.rectangle; } return undefined; } var highPositions = geometry.attributes.position3DHigh.values; var lowPositions = geometry.attributes.position3DLow.values; var length = highPositions.length; var minLat = Number.POSITIVE_INFINITY; var minLon = Number.POSITIVE_INFINITY; var maxLat = Number.NEGATIVE_INFINITY; var maxLon = Number.NEGATIVE_INFINITY; for (var i = 0; i < length; i += 3) { var highPosition = Cartesian3.unpack( highPositions, i, scratchBVCartesianHigh ); var lowPosition = Cartesian3.unpack(lowPositions, i, scratchBVCartesianLow); var position = Cartesian3.add( highPosition, lowPosition, scratchBVCartesian ); var cartographic = ellipsoid.cartesianToCartographic( position, scratchBVCartographic ); var latitude = cartographic.latitude; var longitude = cartographic.longitude; minLat = Math.min(minLat, latitude); minLon = Math.min(minLon, longitude); maxLat = Math.max(maxLat, latitude); maxLon = Math.max(maxLon, longitude); } var rectangle = scratchBVRectangle; rectangle.north = maxLat; rectangle.south = minLat; rectangle.east = maxLon; rectangle.west = minLon; return rectangle; } function setMinMaxTerrainHeights(primitive, rectangle, ellipsoid) { var result = ApproximateTerrainHeights.getMinimumMaximumHeights( rectangle, ellipsoid ); primitive._minTerrainHeight = result.minimumTerrainHeight; primitive._maxTerrainHeight = result.maximumTerrainHeight; } function createBoundingVolume(groundPrimitive, frameState, geometry) { var ellipsoid = frameState.mapProjection.ellipsoid; var rectangle = getRectangle(frameState, geometry); var obb = OrientedBoundingBox.fromRectangle( rectangle, groundPrimitive._minHeight, groundPrimitive._maxHeight, ellipsoid ); groundPrimitive._boundingVolumes.push(obb); if (!frameState.scene3DOnly) { var projection = frameState.mapProjection; var boundingVolume = BoundingSphere.fromRectangleWithHeights2D( rectangle, projection, groundPrimitive._maxHeight, groundPrimitive._minHeight ); Cartesian3.fromElements( boundingVolume.center.z, boundingVolume.center.x, boundingVolume.center.y, boundingVolume.center ); groundPrimitive._boundingVolumes2D.push(boundingVolume); } } function boundingVolumeIndex(commandIndex, length) { return Math.floor((commandIndex % length) / 2); } function updateAndQueueRenderCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ) { // Use derived appearance command for 2D if needed var classificationPrimitive = groundPrimitive._primitive; if ( frameState.mode !== SceneMode$1.SCENE3D && command.shaderProgram === classificationPrimitive._spColor && classificationPrimitive._needs2DShader ) { command = command.derivedCommands.appearance2D; } command.owner = groundPrimitive; command.modelMatrix = modelMatrix; command.boundingVolume = boundingVolume; command.cull = cull; command.debugShowBoundingVolume = debugShowBoundingVolume; frameState.commandList.push(command); } function updateAndQueuePickCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume ) { // Use derived pick command for 2D if needed var classificationPrimitive = groundPrimitive._primitive; if ( frameState.mode !== SceneMode$1.SCENE3D && command.shaderProgram === classificationPrimitive._spPick && classificationPrimitive._needs2DShader ) { command = command.derivedCommands.pick2D; } command.owner = groundPrimitive; command.modelMatrix = modelMatrix; command.boundingVolume = boundingVolume; command.cull = cull; frameState.commandList.push(command); } function updateAndQueueCommands$1( groundPrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { var boundingVolumes; if (frameState.mode === SceneMode$1.SCENE3D) { boundingVolumes = groundPrimitive._boundingVolumes; } else { boundingVolumes = groundPrimitive._boundingVolumes2D; } var classificationType = groundPrimitive.classificationType; var queueTerrainCommands = classificationType !== ClassificationType$1.CESIUM_3D_TILE; var queue3DTilesCommands = classificationType !== ClassificationType$1.TERRAIN; var passes = frameState.passes; var classificationPrimitive = groundPrimitive._primitive; var i; var boundingVolume; var command; if (passes.render) { var colorLength = colorCommands.length; for (i = 0; i < colorLength; ++i) { boundingVolume = boundingVolumes[boundingVolumeIndex(i, colorLength)]; if (queueTerrainCommands) { command = colorCommands[i]; updateAndQueueRenderCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } if (queue3DTilesCommands) { command = colorCommands[i].derivedCommands.tileset; updateAndQueueRenderCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } } if (frameState.invertClassification) { var ignoreShowCommands = classificationPrimitive._commandsIgnoreShow; var ignoreShowCommandsLength = ignoreShowCommands.length; for (i = 0; i < ignoreShowCommandsLength; ++i) { boundingVolume = boundingVolumes[i]; command = ignoreShowCommands[i]; updateAndQueueRenderCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } } } if (passes.pick) { var pickLength = pickCommands.length; var pickOffsets; if (!groundPrimitive._useFragmentCulling) { // Must be using pick offsets pickOffsets = classificationPrimitive._primitive._pickOffsets; } for (i = 0; i < pickLength; ++i) { boundingVolume = boundingVolumes[boundingVolumeIndex(i, pickLength)]; if (!groundPrimitive._useFragmentCulling) { var pickOffset = pickOffsets[boundingVolumeIndex(i, pickLength)]; boundingVolume = boundingVolumes[pickOffset.index]; } if (queueTerrainCommands) { command = pickCommands[i]; updateAndQueuePickCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume ); } if (queue3DTilesCommands) { command = pickCommands[i].derivedCommands.tileset; updateAndQueuePickCommand( groundPrimitive, command, frameState, modelMatrix, cull, boundingVolume ); } } } } /** * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the * GroundPrimitive synchronously. * * @returns {Promise} A promise that will resolve once the terrain heights have been loaded. * */ GroundPrimitive.initializeTerrainHeights = function () { return ApproximateTerrainHeights.initialize(); }; /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {DeveloperError} For synchronous GroundPrimitive, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve. * @exception {DeveloperError} All instance geometries must have the same primitiveType. * @exception {DeveloperError} Appearance and material have a uniform with the same name. */ GroundPrimitive.prototype.update = function (frameState) { if (!defined(this._primitive) && !defined(this.geometryInstances)) { return; } if (!ApproximateTerrainHeights.initialized) { //>>includeStart('debug', pragmas.debug); if (!this.asynchronous) { throw new DeveloperError( "For synchronous GroundPrimitives, you must call GroundPrimitive.initializeTerrainHeights() and wait for the returned promise to resolve." ); } //>>includeEnd('debug'); GroundPrimitive.initializeTerrainHeights(); return; } var that = this; var primitiveOptions = this._classificationPrimitiveOptions; if (!defined(this._primitive)) { var ellipsoid = frameState.mapProjection.ellipsoid; var instance; var geometry; var instanceType; var instances = Array.isArray(this.geometryInstances) ? this.geometryInstances : [this.geometryInstances]; var length = instances.length; var groundInstances = new Array(length); var i; var rectangle; for (i = 0; i < length; ++i) { instance = instances[i]; geometry = instance.geometry; var instanceRectangle = getRectangle(frameState, geometry); if (!defined(rectangle)) { rectangle = Rectangle.clone(instanceRectangle); } else if (defined(instanceRectangle)) { Rectangle.union(rectangle, instanceRectangle, rectangle); } var id = instance.id; if (defined(id) && defined(instanceRectangle)) { var boundingSphere = ApproximateTerrainHeights.getBoundingSphere( instanceRectangle, ellipsoid ); this._boundingSpheresKeys.push(id); this._boundingSpheres.push(boundingSphere); } instanceType = geometry.constructor; if (!defined(instanceType) || !defined(instanceType.createShadowVolume)) { //>>includeStart('debug', pragmas.debug); throw new DeveloperError( "Not all of the geometry instances have GroundPrimitive support." ); //>>includeEnd('debug'); } } // Now compute the min/max heights for the primitive setMinMaxTerrainHeights(this, rectangle, ellipsoid); var exaggeration = frameState.terrainExaggeration; this._minHeight = this._minTerrainHeight * exaggeration; this._maxHeight = this._maxTerrainHeight * exaggeration; var useFragmentCulling = GroundPrimitive._supportsMaterials( frameState.context ); this._useFragmentCulling = useFragmentCulling; if (useFragmentCulling) { // Determine whether to add spherical or planar extent attributes for computing texture coordinates. // This depends on the size of the GeometryInstances. var attributes; var usePlanarExtents = true; for (i = 0; i < length; ++i) { instance = instances[i]; geometry = instance.geometry; rectangle = getRectangle(frameState, geometry); if (ShadowVolumeAppearance.shouldUseSphericalCoordinates(rectangle)) { usePlanarExtents = false; break; } } for (i = 0; i < length; ++i) { instance = instances[i]; geometry = instance.geometry; instanceType = geometry.constructor; var boundingRectangle = getRectangle(frameState, geometry); var textureCoordinateRotationPoints = geometry.textureCoordinateRotationPoints; if (usePlanarExtents) { attributes = ShadowVolumeAppearance.getPlanarTextureCoordinateAttributes( boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection, this._maxHeight ); } else { attributes = ShadowVolumeAppearance.getSphericalExtentGeometryInstanceAttributes( boundingRectangle, textureCoordinateRotationPoints, ellipsoid, frameState.mapProjection ); } var instanceAttributes = instance.attributes; for (var attributeKey in instanceAttributes) { if (instanceAttributes.hasOwnProperty(attributeKey)) { attributes[attributeKey] = instanceAttributes[attributeKey]; } } groundInstances[i] = new GeometryInstance({ geometry: instanceType.createShadowVolume( geometry, getComputeMinimumHeightFunction(this), getComputeMaximumHeightFunction(this) ), attributes: attributes, id: instance.id, }); } } else { // ClassificationPrimitive will check if the colors are all the same if it detects lack of fragment culling attributes for (i = 0; i < length; ++i) { instance = instances[i]; geometry = instance.geometry; instanceType = geometry.constructor; groundInstances[i] = new GeometryInstance({ geometry: instanceType.createShadowVolume( geometry, getComputeMinimumHeightFunction(this), getComputeMaximumHeightFunction(this) ), attributes: instance.attributes, id: instance.id, }); } } primitiveOptions.geometryInstances = groundInstances; primitiveOptions.appearance = this.appearance; primitiveOptions._createBoundingVolumeFunction = function ( frameState, geometry ) { createBoundingVolume(that, frameState, geometry); }; primitiveOptions._updateAndQueueCommandsFunction = function ( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { updateAndQueueCommands$1( that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume); }; this._primitive = new ClassificationPrimitive(primitiveOptions); this._primitive.readyPromise.then(function (primitive) { that._ready = true; if (that.releaseGeometryInstances) { that.geometryInstances = undefined; } var error = primitive._error; if (!defined(error)) { that._readyPromise.resolve(that); } else { that._readyPromise.reject(error); } }); } this._primitive.appearance = this.appearance; this._primitive.show = this.show; this._primitive.debugShowShadowVolume = this.debugShowShadowVolume; this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; this._primitive.update(frameState); }; /** * @private */ GroundPrimitive.prototype.getBoundingSphere = function (id) { var index = this._boundingSpheresKeys.indexOf(id); if (index !== -1) { return this._boundingSpheres[index]; } return undefined; }; /** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {*} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); */ GroundPrimitive.prototype.getGeometryInstanceAttributes = function (id) { //>>includeStart('debug', pragmas.debug); if (!defined(this._primitive)) { throw new DeveloperError( "must call update before calling getGeometryInstanceAttributes" ); } //>>includeEnd('debug'); return this._primitive.getGeometryInstanceAttributes(id); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see GroundPrimitive#destroy */ GroundPrimitive.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * e = e && e.destroy(); * * @see GroundPrimitive#isDestroyed */ GroundPrimitive.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); return destroyObject(this); }; /** * Exposed for testing. * * @param {Context} context Rendering context * @returns {Boolean} Whether or not the current context supports materials on GroundPrimitives. * @private */ GroundPrimitive._supportsMaterials = function (context) { return context.depthTexture; }; /** * Checks if the given Scene supports materials on GroundPrimitives. * Materials on GroundPrimitives require support for the WEBGL_depth_texture extension. * * @param {Scene} scene The current scene. * @returns {Boolean} Whether or not the current scene supports materials on GroundPrimitives. */ GroundPrimitive.supportsMaterials = function (scene) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("scene", scene); //>>includeEnd('debug'); return GroundPrimitive._supportsMaterials(scene.frameState.context); }; /** * The interface for all {@link Property} objects that represent {@link Material} uniforms. * This type defines an interface and cannot be instantiated directly. * * @alias MaterialProperty * @constructor * @abstract * * @see ColorMaterialProperty * @see CompositeMaterialProperty * @see GridMaterialProperty * @see ImageMaterialProperty * @see PolylineGlowMaterialProperty * @see PolylineOutlineMaterialProperty * @see StripeMaterialProperty */ function MaterialProperty() { DeveloperError.throwInstantiationError(); } Object.defineProperties(MaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof MaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: DeveloperError.throwInstantiationError, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof MaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: DeveloperError.throwInstantiationError, }, }); /** * Gets the {@link Material} type at the provided time. * @function * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ MaterialProperty.prototype.getType = DeveloperError.throwInstantiationError; /** * Gets the value of the property at the provided time. * @function * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ MaterialProperty.prototype.getValue = DeveloperError.throwInstantiationError; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * @function * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ MaterialProperty.prototype.equals = DeveloperError.throwInstantiationError; /** * @private */ MaterialProperty.getValue = function (time, materialProperty, material) { var type; if (defined(materialProperty)) { type = materialProperty.getType(time); if (defined(type)) { if (!defined(material) || material.type !== type) { material = Material.fromType(type); } materialProperty.getValue(time, material.uniforms); return material; } } if (!defined(material) || material.type !== Material.ColorType) { material = Material.fromType(Material.ColorType); } Color.clone(Color.WHITE, material.uniforms.color); return material; }; /** * Defines the interface for a dynamic geometry updater. A DynamicGeometryUpdater * is responsible for handling visualization of a specific type of geometry * that needs to be recomputed based on simulation time. * This object is never used directly by client code, but is instead created by * {@link GeometryUpdater} implementations which contain dynamic geometry. * * This type defines an interface and cannot be instantiated directly. * * @alias DynamicGeometryUpdater * @constructor * @private * @abstract */ function DynamicGeometryUpdater$1( geometryUpdater, primitives, orderedGroundPrimitives ) { //>>includeStart('debug', pragmas.debug); Check.defined("geometryUpdater", geometryUpdater); Check.defined("primitives", primitives); Check.defined("orderedGroundPrimitives", orderedGroundPrimitives); //>>includeEnd('debug'); this._primitives = primitives; this._orderedGroundPrimitives = orderedGroundPrimitives; this._primitive = undefined; this._outlinePrimitive = undefined; this._geometryUpdater = geometryUpdater; this._options = geometryUpdater._options; this._entity = geometryUpdater._entity; this._material = undefined; } DynamicGeometryUpdater$1.prototype._isHidden = function (entity, geometry, time) { return ( !entity.isShowing || !entity.isAvailable(time) || !Property.getValueOrDefault(geometry.show, time, true) ); }; DynamicGeometryUpdater$1.prototype._setOptions = DeveloperError.throwInstantiationError; /** * Updates the geometry to the specified time. * @memberof DynamicGeometryUpdater * @function * * @param {JulianDate} time The current time. */ DynamicGeometryUpdater$1.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var geometryUpdater = this._geometryUpdater; var onTerrain = geometryUpdater._onTerrain; var primitives = this._primitives; var orderedGroundPrimitives = this._orderedGroundPrimitives; if (onTerrain) { orderedGroundPrimitives.remove(this._primitive); } else { primitives.removeAndDestroy(this._primitive); primitives.removeAndDestroy(this._outlinePrimitive); this._outlinePrimitive = undefined; } this._primitive = undefined; var entity = this._entity; var geometry = entity[this._geometryUpdater._geometryPropertyName]; this._setOptions(entity, geometry, time); if (this._isHidden(entity, geometry, time)) { return; } var shadows = this._geometryUpdater.shadowsProperty.getValue(time); var options = this._options; if (!defined(geometry.fill) || geometry.fill.getValue(time)) { var fillMaterialProperty = geometryUpdater.fillMaterialProperty; var isColorAppearance = fillMaterialProperty instanceof ColorMaterialProperty; var appearance; var closed = geometryUpdater._getIsClosed(options); if (isColorAppearance) { appearance = new PerInstanceColorAppearance({ closed: closed, flat: onTerrain && !geometryUpdater._supportsMaterialsforEntitiesOnTerrain, }); } else { var material = MaterialProperty.getValue( time, fillMaterialProperty, this._material ); this._material = material; appearance = new MaterialAppearance({ material: material, translucent: material.isTranslucent(), closed: closed, }); } if (onTerrain) { options.vertexFormat = PerInstanceColorAppearance.VERTEX_FORMAT; this._primitive = orderedGroundPrimitives.add( new GroundPrimitive({ geometryInstances: this._geometryUpdater.createFillGeometryInstance( time ), appearance: appearance, asynchronous: false, shadows: shadows, classificationType: this._geometryUpdater.classificationTypeProperty.getValue( time ), }), Property.getValueOrUndefined(this._geometryUpdater.zIndex, time) ); } else { options.vertexFormat = appearance.vertexFormat; var fillInstance = this._geometryUpdater.createFillGeometryInstance(time); if (isColorAppearance) { appearance.translucent = fillInstance.attributes.color.value[3] !== 255; } this._primitive = primitives.add( new Primitive({ geometryInstances: fillInstance, appearance: appearance, asynchronous: false, shadows: shadows, }) ); } } if ( !onTerrain && defined(geometry.outline) && geometry.outline.getValue(time) ) { var outlineInstance = this._geometryUpdater.createOutlineGeometryInstance( time ); var outlineWidth = Property.getValueOrDefault( geometry.outlineWidth, time, 1.0 ); this._outlinePrimitive = primitives.add( new Primitive({ geometryInstances: outlineInstance, appearance: new PerInstanceColorAppearance({ flat: true, translucent: outlineInstance.attributes.color.value[3] !== 255, renderState: { lineWidth: geometryUpdater._scene.clampLineWidth(outlineWidth), }, }), asynchronous: false, shadows: shadows, }) ); } }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * @function * * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ DynamicGeometryUpdater$1.prototype.getBoundingSphere = function (result) { //>>includeStart('debug', pragmas.debug); if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var entity = this._entity; var primitive = this._primitive; var outlinePrimitive = this._outlinePrimitive; var attributes; //Outline and Fill geometries have the same bounding sphere, so just use whichever one is defined and ready if (defined(primitive) && primitive.show && primitive.ready) { attributes = primitive.getGeometryInstanceAttributes(entity); if (defined(attributes) && defined(attributes.boundingSphere)) { BoundingSphere.clone(attributes.boundingSphere, result); return BoundingSphereState$1.DONE; } } if ( defined(outlinePrimitive) && outlinePrimitive.show && outlinePrimitive.ready ) { attributes = outlinePrimitive.getGeometryInstanceAttributes(entity); if (defined(attributes) && defined(attributes.boundingSphere)) { BoundingSphere.clone(attributes.boundingSphere, result); return BoundingSphereState$1.DONE; } } if ( (defined(primitive) && !primitive.ready) || (defined(outlinePrimitive) && !outlinePrimitive.ready) ) { return BoundingSphereState$1.PENDING; } return BoundingSphereState$1.FAILED; }; /** * Returns true if this object was destroyed; otherwise, false. * @memberof DynamicGeometryUpdater * @function * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ DynamicGeometryUpdater$1.prototype.isDestroyed = function () { return false; }; /** * Destroys and resources used by the object. Once an object is destroyed, it should not be used. * @memberof DynamicGeometryUpdater * @function * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ DynamicGeometryUpdater$1.prototype.destroy = function () { var primitives = this._primitives; var orderedGroundPrimitives = this._orderedGroundPrimitives; if (this._geometryUpdater._onTerrain) { orderedGroundPrimitives.remove(this._primitive); } else { primitives.removeAndDestroy(this._primitive); } primitives.removeAndDestroy(this._outlinePrimitive); destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var PolylineShadowVolumeFS = "#ifdef GL_EXT_frag_depth\n\ #extension GL_EXT_frag_depth : enable\n\ #endif\n\ \n\ varying vec4 v_startPlaneNormalEcAndHalfWidth;\n\ varying vec4 v_endPlaneNormalEcAndBatchId;\n\ varying vec4 v_rightPlaneEC; // Technically can compute distance for this here\n\ varying vec4 v_endEcAndStartEcX;\n\ varying vec4 v_texcoordNormalizationAndStartEcYZ;\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #endif\n\ \n\ void main(void)\n\ {\n\ float logDepthOrDepth = czm_branchFreeTernary(czm_sceneMode == czm_sceneMode2D, gl_FragCoord.z, czm_unpackDepth(texture2D(czm_globeDepthTexture, gl_FragCoord.xy / czm_viewport.zw)));\n\ vec3 ecStart = vec3(v_endEcAndStartEcX.w, v_texcoordNormalizationAndStartEcYZ.zw);\n\ \n\ // Discard for sky\n\ if (logDepthOrDepth == 0.0) {\n\ #ifdef DEBUG_SHOW_VOLUME\n\ gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);\n\ return;\n\ #else // DEBUG_SHOW_VOLUME\n\ discard;\n\ #endif // DEBUG_SHOW_VOLUME\n\ }\n\ \n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, logDepthOrDepth);\n\ eyeCoordinate /= eyeCoordinate.w;\n\ \n\ float halfMaxWidth = v_startPlaneNormalEcAndHalfWidth.w * czm_metersPerPixel(eyeCoordinate);\n\ // Check distance of the eye coordinate against the right-facing plane\n\ float widthwiseDistance = czm_planeDistance(v_rightPlaneEC, eyeCoordinate.xyz);\n\ \n\ // Check eye coordinate against the mitering planes\n\ float distanceFromStart = czm_planeDistance(v_startPlaneNormalEcAndHalfWidth.xyz, -dot(ecStart, v_startPlaneNormalEcAndHalfWidth.xyz), eyeCoordinate.xyz);\n\ float distanceFromEnd = czm_planeDistance(v_endPlaneNormalEcAndBatchId.xyz, -dot(v_endEcAndStartEcX.xyz, v_endPlaneNormalEcAndBatchId.xyz), eyeCoordinate.xyz);\n\ \n\ if (abs(widthwiseDistance) > halfMaxWidth || distanceFromStart < 0.0 || distanceFromEnd < 0.0) {\n\ #ifdef DEBUG_SHOW_VOLUME\n\ gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);\n\ return;\n\ #else // DEBUG_SHOW_VOLUME\n\ discard;\n\ #endif // DEBUG_SHOW_VOLUME\n\ }\n\ \n\ // Check distance of the eye coordinate against start and end planes with normals in the right plane.\n\ // For computing unskewed lengthwise texture coordinate.\n\ // Can also be used for clipping extremely pointy miters, but in practice unnecessary because of miter breaking.\n\ \n\ // aligned plane: cross the right plane normal with miter plane normal, then cross the result with right again to point it more \"forward\"\n\ vec3 alignedPlaneNormal;\n\ \n\ // start aligned plane\n\ alignedPlaneNormal = cross(v_rightPlaneEC.xyz, v_startPlaneNormalEcAndHalfWidth.xyz);\n\ alignedPlaneNormal = normalize(cross(alignedPlaneNormal, v_rightPlaneEC.xyz));\n\ distanceFromStart = czm_planeDistance(alignedPlaneNormal, -dot(alignedPlaneNormal, ecStart), eyeCoordinate.xyz);\n\ \n\ // end aligned plane\n\ alignedPlaneNormal = cross(v_rightPlaneEC.xyz, v_endPlaneNormalEcAndBatchId.xyz);\n\ alignedPlaneNormal = normalize(cross(alignedPlaneNormal, v_rightPlaneEC.xyz));\n\ distanceFromEnd = czm_planeDistance(alignedPlaneNormal, -dot(alignedPlaneNormal, v_endEcAndStartEcX.xyz), eyeCoordinate.xyz);\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ gl_FragColor = czm_gammaCorrect(v_color);\n\ #else // PER_INSTANCE_COLOR\n\ // Clamp - distance to aligned planes may be negative due to mitering,\n\ // so fragment texture coordinate might be out-of-bounds.\n\ float s = clamp(distanceFromStart / (distanceFromStart + distanceFromEnd), 0.0, 1.0);\n\ s = (s * v_texcoordNormalizationAndStartEcYZ.x) + v_texcoordNormalizationAndStartEcYZ.y;\n\ float t = (widthwiseDistance + halfMaxWidth) / (2.0 * halfMaxWidth);\n\ \n\ czm_materialInput materialInput;\n\ \n\ materialInput.s = s;\n\ materialInput.st = vec2(s, t);\n\ materialInput.str = vec3(s, t, 0.0);\n\ \n\ czm_material material = czm_getMaterial(materialInput);\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #endif // PER_INSTANCE_COLOR\n\ \n\ // Premultiply alpha. Required for classification primitives on translucent globe.\n\ gl_FragColor.rgb *= gl_FragColor.a;\n\ \n\ czm_writeDepthClamp();\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineShadowVolumeMorphFS = "varying vec3 v_forwardDirectionEC;\n\ varying vec3 v_texcoordNormalizationAndHalfWidth;\n\ varying float v_batchId;\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #else\n\ varying vec2 v_alignedPlaneDistances;\n\ varying float v_texcoordT;\n\ #endif\n\ \n\ float rayPlaneDistanceUnsafe(vec3 origin, vec3 direction, vec3 planeNormal, float planeDistance) {\n\ // We don't expect the ray to ever be parallel to the plane\n\ return (-planeDistance - dot(planeNormal, origin)) / dot(planeNormal, direction);\n\ }\n\ \n\ void main(void)\n\ {\n\ vec4 eyeCoordinate = gl_FragCoord;\n\ eyeCoordinate /= eyeCoordinate.w;\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ gl_FragColor = czm_gammaCorrect(v_color);\n\ #else // PER_INSTANCE_COLOR\n\ // Use distances for planes aligned with segment to prevent skew in dashing\n\ float distanceFromStart = rayPlaneDistanceUnsafe(eyeCoordinate.xyz, -v_forwardDirectionEC, v_forwardDirectionEC.xyz, v_alignedPlaneDistances.x);\n\ float distanceFromEnd = rayPlaneDistanceUnsafe(eyeCoordinate.xyz, v_forwardDirectionEC, -v_forwardDirectionEC.xyz, v_alignedPlaneDistances.y);\n\ \n\ // Clamp - distance to aligned planes may be negative due to mitering\n\ distanceFromStart = max(0.0, distanceFromStart);\n\ distanceFromEnd = max(0.0, distanceFromEnd);\n\ \n\ float s = distanceFromStart / (distanceFromStart + distanceFromEnd);\n\ s = (s * v_texcoordNormalizationAndHalfWidth.x) + v_texcoordNormalizationAndHalfWidth.y;\n\ \n\ czm_materialInput materialInput;\n\ \n\ materialInput.s = s;\n\ materialInput.st = vec2(s, v_texcoordT);\n\ materialInput.str = vec3(s, v_texcoordT, 0.0);\n\ \n\ czm_material material = czm_getMaterial(materialInput);\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #endif // PER_INSTANCE_COLOR\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineShadowVolumeMorphVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ \n\ attribute vec4 startHiAndForwardOffsetX;\n\ attribute vec4 startLoAndForwardOffsetY;\n\ attribute vec4 startNormalAndForwardOffsetZ;\n\ attribute vec4 endNormalAndTextureCoordinateNormalizationX;\n\ attribute vec4 rightNormalAndTextureCoordinateNormalizationY;\n\ attribute vec4 startHiLo2D;\n\ attribute vec4 offsetAndRight2D;\n\ attribute vec4 startEndNormals2D;\n\ attribute vec2 texcoordNormalization2D;\n\ \n\ attribute float batchId;\n\ \n\ varying vec3 v_forwardDirectionEC;\n\ varying vec3 v_texcoordNormalizationAndHalfWidth;\n\ varying float v_batchId;\n\ \n\ // For materials\n\ #ifdef WIDTH_VARYING\n\ varying float v_width;\n\ #endif\n\ #ifdef ANGLE_VARYING\n\ varying float v_polylineAngle;\n\ #endif\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #else\n\ varying vec2 v_alignedPlaneDistances;\n\ varying float v_texcoordT;\n\ #endif\n\ \n\ // Morphing planes using SLERP or NLERP doesn't seem to work, so instead draw the material directly on the shadow volume.\n\ // Morph views are from very far away and aren't meant to be used precisely, so this should be sufficient.\n\ void main()\n\ {\n\ v_batchId = batchId;\n\ \n\ // Start position\n\ vec4 posRelativeToEye2D = czm_translateRelativeToEye(vec3(0.0, startHiLo2D.xy), vec3(0.0, startHiLo2D.zw));\n\ vec4 posRelativeToEye3D = czm_translateRelativeToEye(startHiAndForwardOffsetX.xyz, startLoAndForwardOffsetY.xyz);\n\ vec4 posRelativeToEye = czm_columbusViewMorph(posRelativeToEye2D, posRelativeToEye3D, czm_morphTime);\n\ vec3 posEc2D = (czm_modelViewRelativeToEye * posRelativeToEye2D).xyz;\n\ vec3 posEc3D = (czm_modelViewRelativeToEye * posRelativeToEye3D).xyz;\n\ vec3 startEC = (czm_modelViewRelativeToEye * posRelativeToEye).xyz;\n\ \n\ // Start plane\n\ vec4 startPlane2D;\n\ vec4 startPlane3D;\n\ startPlane2D.xyz = czm_normal * vec3(0.0, startEndNormals2D.xy);\n\ startPlane3D.xyz = czm_normal * startNormalAndForwardOffsetZ.xyz;\n\ startPlane2D.w = -dot(startPlane2D.xyz, posEc2D);\n\ startPlane3D.w = -dot(startPlane3D.xyz, posEc3D);\n\ \n\ // Right plane\n\ vec4 rightPlane2D;\n\ vec4 rightPlane3D;\n\ rightPlane2D.xyz = czm_normal * vec3(0.0, offsetAndRight2D.zw);\n\ rightPlane3D.xyz = czm_normal * rightNormalAndTextureCoordinateNormalizationY.xyz;\n\ rightPlane2D.w = -dot(rightPlane2D.xyz, posEc2D);\n\ rightPlane3D.w = -dot(rightPlane3D.xyz, posEc3D);\n\ \n\ // End position\n\ posRelativeToEye2D = posRelativeToEye2D + vec4(0.0, offsetAndRight2D.xy, 0.0);\n\ posRelativeToEye3D = posRelativeToEye3D + vec4(startHiAndForwardOffsetX.w, startLoAndForwardOffsetY.w, startNormalAndForwardOffsetZ.w, 0.0);\n\ posRelativeToEye = czm_columbusViewMorph(posRelativeToEye2D, posRelativeToEye3D, czm_morphTime);\n\ posEc2D = (czm_modelViewRelativeToEye * posRelativeToEye2D).xyz;\n\ posEc3D = (czm_modelViewRelativeToEye * posRelativeToEye3D).xyz;\n\ vec3 endEC = (czm_modelViewRelativeToEye * posRelativeToEye).xyz;\n\ vec3 forwardEc3D = czm_normal * normalize(vec3(startHiAndForwardOffsetX.w, startLoAndForwardOffsetY.w, startNormalAndForwardOffsetZ.w));\n\ vec3 forwardEc2D = czm_normal * normalize(vec3(0.0, offsetAndRight2D.xy));\n\ \n\ // End plane\n\ vec4 endPlane2D;\n\ vec4 endPlane3D;\n\ endPlane2D.xyz = czm_normal * vec3(0.0, startEndNormals2D.zw);\n\ endPlane3D.xyz = czm_normal * endNormalAndTextureCoordinateNormalizationX.xyz;\n\ endPlane2D.w = -dot(endPlane2D.xyz, posEc2D);\n\ endPlane3D.w = -dot(endPlane3D.xyz, posEc3D);\n\ \n\ // Forward direction\n\ v_forwardDirectionEC = normalize(endEC - startEC);\n\ \n\ vec2 cleanTexcoordNormalization2D;\n\ cleanTexcoordNormalization2D.x = abs(texcoordNormalization2D.x);\n\ cleanTexcoordNormalization2D.y = czm_branchFreeTernary(texcoordNormalization2D.y > 1.0, 0.0, abs(texcoordNormalization2D.y));\n\ vec2 cleanTexcoordNormalization3D;\n\ cleanTexcoordNormalization3D.x = abs(endNormalAndTextureCoordinateNormalizationX.w);\n\ cleanTexcoordNormalization3D.y = rightNormalAndTextureCoordinateNormalizationY.w;\n\ cleanTexcoordNormalization3D.y = czm_branchFreeTernary(cleanTexcoordNormalization3D.y > 1.0, 0.0, abs(cleanTexcoordNormalization3D.y));\n\ \n\ v_texcoordNormalizationAndHalfWidth.xy = mix(cleanTexcoordNormalization2D, cleanTexcoordNormalization3D, czm_morphTime);\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ v_color = czm_batchTable_color(batchId);\n\ #else // PER_INSTANCE_COLOR\n\ // For computing texture coordinates\n\ \n\ v_alignedPlaneDistances.x = -dot(v_forwardDirectionEC, startEC);\n\ v_alignedPlaneDistances.y = -dot(-v_forwardDirectionEC, endEC);\n\ #endif // PER_INSTANCE_COLOR\n\ \n\ #ifdef WIDTH_VARYING\n\ float width = czm_batchTable_width(batchId);\n\ float halfWidth = width * 0.5;\n\ v_width = width;\n\ v_texcoordNormalizationAndHalfWidth.z = halfWidth;\n\ #else\n\ float halfWidth = 0.5 * czm_batchTable_width(batchId);\n\ v_texcoordNormalizationAndHalfWidth.z = halfWidth;\n\ #endif\n\ \n\ // Compute a normal along which to \"push\" the position out, extending the miter depending on view distance.\n\ // Position has already been \"pushed\" by unit length along miter normal, and miter normals are encoded in the planes.\n\ // Decode the normal to use at this specific vertex, push the position back, and then push to where it needs to be.\n\ // Since this is morphing, compute both 3D and 2D positions and then blend.\n\ \n\ // ****** 3D ******\n\ // Check distance to the end plane and start plane, pick the plane that is closer\n\ vec4 positionEc3D = czm_modelViewRelativeToEye * czm_translateRelativeToEye(position3DHigh, position3DLow); // w = 1.0, see czm_computePosition\n\ float absStartPlaneDistance = abs(czm_planeDistance(startPlane3D, positionEc3D.xyz));\n\ float absEndPlaneDistance = abs(czm_planeDistance(endPlane3D, positionEc3D.xyz));\n\ vec3 planeDirection = czm_branchFreeTernary(absStartPlaneDistance < absEndPlaneDistance, startPlane3D.xyz, endPlane3D.xyz);\n\ vec3 upOrDown = normalize(cross(rightPlane3D.xyz, planeDirection)); // Points \"up\" for start plane, \"down\" at end plane.\n\ vec3 normalEC = normalize(cross(planeDirection, upOrDown)); // In practice, the opposite seems to work too.\n\ \n\ // Nudge the top vertex upwards to prevent flickering\n\ vec3 geodeticSurfaceNormal = normalize(cross(normalEC, forwardEc3D));\n\ geodeticSurfaceNormal *= float(0.0 <= rightNormalAndTextureCoordinateNormalizationY.w && rightNormalAndTextureCoordinateNormalizationY.w <= 1.0);\n\ geodeticSurfaceNormal *= MAX_TERRAIN_HEIGHT;\n\ positionEc3D.xyz += geodeticSurfaceNormal;\n\ \n\ // Determine if this vertex is on the \"left\" or \"right\"\n\ normalEC *= sign(endNormalAndTextureCoordinateNormalizationX.w);\n\ \n\ // A \"perfect\" implementation would push along normals according to the angle against forward.\n\ // In practice, just pushing the normal out by halfWidth is sufficient for morph views.\n\ positionEc3D.xyz += halfWidth * max(0.0, czm_metersPerPixel(positionEc3D)) * normalEC; // prevent artifacts when czm_metersPerPixel is negative (behind camera)\n\ \n\ // ****** 2D ******\n\ // Check distance to the end plane and start plane, pick the plane that is closer\n\ vec4 positionEc2D = czm_modelViewRelativeToEye * czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy); // w = 1.0, see czm_computePosition\n\ absStartPlaneDistance = abs(czm_planeDistance(startPlane2D, positionEc2D.xyz));\n\ absEndPlaneDistance = abs(czm_planeDistance(endPlane2D, positionEc2D.xyz));\n\ planeDirection = czm_branchFreeTernary(absStartPlaneDistance < absEndPlaneDistance, startPlane2D.xyz, endPlane2D.xyz);\n\ upOrDown = normalize(cross(rightPlane2D.xyz, planeDirection)); // Points \"up\" for start plane, \"down\" at end plane.\n\ normalEC = normalize(cross(planeDirection, upOrDown)); // In practice, the opposite seems to work too.\n\ \n\ // Nudge the top vertex upwards to prevent flickering\n\ geodeticSurfaceNormal = normalize(cross(normalEC, forwardEc2D));\n\ geodeticSurfaceNormal *= float(0.0 <= texcoordNormalization2D.y && texcoordNormalization2D.y <= 1.0);\n\ geodeticSurfaceNormal *= MAX_TERRAIN_HEIGHT;\n\ positionEc2D.xyz += geodeticSurfaceNormal;\n\ \n\ // Determine if this vertex is on the \"left\" or \"right\"\n\ normalEC *= sign(texcoordNormalization2D.x);\n\ #ifndef PER_INSTANCE_COLOR\n\ // Use vertex's sidedness to compute its texture coordinate.\n\ v_texcoordT = clamp(sign(texcoordNormalization2D.x), 0.0, 1.0);\n\ #endif\n\ \n\ // A \"perfect\" implementation would push along normals according to the angle against forward.\n\ // In practice, just pushing the normal out by halfWidth is sufficient for morph views.\n\ positionEc2D.xyz += halfWidth * max(0.0, czm_metersPerPixel(positionEc2D)) * normalEC; // prevent artifacts when czm_metersPerPixel is negative (behind camera)\n\ \n\ // Blend for actual position\n\ gl_Position = czm_projection * mix(positionEc2D, positionEc3D, czm_morphTime);\n\ \n\ #ifdef ANGLE_VARYING\n\ // Approximate relative screen space direction of the line.\n\ vec2 approxLineDirection = normalize(vec2(v_forwardDirectionEC.x, -v_forwardDirectionEC.y));\n\ approxLineDirection.y = czm_branchFreeTernary(approxLineDirection.x == 0.0 && approxLineDirection.y == 0.0, -1.0, approxLineDirection.y);\n\ v_polylineAngle = czm_fastApproximateAtan(approxLineDirection.x, approxLineDirection.y);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineShadowVolumeVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ \n\ // In 2D and in 3D, texture coordinate normalization component signs encodes:\n\ // * X sign - sidedness relative to right plane\n\ // * Y sign - is negative OR magnitude is greater than 1.0 if vertex is on bottom of volume\n\ #ifndef COLUMBUS_VIEW_2D\n\ attribute vec4 startHiAndForwardOffsetX;\n\ attribute vec4 startLoAndForwardOffsetY;\n\ attribute vec4 startNormalAndForwardOffsetZ;\n\ attribute vec4 endNormalAndTextureCoordinateNormalizationX;\n\ attribute vec4 rightNormalAndTextureCoordinateNormalizationY;\n\ #else\n\ attribute vec4 startHiLo2D;\n\ attribute vec4 offsetAndRight2D;\n\ attribute vec4 startEndNormals2D;\n\ attribute vec2 texcoordNormalization2D;\n\ #endif\n\ \n\ attribute float batchId;\n\ \n\ varying vec4 v_startPlaneNormalEcAndHalfWidth;\n\ varying vec4 v_endPlaneNormalEcAndBatchId;\n\ varying vec4 v_rightPlaneEC;\n\ varying vec4 v_endEcAndStartEcX;\n\ varying vec4 v_texcoordNormalizationAndStartEcYZ;\n\ \n\ // For materials\n\ #ifdef WIDTH_VARYING\n\ varying float v_width;\n\ #endif\n\ #ifdef ANGLE_VARYING\n\ varying float v_polylineAngle;\n\ #endif\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ varying vec4 v_color;\n\ #endif\n\ \n\ void main()\n\ {\n\ #ifdef COLUMBUS_VIEW_2D\n\ vec3 ecStart = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(vec3(0.0, startHiLo2D.xy), vec3(0.0, startHiLo2D.zw))).xyz;\n\ \n\ vec3 forwardDirectionEC = czm_normal * vec3(0.0, offsetAndRight2D.xy);\n\ vec3 ecEnd = forwardDirectionEC + ecStart;\n\ forwardDirectionEC = normalize(forwardDirectionEC);\n\ \n\ // Right plane\n\ v_rightPlaneEC.xyz = czm_normal * vec3(0.0, offsetAndRight2D.zw);\n\ v_rightPlaneEC.w = -dot(v_rightPlaneEC.xyz, ecStart);\n\ \n\ // start plane\n\ vec4 startPlaneEC;\n\ startPlaneEC.xyz = czm_normal * vec3(0.0, startEndNormals2D.xy);\n\ startPlaneEC.w = -dot(startPlaneEC.xyz, ecStart);\n\ \n\ // end plane\n\ vec4 endPlaneEC;\n\ endPlaneEC.xyz = czm_normal * vec3(0.0, startEndNormals2D.zw);\n\ endPlaneEC.w = -dot(endPlaneEC.xyz, ecEnd);\n\ \n\ v_texcoordNormalizationAndStartEcYZ.x = abs(texcoordNormalization2D.x);\n\ v_texcoordNormalizationAndStartEcYZ.y = texcoordNormalization2D.y;\n\ \n\ #else // COLUMBUS_VIEW_2D\n\ vec3 ecStart = (czm_modelViewRelativeToEye * czm_translateRelativeToEye(startHiAndForwardOffsetX.xyz, startLoAndForwardOffsetY.xyz)).xyz;\n\ vec3 offset = czm_normal * vec3(startHiAndForwardOffsetX.w, startLoAndForwardOffsetY.w, startNormalAndForwardOffsetZ.w);\n\ vec3 ecEnd = ecStart + offset;\n\ \n\ vec3 forwardDirectionEC = normalize(offset);\n\ \n\ // start plane\n\ vec4 startPlaneEC;\n\ startPlaneEC.xyz = czm_normal * startNormalAndForwardOffsetZ.xyz;\n\ startPlaneEC.w = -dot(startPlaneEC.xyz, ecStart);\n\ \n\ // end plane\n\ vec4 endPlaneEC;\n\ endPlaneEC.xyz = czm_normal * endNormalAndTextureCoordinateNormalizationX.xyz;\n\ endPlaneEC.w = -dot(endPlaneEC.xyz, ecEnd);\n\ \n\ // Right plane\n\ v_rightPlaneEC.xyz = czm_normal * rightNormalAndTextureCoordinateNormalizationY.xyz;\n\ v_rightPlaneEC.w = -dot(v_rightPlaneEC.xyz, ecStart);\n\ \n\ v_texcoordNormalizationAndStartEcYZ.x = abs(endNormalAndTextureCoordinateNormalizationX.w);\n\ v_texcoordNormalizationAndStartEcYZ.y = rightNormalAndTextureCoordinateNormalizationY.w;\n\ \n\ #endif // COLUMBUS_VIEW_2D\n\ \n\ v_endEcAndStartEcX.xyz = ecEnd;\n\ v_endEcAndStartEcX.w = ecStart.x;\n\ v_texcoordNormalizationAndStartEcYZ.zw = ecStart.yz;\n\ \n\ #ifdef PER_INSTANCE_COLOR\n\ v_color = czm_batchTable_color(batchId);\n\ #endif // PER_INSTANCE_COLOR\n\ \n\ // Compute a normal along which to \"push\" the position out, extending the miter depending on view distance.\n\ // Position has already been \"pushed\" by unit length along miter normal, and miter normals are encoded in the planes.\n\ // Decode the normal to use at this specific vertex, push the position back, and then push to where it needs to be.\n\ vec4 positionRelativeToEye = czm_computePosition();\n\ \n\ // Check distance to the end plane and start plane, pick the plane that is closer\n\ vec4 positionEC = czm_modelViewRelativeToEye * positionRelativeToEye; // w = 1.0, see czm_computePosition\n\ float absStartPlaneDistance = abs(czm_planeDistance(startPlaneEC, positionEC.xyz));\n\ float absEndPlaneDistance = abs(czm_planeDistance(endPlaneEC, positionEC.xyz));\n\ vec3 planeDirection = czm_branchFreeTernary(absStartPlaneDistance < absEndPlaneDistance, startPlaneEC.xyz, endPlaneEC.xyz);\n\ vec3 upOrDown = normalize(cross(v_rightPlaneEC.xyz, planeDirection)); // Points \"up\" for start plane, \"down\" at end plane.\n\ vec3 normalEC = normalize(cross(planeDirection, upOrDown)); // In practice, the opposite seems to work too.\n\ \n\ // Extrude bottom vertices downward for far view distances, like for GroundPrimitives\n\ upOrDown = cross(forwardDirectionEC, normalEC);\n\ upOrDown = float(czm_sceneMode == czm_sceneMode3D) * upOrDown;\n\ upOrDown = float(v_texcoordNormalizationAndStartEcYZ.y > 1.0 || v_texcoordNormalizationAndStartEcYZ.y < 0.0) * upOrDown;\n\ upOrDown = min(GLOBE_MINIMUM_ALTITUDE, czm_geometricToleranceOverMeter * length(positionRelativeToEye.xyz)) * upOrDown;\n\ positionEC.xyz += upOrDown;\n\ \n\ v_texcoordNormalizationAndStartEcYZ.y = czm_branchFreeTernary(v_texcoordNormalizationAndStartEcYZ.y > 1.0, 0.0, abs(v_texcoordNormalizationAndStartEcYZ.y));\n\ \n\ // Determine distance along normalEC to push for a volume of appropriate width.\n\ // Make volumes about double pixel width for a conservative fit - in practice the\n\ // extra cost here is minimal compared to the loose volume heights.\n\ //\n\ // N = normalEC (guaranteed \"right-facing\")\n\ // R = rightEC\n\ // p = angle between N and R\n\ // w = distance to push along R if R == N\n\ // d = distance to push along N\n\ //\n\ // N R\n\ // { \ p| } * cos(p) = dot(N, R) = w / d\n\ // d\ \ | |w * d = w / dot(N, R)\n\ // { \| }\n\ // o---------- polyline segment ---->\n\ //\n\ float width = czm_batchTable_width(batchId);\n\ #ifdef WIDTH_VARYING\n\ v_width = width;\n\ #endif\n\ \n\ v_startPlaneNormalEcAndHalfWidth.xyz = startPlaneEC.xyz;\n\ v_startPlaneNormalEcAndHalfWidth.w = width * 0.5;\n\ \n\ v_endPlaneNormalEcAndBatchId.xyz = endPlaneEC.xyz;\n\ v_endPlaneNormalEcAndBatchId.w = batchId;\n\ \n\ width = width * max(0.0, czm_metersPerPixel(positionEC)); // width = distance to push along R\n\ width = width / dot(normalEC, v_rightPlaneEC.xyz); // width = distance to push along N\n\ \n\ // Determine if this vertex is on the \"left\" or \"right\"\n\ #ifdef COLUMBUS_VIEW_2D\n\ normalEC *= sign(texcoordNormalization2D.x);\n\ #else\n\ normalEC *= sign(endNormalAndTextureCoordinateNormalizationX.w);\n\ #endif\n\ \n\ positionEC.xyz += width * normalEC;\n\ gl_Position = czm_depthClamp(czm_projection * positionEC);\n\ \n\ #ifdef ANGLE_VARYING\n\ // Approximate relative screen space direction of the line.\n\ vec2 approxLineDirection = normalize(vec2(forwardDirectionEC.x, -forwardDirectionEC.y));\n\ approxLineDirection.y = czm_branchFreeTernary(approxLineDirection.x == 0.0 && approxLineDirection.y == 0.0, -1.0, approxLineDirection.y);\n\ v_polylineAngle = czm_fastApproximateAtan(approxLineDirection.x, approxLineDirection.y);\n\ #endif\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineColorAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 prevPosition3DHigh;\n\ attribute vec3 prevPosition3DLow;\n\ attribute vec3 nextPosition3DHigh;\n\ attribute vec3 nextPosition3DLow;\n\ attribute vec2 expandAndWidth;\n\ attribute vec4 color;\n\ attribute float batchId;\n\ \n\ varying vec4 v_color;\n\ \n\ void main()\n\ {\n\ float expandDir = expandAndWidth.x;\n\ float width = abs(expandAndWidth.y) + 0.5;\n\ bool usePrev = expandAndWidth.y < 0.0;\n\ \n\ vec4 p = czm_computePosition();\n\ vec4 prev = czm_computePrevPosition();\n\ vec4 next = czm_computeNextPosition();\n\ \n\ float angle;\n\ vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle);\n\ gl_Position = czm_viewportOrthographic * positionWC;\n\ \n\ v_color = color;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineCommon = "void clipLineSegmentToNearPlane(\n\ vec3 p0,\n\ vec3 p1,\n\ out vec4 positionWC,\n\ out bool clipped,\n\ out bool culledByNearPlane,\n\ out vec4 clippedPositionEC)\n\ {\n\ culledByNearPlane = false;\n\ clipped = false;\n\ \n\ vec3 p0ToP1 = p1 - p0;\n\ float magnitude = length(p0ToP1);\n\ vec3 direction = normalize(p0ToP1);\n\ \n\ // Distance that p0 is behind the near plane. Negative means p0 is\n\ // in front of the near plane.\n\ float endPoint0Distance = czm_currentFrustum.x + p0.z;\n\ \n\ // Camera looks down -Z.\n\ // When moving a point along +Z: LESS VISIBLE\n\ // * Points in front of the camera move closer to the camera.\n\ // * Points behind the camrea move farther away from the camera.\n\ // When moving a point along -Z: MORE VISIBLE\n\ // * Points in front of the camera move farther away from the camera.\n\ // * Points behind the camera move closer to the camera.\n\ \n\ // Positive denominator: -Z, becoming more visible\n\ // Negative denominator: +Z, becoming less visible\n\ // Nearly zero: parallel to near plane\n\ float denominator = -direction.z;\n\ \n\ if (endPoint0Distance > 0.0 && abs(denominator) < czm_epsilon7)\n\ {\n\ // p0 is behind the near plane and the line to p1 is nearly parallel to\n\ // the near plane, so cull the segment completely.\n\ culledByNearPlane = true;\n\ }\n\ else if (endPoint0Distance > 0.0)\n\ {\n\ // p0 is behind the near plane, and the line to p1 is moving distinctly\n\ // toward or away from it.\n\ \n\ // t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction)\n\ float t = endPoint0Distance / denominator;\n\ if (t < 0.0 || t > magnitude)\n\ {\n\ // Near plane intersection is not between the two points.\n\ // We already confirmed p0 is behind the naer plane, so now\n\ // we know the entire segment is behind it.\n\ culledByNearPlane = true;\n\ }\n\ else\n\ {\n\ // Segment crosses the near plane, update p0 to lie exactly on it.\n\ p0 = p0 + t * direction;\n\ \n\ // Numerical noise might put us a bit on the wrong side of the near plane.\n\ // Don't let that happen.\n\ p0.z = min(p0.z, -czm_currentFrustum.x);\n\ \n\ clipped = true;\n\ }\n\ }\n\ \n\ clippedPositionEC = vec4(p0, 1.0);\n\ positionWC = czm_eyeToWindowCoordinates(clippedPositionEC);\n\ }\n\ \n\ vec4 getPolylineWindowCoordinatesEC(vec4 positionEC, vec4 prevEC, vec4 nextEC, float expandDirection, float width, bool usePrevious, out float angle)\n\ {\n\ // expandDirection +1 is to the _left_ when looking from positionEC toward nextEC.\n\ \n\ #ifdef POLYLINE_DASH\n\ // Compute the window coordinates of the points.\n\ vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC);\n\ vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC);\n\ vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC);\n\ \n\ // Determine the relative screen space direction of the line.\n\ vec2 lineDir;\n\ if (usePrevious) {\n\ lineDir = normalize(positionWindow.xy - previousWindow.xy);\n\ }\n\ else {\n\ lineDir = normalize(nextWindow.xy - positionWindow.xy);\n\ }\n\ angle = atan(lineDir.x, lineDir.y) - 1.570796327; // precomputed atan(1,0)\n\ \n\ // Quantize the angle so it doesn't change rapidly between segments.\n\ angle = floor(angle / czm_piOverFour + 0.5) * czm_piOverFour;\n\ #endif\n\ \n\ vec4 clippedPrevWC, clippedPrevEC;\n\ bool prevSegmentClipped, prevSegmentCulled;\n\ clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, clippedPrevWC, prevSegmentClipped, prevSegmentCulled, clippedPrevEC);\n\ \n\ vec4 clippedNextWC, clippedNextEC;\n\ bool nextSegmentClipped, nextSegmentCulled;\n\ clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, clippedNextWC, nextSegmentClipped, nextSegmentCulled, clippedNextEC);\n\ \n\ bool segmentClipped, segmentCulled;\n\ vec4 clippedPositionWC, clippedPositionEC;\n\ clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, clippedPositionWC, segmentClipped, segmentCulled, clippedPositionEC);\n\ \n\ if (segmentCulled)\n\ {\n\ return vec4(0.0, 0.0, 0.0, 1.0);\n\ }\n\ \n\ vec2 directionToPrevWC = normalize(clippedPrevWC.xy - clippedPositionWC.xy);\n\ vec2 directionToNextWC = normalize(clippedNextWC.xy - clippedPositionWC.xy);\n\ \n\ // If a segment was culled, we can't use the corresponding direction\n\ // computed above. We should never see both of these be true without\n\ // `segmentCulled` above also being true.\n\ if (prevSegmentCulled)\n\ {\n\ directionToPrevWC = -directionToNextWC;\n\ }\n\ else if (nextSegmentCulled)\n\ {\n\ directionToNextWC = -directionToPrevWC;\n\ }\n\ \n\ vec2 thisSegmentForwardWC, otherSegmentForwardWC;\n\ if (usePrevious)\n\ {\n\ thisSegmentForwardWC = -directionToPrevWC;\n\ otherSegmentForwardWC = directionToNextWC;\n\ }\n\ else\n\ {\n\ thisSegmentForwardWC = directionToNextWC;\n\ otherSegmentForwardWC = -directionToPrevWC;\n\ }\n\ \n\ vec2 thisSegmentLeftWC = vec2(-thisSegmentForwardWC.y, thisSegmentForwardWC.x);\n\ \n\ vec2 leftWC = thisSegmentLeftWC;\n\ float expandWidth = width * 0.5;\n\ \n\ // When lines are split at the anti-meridian, the position may be at the\n\ // same location as the next or previous position, and we need to handle\n\ // that to avoid producing NaNs.\n\ if (!czm_equalsEpsilon(prevEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1) && !czm_equalsEpsilon(nextEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1))\n\ {\n\ vec2 otherSegmentLeftWC = vec2(-otherSegmentForwardWC.y, otherSegmentForwardWC.x);\n\ \n\ vec2 leftSumWC = thisSegmentLeftWC + otherSegmentLeftWC;\n\ float leftSumLength = length(leftSumWC);\n\ leftWC = leftSumLength < czm_epsilon6 ? thisSegmentLeftWC : (leftSumWC / leftSumLength);\n\ \n\ // The sine of the angle between the two vectors is given by the formula\n\ // |a x b| = |a||b|sin(theta)\n\ // which is\n\ // float sinAngle = length(cross(vec3(leftWC, 0.0), vec3(-thisSegmentForwardWC, 0.0)));\n\ // Because the z components of both vectors are zero, the x and y coordinate will be zero.\n\ // Therefore, the sine of the angle is just the z component of the cross product.\n\ vec2 u = -thisSegmentForwardWC;\n\ vec2 v = leftWC;\n\ float sinAngle = abs(u.x * v.y - u.y * v.x);\n\ expandWidth = clamp(expandWidth / sinAngle, 0.0, width * 2.0);\n\ }\n\ \n\ vec2 offset = leftWC * expandDirection * expandWidth * czm_pixelRatio;\n\ return vec4(clippedPositionWC.xy + offset, -clippedPositionWC.z, 1.0) * (czm_projection * clippedPositionEC).w;\n\ }\n\ \n\ vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)\n\ {\n\ vec4 positionEC = czm_modelViewRelativeToEye * position;\n\ vec4 prevEC = czm_modelViewRelativeToEye * previous;\n\ vec4 nextEC = czm_modelViewRelativeToEye * next;\n\ return getPolylineWindowCoordinatesEC(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, angle);\n\ }\n\ "; var defaultVertexShaderSource$1 = PolylineCommon + "\n" + PolylineColorAppearanceVS; var defaultFragmentShaderSource$1 = PerInstanceFlatColorAppearanceFS; if (!FeatureDetection.isInternetExplorer()) { defaultVertexShaderSource$1 = "#define CLIP_POLYLINE \n" + defaultVertexShaderSource$1; } /** * An appearance for {@link GeometryInstance} instances with color attributes and * {@link PolylineGeometry} or {@link GroundPolylineGeometry}. * This allows several geometry instances, each with a different color, to * be drawn with the same {@link Primitive}. * * @alias PolylineColorAppearance * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link PolylineColorAppearance#renderState} has alpha blending enabled. * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {Object} [options.renderState] Optional render state to override the default render state. * * @example * // A solid white line segment * var primitive = new Cesium.Primitive({ * geometryInstances : new Cesium.GeometryInstance({ * geometry : new Cesium.PolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 5.0, 0.0 * ]), * width : 10.0, * vertexFormat : Cesium.PolylineColorAppearance.VERTEX_FORMAT * }), * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 1.0, 1.0, 1.0)) * } * }), * appearance : new Cesium.PolylineColorAppearance({ * translucent : false * }) * }); */ function PolylineColorAppearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var translucent = defaultValue(options.translucent, true); var closed = false; var vertexFormat = PolylineColorAppearance.VERTEX_FORMAT; /** * This property is part of the {@link Appearance} interface, but is not * used by {@link PolylineColorAppearance} since a fully custom fragment shader is used. * * @type Material * * @default undefined */ this.material = undefined; /** * When true, the geometry is expected to appear translucent so * {@link PolylineColorAppearance#renderState} has alpha blending enabled. * * @type {Boolean} * * @default true */ this.translucent = translucent; this._vertexShaderSource = defaultValue( options.vertexShaderSource, defaultVertexShaderSource$1 ); this._fragmentShaderSource = defaultValue( options.fragmentShaderSource, defaultFragmentShaderSource$1 ); this._renderState = Appearance.getDefaultRenderState( translucent, closed, options.renderState ); this._closed = closed; // Non-derived members this._vertexFormat = vertexFormat; } Object.defineProperties(PolylineColorAppearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof PolylineColorAppearance.prototype * * @type {String} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * The GLSL source code for the fragment shader. * * @memberof PolylineColorAppearance.prototype * * @type {String} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. *

* The render state can be explicitly defined when constructing a {@link PolylineColorAppearance} * instance, or it is set implicitly via {@link PolylineColorAppearance#translucent}. *

* * @memberof PolylineColorAppearance.prototype * * @type {Object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed so * {@link PolylineColorAppearance#renderState} has backface culling enabled. * This is always false for PolylineColorAppearance. * * @memberof PolylineColorAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, /** * The {@link VertexFormat} that this appearance instance is compatible with. * A geometry can have more vertex attributes and still be compatible - at a * potential performance cost - but it can't have less. * * @memberof PolylineColorAppearance.prototype * * @type VertexFormat * @readonly * * @default {@link PolylineColorAppearance.VERTEX_FORMAT} */ vertexFormat: { get: function () { return this._vertexFormat; }, }, }); /** * The {@link VertexFormat} that all {@link PolylineColorAppearance} instances * are compatible with. This requires only a position attribute. * * @type VertexFormat * * @constant */ PolylineColorAppearance.VERTEX_FORMAT = VertexFormat.POSITION_ONLY; /** * Procedurally creates the full GLSL fragment shader source. * * @function * * @returns {String} The full GLSL fragment shader source. */ PolylineColorAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource; /** * Determines if the geometry is translucent based on {@link PolylineColorAppearance#translucent}. * * @function * * @returns {Boolean} true if the appearance is translucent. */ PolylineColorAppearance.prototype.isTranslucent = Appearance.prototype.isTranslucent; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @function * * @returns {Object} The render state. */ PolylineColorAppearance.prototype.getRenderState = Appearance.prototype.getRenderState; //This file is automatically rebuilt by the Cesium build process. var PolylineMaterialAppearanceVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 prevPosition3DHigh;\n\ attribute vec3 prevPosition3DLow;\n\ attribute vec3 nextPosition3DHigh;\n\ attribute vec3 nextPosition3DLow;\n\ attribute vec2 expandAndWidth;\n\ attribute vec2 st;\n\ attribute float batchId;\n\ \n\ varying float v_width;\n\ varying vec2 v_st;\n\ varying float v_polylineAngle;\n\ \n\ void main()\n\ {\n\ float expandDir = expandAndWidth.x;\n\ float width = abs(expandAndWidth.y) + 0.5;\n\ bool usePrev = expandAndWidth.y < 0.0;\n\ \n\ vec4 p = czm_computePosition();\n\ vec4 prev = czm_computePrevPosition();\n\ vec4 next = czm_computeNextPosition();\n\ \n\ float angle;\n\ vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle);\n\ gl_Position = czm_viewportOrthographic * positionWC;\n\ \n\ v_width = width;\n\ v_st.s = st.s;\n\ v_st.t = czm_writeNonPerspective(st.t, gl_Position.w);\n\ v_polylineAngle = angle;\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PolylineFS$1 = "#ifdef VECTOR_TILE\n\ uniform vec4 u_highlightColor;\n\ #endif\n\ \n\ varying vec2 v_st;\n\ \n\ void main()\n\ {\n\ czm_materialInput materialInput;\n\ \n\ vec2 st = v_st;\n\ st.t = czm_readNonPerspective(st.t, gl_FragCoord.w);\n\ \n\ materialInput.s = st.s;\n\ materialInput.st = st;\n\ materialInput.str = vec3(st, 0.0);\n\ \n\ czm_material material = czm_getMaterial(materialInput);\n\ gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n\ #ifdef VECTOR_TILE\n\ gl_FragColor *= u_highlightColor;\n\ #endif\n\ \n\ czm_writeLogDepth();\n\ }\n\ "; var defaultVertexShaderSource = PolylineCommon + "\n" + PolylineMaterialAppearanceVS; var defaultFragmentShaderSource = PolylineFS$1; if (!FeatureDetection.isInternetExplorer()) { defaultVertexShaderSource = "#define CLIP_POLYLINE \n" + defaultVertexShaderSource; } /** * An appearance for {@link PolylineGeometry} that supports shading with materials. * * @alias PolylineMaterialAppearance * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link PolylineMaterialAppearance#renderState} has alpha blending enabled. * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color. * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {Object} [options.renderState] Optional render state to override the default render state. * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} * * @example * var primitive = new Cesium.Primitive({ * geometryInstances : new Cesium.GeometryInstance({ * geometry : new Cesium.PolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 5.0, 0.0 * ]), * width : 10.0, * vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT * }) * }), * appearance : new Cesium.PolylineMaterialAppearance({ * material : Cesium.Material.fromType('Color') * }) * }); */ function PolylineMaterialAppearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var translucent = defaultValue(options.translucent, true); var closed = false; var vertexFormat = PolylineMaterialAppearance.VERTEX_FORMAT; /** * The material used to determine the fragment color. Unlike other {@link PolylineMaterialAppearance} * properties, this is not read-only, so an appearance's material can change on the fly. * * @type Material * * @default {@link Material.ColorType} * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} */ this.material = defined(options.material) ? options.material : Material.fromType(Material.ColorType); /** * When true, the geometry is expected to appear translucent so * {@link PolylineMaterialAppearance#renderState} has alpha blending enabled. * * @type {Boolean} * * @default true */ this.translucent = translucent; this._vertexShaderSource = defaultValue( options.vertexShaderSource, defaultVertexShaderSource ); this._fragmentShaderSource = defaultValue( options.fragmentShaderSource, defaultFragmentShaderSource ); this._renderState = Appearance.getDefaultRenderState( translucent, closed, options.renderState ); this._closed = closed; // Non-derived members this._vertexFormat = vertexFormat; } Object.defineProperties(PolylineMaterialAppearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof PolylineMaterialAppearance.prototype * * @type {String} * @readonly */ vertexShaderSource: { get: function () { var vs = this._vertexShaderSource; if ( this.material.shaderSource.search( /varying\s+float\s+v_polylineAngle;/g ) !== -1 ) { vs = "#define POLYLINE_DASH\n" + vs; } return vs; }, }, /** * The GLSL source code for the fragment shader. * * @memberof PolylineMaterialAppearance.prototype * * @type {String} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. *

* The render state can be explicitly defined when constructing a {@link PolylineMaterialAppearance} * instance, or it is set implicitly via {@link PolylineMaterialAppearance#translucent} * and {@link PolylineMaterialAppearance#closed}. *

* * @memberof PolylineMaterialAppearance.prototype * * @type {Object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed so * {@link PolylineMaterialAppearance#renderState} has backface culling enabled. * This is always false for PolylineMaterialAppearance. * * @memberof PolylineMaterialAppearance.prototype * * @type {Boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, /** * The {@link VertexFormat} that this appearance instance is compatible with. * A geometry can have more vertex attributes and still be compatible - at a * potential performance cost - but it can't have less. * * @memberof PolylineMaterialAppearance.prototype * * @type VertexFormat * @readonly * * @default {@link PolylineMaterialAppearance.VERTEX_FORMAT} */ vertexFormat: { get: function () { return this._vertexFormat; }, }, }); /** * The {@link VertexFormat} that all {@link PolylineMaterialAppearance} instances * are compatible with. This requires position and st attributes. * * @type VertexFormat * * @constant */ PolylineMaterialAppearance.VERTEX_FORMAT = VertexFormat.POSITION_AND_ST; /** * Procedurally creates the full GLSL fragment shader source. For {@link PolylineMaterialAppearance}, * this is derived from {@link PolylineMaterialAppearance#fragmentShaderSource} and {@link PolylineMaterialAppearance#material}. * * @function * * @returns {String} The full GLSL fragment shader source. */ PolylineMaterialAppearance.prototype.getFragmentShaderSource = Appearance.prototype.getFragmentShaderSource; /** * Determines if the geometry is translucent based on {@link PolylineMaterialAppearance#translucent} and {@link Material#isTranslucent}. * * @function * * @returns {Boolean} true if the appearance is translucent. */ PolylineMaterialAppearance.prototype.isTranslucent = Appearance.prototype.isTranslucent; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @function * * @returns {Object} The render state. */ PolylineMaterialAppearance.prototype.getRenderState = Appearance.prototype.getRenderState; /** * A GroundPolylinePrimitive represents a polyline draped over the terrain or 3D Tiles in the {@link Scene}. *

* Only to be used with GeometryInstances containing {@link GroundPolylineGeometry}. *

* * @alias GroundPolylinePrimitive * @constructor * * @param {Object} [options] Object with the following properties: * @param {Array|GeometryInstance} [options.geometryInstances] GeometryInstances containing GroundPolylineGeometry * @param {Appearance} [options.appearance] The Appearance used to render the polyline. Defaults to a white color {@link Material} on a {@link PolylineMaterialAppearance}. * @param {Boolean} [options.show=true] Determines if this primitive will be shown. * @param {Boolean} [options.interleave=false] When true, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time. * @param {Boolean} [options.releaseGeometryInstances=true] When true, the primitive does not keep a reference to the input geometryInstances to save memory. * @param {Boolean} [options.allowPicking=true] When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready. If false initializeTerrainHeights() must be called first. * @param {ClassificationType} [options.classificationType=ClassificationType.BOTH] Determines whether terrain, 3D Tiles or both will be classified. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.debugShowShadowVolume=false] For debugging only. Determines if the shadow volume for each geometry in the primitive is drawn. Must be true on creation to have effect. * * @example * // 1. Draw a polyline on terrain with a basic color material * * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.GroundPolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -112.1340164450331, 36.05494287836128, * -112.08821010582645, 36.097804071380715 * ]), * width : 4.0 * }), * id : 'object returned when this instance is picked and to get/set per-instance attributes' * }); * * scene.groundPrimitives.add(new Cesium.GroundPolylinePrimitive({ * geometryInstances : instance, * appearance : new Cesium.PolylineMaterialAppearance() * })); * * // 2. Draw a looped polyline on terrain with per-instance color and a distance display condition. * // Distance display conditions for polylines on terrain are based on an approximate terrain height * // instead of true terrain height. * * var instance = new Cesium.GeometryInstance({ * geometry : new Cesium.GroundPolylineGeometry({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -112.1340164450331, 36.05494287836128, * -112.08821010582645, 36.097804071380715, * -112.13296079730024, 36.168769146801104 * ]), * loop : true, * width : 4.0 * }), * attributes : { * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromCssColorString('green').withAlpha(0.7)), * distanceDisplayCondition : new Cesium.DistanceDisplayConditionGeometryInstanceAttribute(1000, 30000) * }, * id : 'object returned when this instance is picked and to get/set per-instance attributes' * }); * * scene.groundPrimitives.add(new Cesium.GroundPolylinePrimitive({ * geometryInstances : instance, * appearance : new Cesium.PolylineColorAppearance() * })); */ function GroundPolylinePrimitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The geometry instances rendered with this primitive. This may * be undefined if options.releaseGeometryInstances * is true when the primitive is constructed. *

* Changing this property after the primitive is rendered has no effect. *

* * @readonly * @type {Array|GeometryInstance} * * @default undefined */ this.geometryInstances = options.geometryInstances; this._hasPerInstanceColors = true; var appearance = options.appearance; if (!defined(appearance)) { appearance = new PolylineMaterialAppearance(); } /** * The {@link Appearance} used to shade this primitive. Each geometry * instance is shaded with the same appearance. Some appearances, like * {@link PolylineColorAppearance} allow giving each instance unique * properties. * * @type Appearance * * @default undefined */ this.appearance = appearance; /** * Determines if the primitive will be shown. This affects all geometry * instances in the primitive. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * Determines whether terrain, 3D Tiles or both will be classified. * * @type {ClassificationType} * * @default ClassificationType.BOTH */ this.classificationType = defaultValue( options.classificationType, ClassificationType$1.BOTH ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); // Shadow volume is shown by removing a discard in the shader, so this isn't toggleable. this._debugShowShadowVolume = defaultValue( options.debugShowShadowVolume, false ); this._primitiveOptions = { geometryInstances: undefined, appearance: undefined, vertexCacheOptimize: false, interleave: defaultValue(options.interleave, false), releaseGeometryInstances: defaultValue( options.releaseGeometryInstances, true ), allowPicking: defaultValue(options.allowPicking, true), asynchronous: defaultValue(options.asynchronous, true), compressVertices: false, _createShaderProgramFunction: undefined, _createCommandsFunction: undefined, _updateAndQueueCommandsFunction: undefined, }; // Used when inserting in an OrderedPrimitiveCollection this._zIndex = undefined; this._ready = false; this._readyPromise = when.defer(); this._primitive = undefined; this._sp = undefined; this._sp2D = undefined; this._spMorph = undefined; this._renderState = getRenderState(false); this._renderState3DTiles = getRenderState(true); this._renderStateMorph = RenderState.fromCache({ cull: { enabled: true, face: CullFace$1.FRONT, // Geometry is "inverted," so cull front when materials on volume instead of on terrain (morph) }, depthTest: { enabled: true, }, blending: BlendingState$1.PRE_MULTIPLIED_ALPHA_BLEND, depthMask: false, }); } Object.defineProperties(GroundPolylinePrimitive.prototype, { /** * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance. * * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly * * @default false */ interleave: { get: function () { return this._primitiveOptions.interleave; }, }, /** * When true, the primitive does not keep a reference to the input geometryInstances to save memory. * * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ releaseGeometryInstances: { get: function () { return this._primitiveOptions.releaseGeometryInstances; }, }, /** * When true, each geometry instance will only be pickable with {@link Scene#pick}. When false, GPU memory is saved. * * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking: { get: function () { return this._primitiveOptions.allowPicking; }, }, /** * Determines if the geometry instances will be created and batched on a web worker. * * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous: { get: function () { return this._primitiveOptions.asynchronous; }, }, /** * Determines if the primitive is complete and ready to render. If this property is * true, the primitive will be rendered the next time that {@link GroundPolylinePrimitive#update} * is called. * * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof GroundPolylinePrimitive.prototype * @type {Promise.} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * This property is for debugging only; it is not for production use nor is it optimized. *

* If true, draws the shadow volume for each geometry in the primitive. *

* * @memberof GroundPolylinePrimitive.prototype * * @type {Boolean} * @readonly * * @default false */ debugShowShadowVolume: { get: function () { return this._debugShowShadowVolume; }, }, }); /** * Initializes the minimum and maximum terrain heights. This only needs to be called if you are creating the * GroundPolylinePrimitive synchronously. * * @returns {Promise} A promise that will resolve once the terrain heights have been loaded. */ GroundPolylinePrimitive.initializeTerrainHeights = function () { return ApproximateTerrainHeights.initialize(); }; function createShaderProgram(groundPolylinePrimitive, frameState, appearance) { var context = frameState.context; var primitive = groundPolylinePrimitive._primitive; var attributeLocations = primitive._attributeLocations; var vs = primitive._batchTable.getVertexShaderCallback()( PolylineShadowVolumeVS ); vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs); vs = Primitive._modifyShaderPosition( groundPolylinePrimitive, vs, frameState.scene3DOnly ); var vsMorph = primitive._batchTable.getVertexShaderCallback()( PolylineShadowVolumeMorphVS ); vsMorph = Primitive._appendShowToShader(primitive, vsMorph); vsMorph = Primitive._appendDistanceDisplayConditionToShader( primitive, vsMorph ); vsMorph = Primitive._modifyShaderPosition( groundPolylinePrimitive, vsMorph, frameState.scene3DOnly ); // Access pick color from fragment shader. // Helps with varying budget. var fs = primitive._batchTable.getVertexShaderCallback()( PolylineShadowVolumeFS ); var vsDefines = [ "GLOBE_MINIMUM_ALTITUDE " + frameState.mapProjection.ellipsoid.minimumRadius.toFixed(1), ]; var colorDefine = ""; var materialShaderSource = ""; if (defined(appearance.material)) { materialShaderSource = defined(appearance.material) ? appearance.material.shaderSource : ""; // Check for use of v_width and v_polylineAngle in material shader // to determine whether these varyings should be active in the vertex shader. if ( materialShaderSource.search(/varying\s+float\s+v_polylineAngle;/g) !== -1 ) { vsDefines.push("ANGLE_VARYING"); } if (materialShaderSource.search(/varying\s+float\s+v_width;/g) !== -1) { vsDefines.push("WIDTH_VARYING"); } } else { colorDefine = "PER_INSTANCE_COLOR"; } vsDefines.push(colorDefine); var fsDefines = groundPolylinePrimitive.debugShowShadowVolume ? ["DEBUG_SHOW_VOLUME", colorDefine] : [colorDefine]; var vsColor3D = new ShaderSource({ defines: vsDefines, sources: [vs], }); var fsColor3D = new ShaderSource({ defines: fsDefines, sources: [materialShaderSource, fs], }); groundPolylinePrimitive._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: primitive._sp, vertexShaderSource: vsColor3D, fragmentShaderSource: fsColor3D, attributeLocations: attributeLocations, }); // Derive 2D/CV var colorProgram2D = context.shaderCache.getDerivedShaderProgram( groundPolylinePrimitive._sp, "2dColor" ); if (!defined(colorProgram2D)) { var vsColor2D = new ShaderSource({ defines: vsDefines.concat(["COLUMBUS_VIEW_2D"]), sources: [vs], }); colorProgram2D = context.shaderCache.createDerivedShaderProgram( groundPolylinePrimitive._sp, "2dColor", { context: context, shaderProgram: groundPolylinePrimitive._sp2D, vertexShaderSource: vsColor2D, fragmentShaderSource: fsColor3D, attributeLocations: attributeLocations, } ); } groundPolylinePrimitive._sp2D = colorProgram2D; // Derive Morph var colorProgramMorph = context.shaderCache.getDerivedShaderProgram( groundPolylinePrimitive._sp, "MorphColor" ); if (!defined(colorProgramMorph)) { var vsColorMorph = new ShaderSource({ defines: vsDefines.concat([ "MAX_TERRAIN_HEIGHT " + ApproximateTerrainHeights._defaultMaxTerrainHeight.toFixed(1), ]), sources: [vsMorph], }); fs = primitive._batchTable.getVertexShaderCallback()( PolylineShadowVolumeMorphFS ); var fsColorMorph = new ShaderSource({ defines: fsDefines, sources: [materialShaderSource, fs], }); colorProgramMorph = context.shaderCache.createDerivedShaderProgram( groundPolylinePrimitive._sp, "MorphColor", { context: context, shaderProgram: groundPolylinePrimitive._spMorph, vertexShaderSource: vsColorMorph, fragmentShaderSource: fsColorMorph, attributeLocations: attributeLocations, } ); } groundPolylinePrimitive._spMorph = colorProgramMorph; } function getRenderState(mask3DTiles) { return RenderState.fromCache({ cull: { enabled: true, // prevent double-draw. Geometry is "inverted" (reversed winding order) so we're drawing backfaces. }, blending: BlendingState$1.PRE_MULTIPLIED_ALPHA_BLEND, depthMask: false, stencilTest: { enabled: mask3DTiles, frontFunction: StencilFunction$1.EQUAL, frontOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.KEEP, zPass: StencilOperation$1.KEEP, }, backFunction: StencilFunction$1.EQUAL, backOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.KEEP, zPass: StencilOperation$1.KEEP, }, reference: StencilConstants$1.CESIUM_3D_TILE_MASK, mask: StencilConstants$1.CESIUM_3D_TILE_MASK, }, }); } function createCommands$3( groundPolylinePrimitive, appearance, material, translucent, colorCommands, pickCommands ) { var primitive = groundPolylinePrimitive._primitive; var length = primitive._va.length; colorCommands.length = length; pickCommands.length = length; var isPolylineColorAppearance = appearance instanceof PolylineColorAppearance; var materialUniforms = isPolylineColorAppearance ? {} : material._uniforms; var uniformMap = primitive._batchTable.getUniformMapCallback()( materialUniforms ); for (var i = 0; i < length; i++) { var vertexArray = primitive._va[i]; var command = colorCommands[i]; if (!defined(command)) { command = colorCommands[i] = new DrawCommand({ owner: groundPolylinePrimitive, primitiveType: primitive._primitiveType, }); } command.vertexArray = vertexArray; command.renderState = groundPolylinePrimitive._renderState; command.shaderProgram = groundPolylinePrimitive._sp; command.uniformMap = uniformMap; command.pass = Pass$1.TERRAIN_CLASSIFICATION; command.pickId = "czm_batchTable_pickColor(v_endPlaneNormalEcAndBatchId.w)"; var derivedTilesetCommand = DrawCommand.shallowClone( command, command.derivedCommands.tileset ); derivedTilesetCommand.renderState = groundPolylinePrimitive._renderState3DTiles; derivedTilesetCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; command.derivedCommands.tileset = derivedTilesetCommand; // derive for 2D var derived2DCommand = DrawCommand.shallowClone( command, command.derivedCommands.color2D ); derived2DCommand.shaderProgram = groundPolylinePrimitive._sp2D; command.derivedCommands.color2D = derived2DCommand; var derived2DTilesetCommand = DrawCommand.shallowClone( derivedTilesetCommand, derivedTilesetCommand.derivedCommands.color2D ); derived2DTilesetCommand.shaderProgram = groundPolylinePrimitive._sp2D; derivedTilesetCommand.derivedCommands.color2D = derived2DTilesetCommand; // derive for Morph var derivedMorphCommand = DrawCommand.shallowClone( command, command.derivedCommands.colorMorph ); derivedMorphCommand.renderState = groundPolylinePrimitive._renderStateMorph; derivedMorphCommand.shaderProgram = groundPolylinePrimitive._spMorph; derivedMorphCommand.pickId = "czm_batchTable_pickColor(v_batchId)"; command.derivedCommands.colorMorph = derivedMorphCommand; } } function updateAndQueueCommand( groundPolylinePrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ) { // Use derived appearance command for morph and 2D if (frameState.mode === SceneMode$1.MORPHING) { command = command.derivedCommands.colorMorph; } else if (frameState.mode !== SceneMode$1.SCENE3D) { command = command.derivedCommands.color2D; } command.modelMatrix = modelMatrix; command.boundingVolume = boundingVolume; command.cull = cull; command.debugShowBoundingVolume = debugShowBoundingVolume; frameState.commandList.push(command); } function updateAndQueueCommands( groundPolylinePrimitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume ) { var primitive = groundPolylinePrimitive._primitive; Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix); // Expected to be identity - GroundPrimitives don't support other model matrices var boundingSpheres; if (frameState.mode === SceneMode$1.SCENE3D) { boundingSpheres = primitive._boundingSphereWC; } else if (frameState.mode === SceneMode$1.COLUMBUS_VIEW) { boundingSpheres = primitive._boundingSphereCV; } else if ( frameState.mode === SceneMode$1.SCENE2D && defined(primitive._boundingSphere2D) ) { boundingSpheres = primitive._boundingSphere2D; } else if (defined(primitive._boundingSphereMorph)) { boundingSpheres = primitive._boundingSphereMorph; } var morphing = frameState.mode === SceneMode$1.MORPHING; var classificationType = groundPolylinePrimitive.classificationType; var queueTerrainCommands = classificationType !== ClassificationType$1.CESIUM_3D_TILE; var queue3DTilesCommands = classificationType !== ClassificationType$1.TERRAIN && !morphing; var command; var passes = frameState.passes; if (passes.render || (passes.pick && primitive.allowPicking)) { var colorLength = colorCommands.length; for (var j = 0; j < colorLength; ++j) { var boundingVolume = boundingSpheres[j]; if (queueTerrainCommands) { command = colorCommands[j]; updateAndQueueCommand( groundPolylinePrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } if (queue3DTilesCommands) { command = colorCommands[j].derivedCommands.tileset; updateAndQueueCommand( groundPolylinePrimitive, command, frameState, modelMatrix, cull, boundingVolume, debugShowBoundingVolume ); } } } } /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {DeveloperError} For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve. * @exception {DeveloperError} All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive. */ GroundPolylinePrimitive.prototype.update = function (frameState) { if (!defined(this._primitive) && !defined(this.geometryInstances)) { return; } if (!ApproximateTerrainHeights.initialized) { //>>includeStart('debug', pragmas.debug); if (!this.asynchronous) { throw new DeveloperError( "For synchronous GroundPolylinePrimitives, you must call GroundPolylinePrimitives.initializeTerrainHeights() and wait for the returned promise to resolve." ); } //>>includeEnd('debug'); GroundPolylinePrimitive.initializeTerrainHeights(); return; } var i; var that = this; var primitiveOptions = this._primitiveOptions; if (!defined(this._primitive)) { var geometryInstances = Array.isArray(this.geometryInstances) ? this.geometryInstances : [this.geometryInstances]; var geometryInstancesLength = geometryInstances.length; var groundInstances = new Array(geometryInstancesLength); var attributes; // Check if each instance has a color attribute. for (i = 0; i < geometryInstancesLength; ++i) { attributes = geometryInstances[i].attributes; if (!defined(attributes) || !defined(attributes.color)) { this._hasPerInstanceColors = false; break; } } for (i = 0; i < geometryInstancesLength; ++i) { var geometryInstance = geometryInstances[i]; attributes = {}; var instanceAttributes = geometryInstance.attributes; for (var attributeKey in instanceAttributes) { if (instanceAttributes.hasOwnProperty(attributeKey)) { attributes[attributeKey] = instanceAttributes[attributeKey]; } } // Automatically create line width attribute if not already given if (!defined(attributes.width)) { attributes.width = new GeometryInstanceAttribute({ componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 1.0, value: [geometryInstance.geometry.width], }); } // Update each geometry for framestate.scene3DOnly = true and projection geometryInstance.geometry._scene3DOnly = frameState.scene3DOnly; GroundPolylineGeometry.setProjectionAndEllipsoid( geometryInstance.geometry, frameState.mapProjection ); groundInstances[i] = new GeometryInstance({ geometry: geometryInstance.geometry, attributes: attributes, id: geometryInstance.id, pickPrimitive: that, }); } primitiveOptions.geometryInstances = groundInstances; primitiveOptions.appearance = this.appearance; primitiveOptions._createShaderProgramFunction = function ( primitive, frameState, appearance ) { createShaderProgram(that, frameState, appearance); }; primitiveOptions._createCommandsFunction = function ( primitive, appearance, material, translucent, twoPasses, colorCommands, pickCommands ) { createCommands$3( that, appearance, material, translucent, colorCommands, pickCommands ); }; primitiveOptions._updateAndQueueCommandsFunction = function ( primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses ) { updateAndQueueCommands( that, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume ); }; this._primitive = new Primitive(primitiveOptions); this._primitive.readyPromise.then(function (primitive) { that._ready = true; if (that.releaseGeometryInstances) { that.geometryInstances = undefined; } var error = primitive._error; if (!defined(error)) { that._readyPromise.resolve(that); } else { that._readyPromise.reject(error); } }); } if ( this.appearance instanceof PolylineColorAppearance && !this._hasPerInstanceColors ) { throw new DeveloperError( "All GeometryInstances must have color attributes to use PolylineColorAppearance with GroundPolylinePrimitive." ); } this._primitive.appearance = this.appearance; this._primitive.show = this.show; this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; this._primitive.update(frameState); }; /** * Returns the modifiable per-instance attributes for a {@link GeometryInstance}. * * @param {*} id The id of the {@link GeometryInstance}. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id. * * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes. * * @example * var attributes = primitive.getGeometryInstanceAttributes('an id'); * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); */ GroundPolylinePrimitive.prototype.getGeometryInstanceAttributes = function ( id ) { //>>includeStart('debug', pragmas.debug); if (!defined(this._primitive)) { throw new DeveloperError( "must call update before calling getGeometryInstanceAttributes" ); } //>>includeEnd('debug'); return this._primitive.getGeometryInstanceAttributes(id); }; /** * Checks if the given Scene supports GroundPolylinePrimitives. * GroundPolylinePrimitives require support for the WEBGL_depth_texture extension. * * @param {Scene} scene The current scene. * @returns {Boolean} Whether or not the current scene supports GroundPolylinePrimitives. */ GroundPolylinePrimitive.isSupported = function (scene) { return scene.frameState.context.depthTexture; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see GroundPolylinePrimitive#destroy */ GroundPolylinePrimitive.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * e = e && e.destroy(); * * @see GroundPolylinePrimitive#isDestroyed */ GroundPolylinePrimitive.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); this._sp = this._sp && this._sp.destroy(); // Derived programs, destroyed above if they existed. this._sp2D = undefined; this._spMorph = undefined; return destroyObject(this); }; var defaultRepeat$2 = new Cartesian2(1, 1); var defaultTransparent = false; var defaultColor$7 = Color.WHITE; /** * A {@link MaterialProperty} that maps to image {@link Material} uniforms. * @alias ImageMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|String|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} [options.image] A Property specifying the Image, URL, Canvas, or Video. * @param {Property|Cartesian2} [options.repeat=new Cartesian2(1.0, 1.0)] A {@link Cartesian2} Property specifying the number of times the image repeats in each direction. * @param {Property|Color} [options.color=Color.WHITE] The color applied to the image * @param {Property|Boolean} [options.transparent=false] Set to true when the image has transparency (for example, when a png has transparent sections) */ function ImageMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._image = undefined; this._imageSubscription = undefined; this._repeat = undefined; this._repeatSubscription = undefined; this._color = undefined; this._colorSubscription = undefined; this._transparent = undefined; this._transparentSubscription = undefined; this.image = options.image; this.repeat = options.repeat; this.color = options.color; this.transparent = options.transparent; } Object.defineProperties(ImageMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof ImageMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._image) && Property.isConstant(this._repeat) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof ImageMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying Image, URL, Canvas, or Video to use. * @memberof ImageMaterialProperty.prototype * @type {Property|undefined} */ image: createPropertyDescriptor("image"), /** * Gets or sets the {@link Cartesian2} Property specifying the number of times the image repeats in each direction. * @memberof ImageMaterialProperty.prototype * @type {Property|undefined} * @default new Cartesian2(1, 1) */ repeat: createPropertyDescriptor("repeat"), /** * Gets or sets the Color Property specifying the desired color applied to the image. * @memberof ImageMaterialProperty.prototype * @type {Property|undefined} * @default 1.0 */ color: createPropertyDescriptor("color"), /** * Gets or sets the Boolean Property specifying whether the image has transparency * @memberof ImageMaterialProperty.prototype * @type {Property|undefined} * @default 1.0 */ transparent: createPropertyDescriptor("transparent"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ ImageMaterialProperty.prototype.getType = function (time) { return "Image"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ ImageMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.image = Property.getValueOrUndefined(this._image, time); result.repeat = Property.getValueOrClonedDefault( this._repeat, time, defaultRepeat$2, result.repeat ); result.color = Property.getValueOrClonedDefault( this._color, time, defaultColor$7, result.color ); if (Property.getValueOrDefault(this._transparent, time, defaultTransparent)) { result.color.alpha = Math.min(0.99, result.color.alpha); } return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ ImageMaterialProperty.prototype.equals = function (other) { return ( this === other || (other instanceof ImageMaterialProperty && Property.equals(this._image, other._image) && Property.equals(this._repeat, other._repeat) && Property.equals(this._color, other._color) && Property.equals(this._transparent, other._transparent)) ); }; function createMaterialProperty(value) { if (value instanceof Color) { return new ColorMaterialProperty(value); } if ( typeof value === "string" || value instanceof Resource || value instanceof HTMLCanvasElement || value instanceof HTMLVideoElement ) { var result = new ImageMaterialProperty(); result.image = value; return result; } //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Unable to infer material type: " + value); //>>includeEnd('debug'); } /** * @private */ function createMaterialPropertyDescriptor(name, configurable) { return createPropertyDescriptor(name, configurable, createMaterialProperty); } /** * @typedef {Object} BoxGraphics.ConstructorOptions * * Initialization options for the BoxGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the box. * @property {Property | Cartesian3} [dimensions] A {@link Cartesian3} Property specifying the length, width, and height of the box. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height from the entity position is relative to. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the box is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the box. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the box is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the box casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this box will be displayed. * */ /** * Describes a box. The center position and orientation are determined by the containing {@link Entity}. * * @alias BoxGraphics * @constructor * * @param {BoxGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Box.html|Cesium Sandcastle Box Demo} */ function BoxGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._dimensions = undefined; this._dimensionsSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(BoxGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof BoxGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the box. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets {@link Cartesian3} Property property specifying the length, width, and height of the box. * @memberof BoxGraphics.prototype * @type {Property|undefined} */ dimensions: createPropertyDescriptor("dimensions"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the boolean Property specifying whether the box is filled with the provided material. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the material used to fill the box. * @memberof BoxGraphics.prototype * @type {MaterialProperty|undefined} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the box is outlined. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the box * casts or receives shadows from light sources. * @memberof BoxGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this box will be displayed. * @memberof BoxGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {BoxGraphics} [result] The object onto which to store the result. * @returns {BoxGraphics} The modified result parameter or a new instance if one was not provided. */ BoxGraphics.prototype.clone = function (result) { if (!defined(result)) { return new BoxGraphics(this); } result.show = this.show; result.dimensions = this.dimensions; result.heightReference = this.heightReference; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {BoxGraphics} source The object to be merged into this object. */ BoxGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.dimensions = defaultValue(this.dimensions, source.dimensions); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * The interface for all {@link Property} objects that define a world * location as a {@link Cartesian3} with an associated {@link ReferenceFrame}. * This type defines an interface and cannot be instantiated directly. * * @alias PositionProperty * @constructor * @abstract * * @see CompositePositionProperty * @see ConstantPositionProperty * @see SampledPositionProperty * @see TimeIntervalCollectionPositionProperty */ function PositionProperty() { DeveloperError.throwInstantiationError(); } Object.defineProperties(PositionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof PositionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: DeveloperError.throwInstantiationError, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof PositionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: DeveloperError.throwInstantiationError, }, /** * Gets the reference frame that the position is defined in. * @memberof PositionProperty.prototype * @type {ReferenceFrame} */ referenceFrame: { get: DeveloperError.throwInstantiationError, }, }); /** * Gets the value of the property at the provided time in the fixed frame. * @function * * @param {JulianDate} time The time for which to retrieve the value. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ PositionProperty.prototype.getValue = DeveloperError.throwInstantiationError; /** * Gets the value of the property at the provided time and in the provided reference frame. * @function * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ PositionProperty.prototype.getValueInReferenceFrame = DeveloperError.throwInstantiationError; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * @function * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PositionProperty.prototype.equals = DeveloperError.throwInstantiationError; var scratchMatrix3$1 = new Matrix3(); /** * @private */ PositionProperty.convertToReferenceFrame = function ( time, value, inputFrame, outputFrame, result ) { if (!defined(value)) { return value; } if (!defined(result)) { result = new Cartesian3(); } if (inputFrame === outputFrame) { return Cartesian3.clone(value, result); } var icrfToFixed = Transforms.computeIcrfToFixedMatrix(time, scratchMatrix3$1); if (!defined(icrfToFixed)) { icrfToFixed = Transforms.computeTemeToPseudoFixedMatrix( time, scratchMatrix3$1 ); } if (inputFrame === ReferenceFrame$1.INERTIAL) { return Matrix3.multiplyByVector(icrfToFixed, value, result); } if (inputFrame === ReferenceFrame$1.FIXED) { return Matrix3.multiplyByVector( Matrix3.transpose(icrfToFixed, scratchMatrix3$1), value, result ); } }; /** * A {@link PositionProperty} whose value does not change in respect to the * {@link ReferenceFrame} in which is it defined. * * @alias ConstantPositionProperty * @constructor * * @param {Cartesian3} [value] The property value. * @param {ReferenceFrame} [referenceFrame=ReferenceFrame.FIXED] The reference frame in which the position is defined. */ function ConstantPositionProperty(value, referenceFrame) { this._definitionChanged = new Event(); this._value = Cartesian3.clone(value); this._referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); } Object.defineProperties(ConstantPositionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof ConstantPositionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( !defined(this._value) || this._referenceFrame === ReferenceFrame$1.FIXED ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof ConstantPositionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the reference frame in which the position is defined. * @memberof ConstantPositionProperty.prototype * @type {ReferenceFrame} * @default ReferenceFrame.FIXED; */ referenceFrame: { get: function () { return this._referenceFrame; }, }, }); /** * Gets the value of the property at the provided time in the fixed frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ ConstantPositionProperty.prototype.getValue = function (time, result) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; /** * Sets the value of the property. * * @param {Cartesian3} value The property value. * @param {ReferenceFrame} [referenceFrame=this.referenceFrame] The reference frame in which the position is defined. */ ConstantPositionProperty.prototype.setValue = function (value, referenceFrame) { var definitionChanged = false; if (!Cartesian3.equals(this._value, value)) { definitionChanged = true; this._value = Cartesian3.clone(value); } if (defined(referenceFrame) && this._referenceFrame !== referenceFrame) { definitionChanged = true; this._referenceFrame = referenceFrame; } if (definitionChanged) { this._definitionChanged.raiseEvent(this); } }; /** * Gets the value of the property at the provided time and in the provided reference frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ ConstantPositionProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!defined(referenceFrame)) { throw new DeveloperError("referenceFrame is required."); } //>>includeEnd('debug'); return PositionProperty.convertToReferenceFrame( time, this._value, this._referenceFrame, referenceFrame, result ); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ ConstantPositionProperty.prototype.equals = function (other) { return ( this === other || (other instanceof ConstantPositionProperty && Cartesian3.equals(this._value, other._value) && this._referenceFrame === other._referenceFrame) ); }; /** * @typedef {Object} CorridorGraphics.ConstructorOptions * * Initialization options for the CorridorGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the corridor. * @property {Property | Cartesian3} [positions] A Property specifying the array of {@link Cartesian3} positions that define the centerline of the corridor. * @property {Property | number} [width] A numeric Property specifying the distance between the edges of the corridor. * @property {Property | number} [height=0] A numeric Property specifying the altitude of the corridor relative to the ellipsoid surface. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | number} [extrudedHeight] A numeric Property specifying the altitude of the corridor's extruded face relative to the ellipsoid surface. * @property {Property | HeightReference} [extrudedHeightReference=HeightReference.NONE] A Property specifying what the extrudedHeight is relative to. * @property {Property | CornerType} [cornerType=CornerType.ROUNDED] A {@link CornerType} Property specifying the style of the corners. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the distance between each latitude and longitude. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the corridor is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the corridor. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the corridor is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the corridor casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this corridor will be displayed. * @property {Property | ClassificationType} [classificationType=ClassificationType.BOTH] An enum Property specifying whether this corridor will classify terrain, 3D Tiles, or both when on the ground. * @property {ConstantProperty | number} [zIndex] A Property specifying the zIndex of the corridor, used for ordering. Only has an effect if height and extrudedHeight are undefined, and if the corridor is static. */ /** * Describes a corridor, which is a shape defined by a centerline and width that * conforms to the curvature of the globe. It can be placed on the surface or at altitude * and can optionally be extruded into a volume. * * @alias CorridorGraphics * @constructor * * @param {CorridorGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Corridor.html|Cesium Sandcastle Corridor Demo} */ function CorridorGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._positions = undefined; this._positionsSubscription = undefined; this._width = undefined; this._widthSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; this._extrudedHeightReference = undefined; this._extrudedHeightReferenceSubscription = undefined; this._cornerType = undefined; this._cornerTypeSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._classificationType = undefined; this._classificationTypeSubscription = undefined; this._zIndex = undefined; this._zIndexSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(CorridorGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof CorridorGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the corridor. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets a Property specifying the array of {@link Cartesian3} positions that define the centerline of the corridor. * @memberof CorridorGraphics.prototype * @type {Property|undefined} */ positions: createPropertyDescriptor("positions"), /** * Gets or sets the numeric Property specifying the width of the corridor. * @memberof CorridorGraphics.prototype * @type {Property|undefined} */ width: createPropertyDescriptor("width"), /** * Gets or sets the numeric Property specifying the altitude of the corridor. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default 0.0 */ height: createPropertyDescriptor("height"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the numeric Property specifying the altitude of the corridor extrusion. * Setting this property creates a corridor shaped volume starting at height and ending * at this altitude. * @memberof CorridorGraphics.prototype * @type {Property|undefined} */ extrudedHeight: createPropertyDescriptor("extrudedHeight"), /** * Gets or sets the Property specifying the extruded {@link HeightReference}. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ extrudedHeightReference: createPropertyDescriptor("extrudedHeightReference"), /** * Gets or sets the {@link CornerType} Property specifying how corners are styled. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default CornerType.ROUNDED */ cornerType: createPropertyDescriptor("cornerType"), /** * Gets or sets the numeric Property specifying the sampling distance between each latitude and longitude point. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the corridor is filled with the provided material. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the corridor. * @memberof CorridorGraphics.prototype * @type {MaterialProperty|undefined} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the corridor is outlined. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the corridor * casts or receives shadows from light sources. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this corridor will be displayed. * @memberof CorridorGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the {@link ClassificationType} Property specifying whether this corridor will classify terrain, 3D Tiles, or both when on the ground. * @memberof CorridorGraphics.prototype * @type {Property|undefined} * @default ClassificationType.BOTH */ classificationType: createPropertyDescriptor("classificationType"), /** * Gets or sets the zIndex Property specifying the ordering of the corridor. Only has an effect if the coridor is static and neither height or exturdedHeight are specified. * @memberof CorridorGraphics.prototype * @type {ConstantProperty|undefined} * @default 0 */ zIndex: createPropertyDescriptor("zIndex"), }); /** * Duplicates this instance. * * @param {CorridorGraphics} [result] The object onto which to store the result. * @returns {CorridorGraphics} The modified result parameter or a new instance if one was not provided. */ CorridorGraphics.prototype.clone = function (result) { if (!defined(result)) { return new CorridorGraphics(this); } result.show = this.show; result.positions = this.positions; result.width = this.width; result.height = this.height; result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; result.extrudedHeightReference = this.extrudedHeightReference; result.cornerType = this.cornerType; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; result.classificationType = this.classificationType; result.zIndex = this.zIndex; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {CorridorGraphics} source The object to be merged into this object. */ CorridorGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.positions = defaultValue(this.positions, source.positions); this.width = defaultValue(this.width, source.width); this.height = defaultValue(this.height, source.height); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.extrudedHeight = defaultValue( this.extrudedHeight, source.extrudedHeight ); this.extrudedHeightReference = defaultValue( this.extrudedHeightReference, source.extrudedHeightReference ); this.cornerType = defaultValue(this.cornerType, source.cornerType); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.classificationType = defaultValue( this.classificationType, source.classificationType ); this.zIndex = defaultValue(this.zIndex, source.zIndex); }; function createRawProperty(value) { return value; } /** * @private */ function createRawPropertyDescriptor(name, configurable) { return createPropertyDescriptor(name, configurable, createRawProperty); } /** * @typedef {Object} CylinderGraphics.ConstructorOptions * * Initialization options for the CylinderGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the cylinder. * @property {Property | number} [length] A numeric Property specifying the length of the cylinder. * @property {Property | number} [topRadius] A numeric Property specifying the radius of the top of the cylinder. * @property {Property | number} [bottomRadius] A numeric Property specifying the radius of the bottom of the cylinder. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height from the entity position is relative to. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the cylinder is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the cylinder. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the cylinder is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | number} [numberOfVerticalLines=16] A numeric Property specifying the number of vertical lines to draw along the perimeter for the outline. * @property {Property | number} [slices=128] The number of edges around the perimeter of the cylinder. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the cylinder casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this cylinder will be displayed. */ /** * Describes a cylinder, truncated cone, or cone defined by a length, top radius, and bottom radius. * The center position and orientation are determined by the containing {@link Entity}. * * @alias CylinderGraphics * @constructor * * @param {CylinderGraphics.ConstructorOptions} [options] Object describing initialization options */ function CylinderGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._length = undefined; this._lengthSubscription = undefined; this._topRadius = undefined; this._topRadiusSubscription = undefined; this._bottomRadius = undefined; this._bottomRadiusSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._numberOfVerticalLines = undefined; this._numberOfVerticalLinesSubscription = undefined; this._slices = undefined; this._slicesSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(CylinderGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof CylinderGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the numeric Property specifying the length of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property|undefined} */ length: createPropertyDescriptor("length"), /** * Gets or sets the numeric Property specifying the radius of the top of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property|undefined} */ topRadius: createPropertyDescriptor("topRadius"), /** * Gets or sets the numeric Property specifying the radius of the bottom of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property|undefined} */ bottomRadius: createPropertyDescriptor("bottomRadius"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the boolean Property specifying whether the cylinder is filled with the provided material. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the cylinder. * @memberof CylinderGraphics.prototype * @type {MaterialProperty|undefined} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the boolean Property specifying whether the cylinder is outlined. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets the Property specifying the number of vertical lines to draw along the perimeter for the outline. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default 16 */ numberOfVerticalLines: createPropertyDescriptor("numberOfVerticalLines"), /** * Gets or sets the Property specifying the number of edges around the perimeter of the cylinder. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default 128 */ slices: createPropertyDescriptor("slices"), /** * Get or sets the enum Property specifying whether the cylinder * casts or receives shadows from light sources. * @memberof CylinderGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this cylinder will be displayed. * @memberof CylinderGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {CylinderGraphics} [result] The object onto which to store the result. * @returns {CylinderGraphics} The modified result parameter or a new instance if one was not provided. */ CylinderGraphics.prototype.clone = function (result) { if (!defined(result)) { return new CylinderGraphics(this); } result.show = this.show; result.length = this.length; result.topRadius = this.topRadius; result.bottomRadius = this.bottomRadius; result.heightReference = this.heightReference; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.numberOfVerticalLines = this.numberOfVerticalLines; result.slices = this.slices; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {CylinderGraphics} source The object to be merged into this object. */ CylinderGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.length = defaultValue(this.length, source.length); this.topRadius = defaultValue(this.topRadius, source.topRadius); this.bottomRadius = defaultValue(this.bottomRadius, source.bottomRadius); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.numberOfVerticalLines = defaultValue( this.numberOfVerticalLines, source.numberOfVerticalLines ); this.slices = defaultValue(this.slices, source.slices); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * @typedef {Object} EllipseGraphics.ConstructorOptions * * Initialization options for the EllipseGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the ellipse. * @property {Property | number} [semiMajorAxis] The numeric Property specifying the semi-major axis. * @property {Property | number} [semiMinorAxis] The numeric Property specifying the semi-minor axis. * @property {Property | number} [height=0] A numeric Property specifying the altitude of the ellipse relative to the ellipsoid surface. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | number} [extrudedHeight] A numeric Property specifying the altitude of the ellipse's extruded face relative to the ellipsoid surface. * @property {Property | HeightReference} [extrudedHeightReference=HeightReference.NONE] A Property specifying what the extrudedHeight is relative to. * @property {Property | number} [rotation=0.0] A numeric property specifying the rotation of the ellipse counter-clockwise from north. * @property {Property | number} [stRotation=0.0] A numeric property specifying the rotation of the ellipse texture counter-clockwise from north. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between points on the ellipse. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the ellipse is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the ellipse. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the ellipse is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | number} [numberOfVerticalLines=16] A numeric Property specifying the number of vertical lines to draw along the perimeter for the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the ellipse casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this ellipse will be displayed. * @property {Property | ClassificationType} [classificationType=ClassificationType.BOTH] An enum Property specifying whether this ellipse will classify terrain, 3D Tiles, or both when on the ground. * @property {ConstantProperty | number} [zIndex=0] A property specifying the zIndex of the Ellipse. Used for ordering ground geometry. Only has an effect if the ellipse is constant and neither height or exturdedHeight are specified. */ /** * Describes an ellipse defined by a center point and semi-major and semi-minor axes. * The ellipse conforms to the curvature of the globe and can be placed on the surface or * at altitude and can optionally be extruded into a volume. * The center point is determined by the containing {@link Entity}. * * @alias EllipseGraphics * @constructor * * @param {EllipseGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Circles and Ellipses.html|Cesium Sandcastle Circles and Ellipses Demo} */ function EllipseGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._semiMajorAxis = undefined; this._semiMajorAxisSubscription = undefined; this._semiMinorAxis = undefined; this._semiMinorAxisSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; this._extrudedHeightReference = undefined; this._extrudedHeightReferenceSubscription = undefined; this._rotation = undefined; this._rotationSubscription = undefined; this._stRotation = undefined; this._stRotationSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._numberOfVerticalLines = undefined; this._numberOfVerticalLinesSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._classificationType = undefined; this._classificationTypeSubscription = undefined; this._zIndex = undefined; this._zIndexSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(EllipseGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof EllipseGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the ellipse. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the numeric Property specifying the semi-major axis. * @memberof EllipseGraphics.prototype * @type {Property|undefined} */ semiMajorAxis: createPropertyDescriptor("semiMajorAxis"), /** * Gets or sets the numeric Property specifying the semi-minor axis. * @memberof EllipseGraphics.prototype * @type {Property|undefined} */ semiMinorAxis: createPropertyDescriptor("semiMinorAxis"), /** * Gets or sets the numeric Property specifying the altitude of the ellipse. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default 0.0 */ height: createPropertyDescriptor("height"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the numeric Property specifying the altitude of the ellipse extrusion. * Setting this property creates volume starting at height and ending at this altitude. * @memberof EllipseGraphics.prototype * @type {Property|undefined} */ extrudedHeight: createPropertyDescriptor("extrudedHeight"), /** * Gets or sets the Property specifying the extruded {@link HeightReference}. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ extrudedHeightReference: createPropertyDescriptor("extrudedHeightReference"), /** * Gets or sets the numeric property specifying the rotation of the ellipse counter-clockwise from north. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default 0 */ rotation: createPropertyDescriptor("rotation"), /** * Gets or sets the numeric property specifying the rotation of the ellipse texture counter-clockwise from north. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default 0 */ stRotation: createPropertyDescriptor("stRotation"), /** * Gets or sets the numeric Property specifying the angular distance between points on the ellipse. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the ellipse is filled with the provided material. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the ellipse. * @memberof EllipseGraphics.prototype * @type {MaterialProperty|undefined} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the ellipse is outlined. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets the numeric Property specifying the number of vertical lines to draw along the perimeter for the outline. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default 16 */ numberOfVerticalLines: createPropertyDescriptor("numberOfVerticalLines"), /** * Get or sets the enum Property specifying whether the ellipse * casts or receives shadows from light sources. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this ellipse will be displayed. * @memberof EllipseGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the {@link ClassificationType} Property specifying whether this ellipse will classify terrain, 3D Tiles, or both when on the ground. * @memberof EllipseGraphics.prototype * @type {Property|undefined} * @default ClassificationType.BOTH */ classificationType: createPropertyDescriptor("classificationType"), /** * Gets or sets the zIndex Property specifying the ellipse ordering. Only has an effect if the ellipse is constant and neither height or extrudedHeight are specified * @memberof EllipseGraphics.prototype * @type {ConstantProperty|undefined} * @default 0 */ zIndex: createPropertyDescriptor("zIndex"), }); /** * Duplicates this instance. * * @param {EllipseGraphics} [result] The object onto which to store the result. * @returns {EllipseGraphics} The modified result parameter or a new instance if one was not provided. */ EllipseGraphics.prototype.clone = function (result) { if (!defined(result)) { return new EllipseGraphics(this); } result.show = this.show; result.semiMajorAxis = this.semiMajorAxis; result.semiMinorAxis = this.semiMinorAxis; result.height = this.height; result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; result.extrudedHeightReference = this.extrudedHeightReference; result.rotation = this.rotation; result.stRotation = this.stRotation; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.numberOfVerticalLines = this.numberOfVerticalLines; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; result.classificationType = this.classificationType; result.zIndex = this.zIndex; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {EllipseGraphics} source The object to be merged into this object. */ EllipseGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.semiMajorAxis = defaultValue(this.semiMajorAxis, source.semiMajorAxis); this.semiMinorAxis = defaultValue(this.semiMinorAxis, source.semiMinorAxis); this.height = defaultValue(this.height, source.height); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.extrudedHeight = defaultValue( this.extrudedHeight, source.extrudedHeight ); this.extrudedHeightReference = defaultValue( this.extrudedHeightReference, source.extrudedHeightReference ); this.rotation = defaultValue(this.rotation, source.rotation); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.numberOfVerticalLines = defaultValue( this.numberOfVerticalLines, source.numberOfVerticalLines ); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.classificationType = defaultValue( this.classificationType, source.classificationType ); this.zIndex = defaultValue(this.zIndex, source.zIndex); }; /** * @typedef {Object} EllipsoidGraphics.ConstructorOptions * * Initialization options for the EllipsoidGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the ellipsoid. * @property {Property | Cartesian3} [radii] A {@link Cartesian3} Property specifying the radii of the ellipsoid. * @property {Property | Cartesian3} [innerRadii] A {@link Cartesian3} Property specifying the inner radii of the ellipsoid. * @property {Property | number} [minimumClock=0.0] A Property specifying the minimum clock angle of the ellipsoid. * @property {Property | number} [maximumClock=2*PI] A Property specifying the maximum clock angle of the ellipsoid. * @property {Property | number} [minimumCone=0.0] A Property specifying the minimum cone angle of the ellipsoid. * @property {Property | number} [maximumCone=PI] A Property specifying the maximum cone angle of the ellipsoid. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height from the entity position is relative to. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the ellipsoid is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the ellipsoid. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the ellipsoid is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | number} [stackPartitions=64] A Property specifying the number of stacks. * @property {Property | number} [slicePartitions=64] A Property specifying the number of radial slices. * @property {Property | number} [subdivisions=128] A Property specifying the number of samples per outline ring, determining the granularity of the curvature. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the ellipsoid casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this ellipsoid will be displayed. */ /** * Describe an ellipsoid or sphere. The center position and orientation are determined by the containing {@link Entity}. * * @alias EllipsoidGraphics * @constructor * * @param {EllipsoidGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Spheres%20and%20Ellipsoids.html|Cesium Sandcastle Spheres and Ellipsoids Demo} */ function EllipsoidGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._radii = undefined; this._radiiSubscription = undefined; this._innerRadii = undefined; this._innerRadiiSubscription = undefined; this._minimumClock = undefined; this._minimumClockSubscription = undefined; this._maximumClock = undefined; this._maximumClockSubscription = undefined; this._minimumCone = undefined; this._minimumConeSubscription = undefined; this._maximumCone = undefined; this._maximumConeSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._stackPartitions = undefined; this._stackPartitionsSubscription = undefined; this._slicePartitions = undefined; this._slicePartitionsSubscription = undefined; this._subdivisions = undefined; this._subdivisionsSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(EllipsoidGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof EllipsoidGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the {@link Cartesian3} {@link Property} specifying the radii of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} */ radii: createPropertyDescriptor("radii"), /** * Gets or sets the {@link Cartesian3} {@link Property} specifying the inner radii of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default radii */ innerRadii: createPropertyDescriptor("innerRadii"), /** * Gets or sets the Property specifying the minimum clock angle of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 0.0 */ minimumClock: createPropertyDescriptor("minimumClock"), /** * Gets or sets the Property specifying the maximum clock angle of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 2*PI */ maximumClock: createPropertyDescriptor("maximumClock"), /** * Gets or sets the Property specifying the minimum cone angle of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 0.0 */ minimumCone: createPropertyDescriptor("minimumCone"), /** * Gets or sets the Property specifying the maximum cone angle of the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default PI */ maximumCone: createPropertyDescriptor("maximumCone"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the boolean Property specifying whether the ellipsoid is filled with the provided material. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the ellipsoid. * @memberof EllipsoidGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the ellipsoid is outlined. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets the Property specifying the number of stacks. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 64 */ stackPartitions: createPropertyDescriptor("stackPartitions"), /** * Gets or sets the Property specifying the number of radial slices per 360 degrees. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 64 */ slicePartitions: createPropertyDescriptor("slicePartitions"), /** * Gets or sets the Property specifying the number of samples per outline ring, determining the granularity of the curvature. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default 128 */ subdivisions: createPropertyDescriptor("subdivisions"), /** * Get or sets the enum Property specifying whether the ellipsoid * casts or receives shadows from light sources. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this ellipsoid will be displayed. * @memberof EllipsoidGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {EllipsoidGraphics} [result] The object onto which to store the result. * @returns {EllipsoidGraphics} The modified result parameter or a new instance if one was not provided. */ EllipsoidGraphics.prototype.clone = function (result) { if (!defined(result)) { return new EllipsoidGraphics(this); } result.show = this.show; result.radii = this.radii; result.innerRadii = this.innerRadii; result.minimumClock = this.minimumClock; result.maximumClock = this.maximumClock; result.minimumCone = this.minimumCone; result.maximumCone = this.maximumCone; result.heightReference = this.heightReference; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.stackPartitions = this.stackPartitions; result.slicePartitions = this.slicePartitions; result.subdivisions = this.subdivisions; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {EllipsoidGraphics} source The object to be merged into this object. */ EllipsoidGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.radii = defaultValue(this.radii, source.radii); this.innerRadii = defaultValue(this.innerRadii, source.innerRadii); this.minimumClock = defaultValue(this.minimumClock, source.minimumClock); this.maximumClock = defaultValue(this.maximumClock, source.maximumClock); this.minimumCone = defaultValue(this.minimumCone, source.minimumCone); this.maximumCone = defaultValue(this.maximumCone, source.maximumCone); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.stackPartitions = defaultValue( this.stackPartitions, source.stackPartitions ); this.slicePartitions = defaultValue( this.slicePartitions, source.slicePartitions ); this.subdivisions = defaultValue(this.subdivisions, source.subdivisions); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * @typedef {Object} LabelGraphics.ConstructorOptions * * Initialization options for the LabelGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the label. * @property {Property | string} [text] A Property specifying the text. Explicit newlines '\n' are supported. * @property {Property | string} [font='30px sans-serif'] A Property specifying the CSS font. * @property {Property | LabelStyle} [style=LabelStyle.FILL] A Property specifying the {@link LabelStyle}. * @property {Property | number} [scale=1.0] A numeric Property specifying the scale to apply to the text. * @property {Property | boolean} [showBackground=false] A boolean Property specifying the visibility of the background behind the label. * @property {Property | Color} [backgroundColor=new Color(0.165, 0.165, 0.165, 0.8)] A Property specifying the background {@link Color}. * @property {Property | Cartesian2} [backgroundPadding=new Cartesian2(7, 5)] A {@link Cartesian2} Property specifying the horizontal and vertical background padding in pixels. * @property {Property | Cartesian2} [pixelOffset=Cartesian2.ZERO] A {@link Cartesian2} Property specifying the pixel offset. * @property {Property | Cartesian3} [eyeOffset=Cartesian3.ZERO] A {@link Cartesian3} Property specifying the eye offset. * @property {Property | HorizontalOrigin} [horizontalOrigin=HorizontalOrigin.CENTER] A Property specifying the {@link HorizontalOrigin}. * @property {Property | VerticalOrigin} [verticalOrigin=VerticalOrigin.CENTER] A Property specifying the {@link VerticalOrigin}. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | Color} [fillColor=Color.WHITE] A Property specifying the fill {@link Color}. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the outline {@link Color}. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the outline width. * @property {Property | NearFarScalar} [translucencyByDistance] A {@link NearFarScalar} Property used to set translucency based on distance from the camera. * @property {Property | NearFarScalar} [pixelOffsetScaleByDistance] A {@link NearFarScalar} Property used to set pixelOffset based on distance from the camera. * @property {Property | NearFarScalar} [scaleByDistance] A {@link NearFarScalar} Property used to set scale based on distance from the camera. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this label will be displayed. * @property {Property | number} [disableDepthTestDistance] A Property specifying the distance from the camera at which to disable the depth test to. */ /** * Describes a two dimensional label located at the position of the containing {@link Entity}. *

*

*
* Example labels *
*

* * @alias LabelGraphics * @constructor * * @param {LabelGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Labels.html|Cesium Sandcastle Labels Demo} */ function LabelGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._text = undefined; this._textSubscription = undefined; this._font = undefined; this._fontSubscription = undefined; this._style = undefined; this._styleSubscription = undefined; this._scale = undefined; this._scaleSubscription = undefined; this._showBackground = undefined; this._showBackgroundSubscription = undefined; this._backgroundColor = undefined; this._backgroundColorSubscription = undefined; this._backgroundPadding = undefined; this._backgroundPaddingSubscription = undefined; this._pixelOffset = undefined; this._pixelOffsetSubscription = undefined; this._eyeOffset = undefined; this._eyeOffsetSubscription = undefined; this._horizontalOrigin = undefined; this._horizontalOriginSubscription = undefined; this._verticalOrigin = undefined; this._verticalOriginSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._fillColor = undefined; this._fillColorSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._translucencyByDistance = undefined; this._translucencyByDistanceSubscription = undefined; this._pixelOffsetScaleByDistance = undefined; this._pixelOffsetScaleByDistanceSubscription = undefined; this._scaleByDistance = undefined; this._scaleByDistanceSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._disableDepthTestDistance = undefined; this._disableDepthTestDistanceSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(LabelGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof LabelGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the label. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ show: createPropertyDescriptor("show"), /** * Gets or sets the string Property specifying the text of the label. * Explicit newlines '\n' are supported. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ text: createPropertyDescriptor("text"), /** * Gets or sets the string Property specifying the font in CSS syntax. * @memberof LabelGraphics.prototype * @type {Property|undefined} * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/font|CSS font on MDN} */ font: createPropertyDescriptor("font"), /** * Gets or sets the Property specifying the {@link LabelStyle}. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ style: createPropertyDescriptor("style"), /** * Gets or sets the numeric Property specifying the uniform scale to apply to the image. * A scale greater than 1.0 enlarges the label while a scale less than 1.0 shrinks it. *

*

*
* From left to right in the above image, the scales are 0.5, 1.0, * and 2.0. *
*

* @memberof LabelGraphics.prototype * @type {Property|undefined} * @default 1.0 */ scale: createPropertyDescriptor("scale"), /** * Gets or sets the boolean Property specifying the visibility of the background behind the label. * @memberof LabelGraphics.prototype * @type {Property|undefined} * @default false */ showBackground: createPropertyDescriptor("showBackground"), /** * Gets or sets the Property specifying the background {@link Color}. * @memberof LabelGraphics.prototype * @type {Property|undefined} * @default new Color(0.165, 0.165, 0.165, 0.8) */ backgroundColor: createPropertyDescriptor("backgroundColor"), /** * Gets or sets the {@link Cartesian2} Property specifying the label's horizontal and vertical * background padding in pixels. * @memberof LabelGraphics.prototype * @type {Property|undefined} * @default new Cartesian2(7, 5) */ backgroundPadding: createPropertyDescriptor("backgroundPadding"), /** * Gets or sets the {@link Cartesian2} Property specifying the label's pixel offset in screen space * from the origin of this label. This is commonly used to align multiple labels and labels at * the same position, e.g., an image and text. The screen space origin is the top, left corner of the * canvas; x increases from left to right, and y increases from top to bottom. *

*

* * * *
default
l.pixeloffset = new Cartesian2(25, 75);
* The label's origin is indicated by the yellow point. *
*

* @memberof LabelGraphics.prototype * @type {Property|undefined} * @default Cartesian2.ZERO */ pixelOffset: createPropertyDescriptor("pixelOffset"), /** * Gets or sets the {@link Cartesian3} Property specifying the label's offset in eye coordinates. * Eye coordinates is a left-handed coordinate system, where x points towards the viewer's * right, y points up, and z points into the screen. *

* An eye offset is commonly used to arrange multiple labels or objects at the same position, e.g., to * arrange a label above its corresponding 3D model. *

* Below, the label is positioned at the center of the Earth but an eye offset makes it always * appear on top of the Earth regardless of the viewer's or Earth's orientation. *

*

* * * *
* l.eyeOffset = new Cartesian3(0.0, 8000000.0, 0.0);

*
*

* @memberof LabelGraphics.prototype * @type {Property|undefined} * @default Cartesian3.ZERO */ eyeOffset: createPropertyDescriptor("eyeOffset"), /** * Gets or sets the Property specifying the {@link HorizontalOrigin}. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ horizontalOrigin: createPropertyDescriptor("horizontalOrigin"), /** * Gets or sets the Property specifying the {@link VerticalOrigin}. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ verticalOrigin: createPropertyDescriptor("verticalOrigin"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof LabelGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the Property specifying the fill {@link Color}. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ fillColor: createPropertyDescriptor("fillColor"), /** * Gets or sets the Property specifying the outline {@link Color}. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the outline width. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets {@link NearFarScalar} Property specifying the translucency of the label based on the distance from the camera. * A label's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's translucency remains clamped to the nearest bound. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ translucencyByDistance: createPropertyDescriptor("translucencyByDistance"), /** * Gets or sets {@link NearFarScalar} Property specifying the pixel offset of the label based on the distance from the camera. * A label's pixel offset will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's pixel offset remains clamped to the nearest bound. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ pixelOffsetScaleByDistance: createPropertyDescriptor( "pixelOffsetScaleByDistance" ), /** * Gets or sets near and far scaling properties of a Label based on the label's distance from the camera. * A label's scale will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's scale remains clamped to the nearest bound. If undefined, * scaleByDistance will be disabled. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ scaleByDistance: createPropertyDescriptor("scaleByDistance"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this label will be displayed. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof LabelGraphics.prototype * @type {Property|undefined} */ disableDepthTestDistance: createPropertyDescriptor( "disableDepthTestDistance" ), }); /** * Duplicates this instance. * * @param {LabelGraphics} [result] The object onto which to store the result. * @returns {LabelGraphics} The modified result parameter or a new instance if one was not provided. */ LabelGraphics.prototype.clone = function (result) { if (!defined(result)) { return new LabelGraphics(this); } result.show = this.show; result.text = this.text; result.font = this.font; result.style = this.style; result.scale = this.scale; result.showBackground = this.showBackground; result.backgroundColor = this.backgroundColor; result.backgroundPadding = this.backgroundPadding; result.pixelOffset = this.pixelOffset; result.eyeOffset = this.eyeOffset; result.horizontalOrigin = this.horizontalOrigin; result.verticalOrigin = this.verticalOrigin; result.heightReference = this.heightReference; result.fillColor = this.fillColor; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.translucencyByDistance = this.translucencyByDistance; result.pixelOffsetScaleByDistance = this.pixelOffsetScaleByDistance; result.scaleByDistance = this.scaleByDistance; result.distanceDisplayCondition = this.distanceDisplayCondition; result.disableDepthTestDistance = this.disableDepthTestDistance; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {LabelGraphics} source The object to be merged into this object. */ LabelGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.text = defaultValue(this.text, source.text); this.font = defaultValue(this.font, source.font); this.style = defaultValue(this.style, source.style); this.scale = defaultValue(this.scale, source.scale); this.showBackground = defaultValue( this.showBackground, source.showBackground ); this.backgroundColor = defaultValue( this.backgroundColor, source.backgroundColor ); this.backgroundPadding = defaultValue( this.backgroundPadding, source.backgroundPadding ); this.pixelOffset = defaultValue(this.pixelOffset, source.pixelOffset); this.eyeOffset = defaultValue(this.eyeOffset, source.eyeOffset); this.horizontalOrigin = defaultValue( this.horizontalOrigin, source.horizontalOrigin ); this.verticalOrigin = defaultValue( this.verticalOrigin, source.verticalOrigin ); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.fillColor = defaultValue(this.fillColor, source.fillColor); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.translucencyByDistance = defaultValue( this.translucencyByDistance, source.translucencyByDistance ); this.pixelOffsetScaleByDistance = defaultValue( this.pixelOffsetScaleByDistance, source.pixelOffsetScaleByDistance ); this.scaleByDistance = defaultValue( this.scaleByDistance, source.scaleByDistance ); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.disableDepthTestDistance = defaultValue( this.disableDepthTestDistance, source.disableDepthTestDistance ); }; var defaultNodeTransformation = new TranslationRotationScale(); /** * A {@link Property} that produces {@link TranslationRotationScale} data. * @alias NodeTransformationProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|Cartesian3} [options.translation=Cartesian3.ZERO] A {@link Cartesian3} Property specifying the (x, y, z) translation to apply to the node. * @param {Property|Quaternion} [options.rotation=Quaternion.IDENTITY] A {@link Quaternion} Property specifying the (x, y, z, w) rotation to apply to the node. * @param {Property|Cartesian3} [options.scale=new Cartesian3(1.0, 1.0, 1.0)] A {@link Cartesian3} Property specifying the (x, y, z) scaling to apply to the node. */ function NodeTransformationProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._translation = undefined; this._translationSubscription = undefined; this._rotation = undefined; this._rotationSubscription = undefined; this._scale = undefined; this._scaleSubscription = undefined; this.translation = options.translation; this.rotation = options.rotation; this.scale = options.scale; } Object.defineProperties(NodeTransformationProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof NodeTransformationProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._translation) && Property.isConstant(this._rotation) && Property.isConstant(this._scale) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof NodeTransformationProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the {@link Cartesian3} Property specifying the (x, y, z) translation to apply to the node. * @memberof NodeTransformationProperty.prototype * @type {Property|undefined} * @default Cartesian3.ZERO */ translation: createPropertyDescriptor("translation"), /** * Gets or sets the {@link Quaternion} Property specifying the (x, y, z, w) rotation to apply to the node. * @memberof NodeTransformationProperty.prototype * @type {Property|undefined} * @default Quaternion.IDENTITY */ rotation: createPropertyDescriptor("rotation"), /** * Gets or sets the {@link Cartesian3} Property specifying the (x, y, z) scaling to apply to the node. * @memberof NodeTransformationProperty.prototype * @type {Property|undefined} * @default new Cartesian3(1.0, 1.0, 1.0) */ scale: createPropertyDescriptor("scale"), }); /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {TranslationRotationScale} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {TranslationRotationScale} The modified result parameter or a new instance if the result parameter was not supplied. */ NodeTransformationProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = new TranslationRotationScale(); } result.translation = Property.getValueOrClonedDefault( this._translation, time, defaultNodeTransformation.translation, result.translation ); result.rotation = Property.getValueOrClonedDefault( this._rotation, time, defaultNodeTransformation.rotation, result.rotation ); result.scale = Property.getValueOrClonedDefault( this._scale, time, defaultNodeTransformation.scale, result.scale ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ NodeTransformationProperty.prototype.equals = function (other) { return ( this === other || (other instanceof NodeTransformationProperty && Property.equals(this._translation, other._translation) && Property.equals(this._rotation, other._rotation) && Property.equals(this._scale, other._scale)) ); }; /** * A {@link Property} whose value is a key-value mapping of property names to the computed value of other properties. * * @alias PropertyBag * @constructor * @implements {DictionaryLike} * * @param {Object} [value] An object, containing key-value mapping of property names to properties. * @param {Function} [createPropertyCallback] A function that will be called when the value of any of the properties in value are not a Property. */ function PropertyBag(value, createPropertyCallback) { this._propertyNames = []; this._definitionChanged = new Event(); if (defined(value)) { this.merge(value, createPropertyCallback); } } Object.defineProperties(PropertyBag.prototype, { /** * Gets the names of all properties registered on this instance. * @memberof PropertyBag.prototype * @type {Array} */ propertyNames: { get: function () { return this._propertyNames; }, }, /** * Gets a value indicating if this property is constant. This property * is considered constant if all property items in this object are constant. * @memberof PropertyBag.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { var propertyNames = this._propertyNames; for (var i = 0, len = propertyNames.length; i < len; i++) { if (!Property.isConstant(this[propertyNames[i]])) { return false; } } return true; }, }, /** * Gets the event that is raised whenever the set of properties contained in this * object changes, or one of the properties itself changes. * * @memberof PropertyBag.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, }); /** * Determines if this object has defined a property with the given name. * * @param {String} propertyName The name of the property to check for. * * @returns {Boolean} True if this object has defined a property with the given name, false otherwise. */ PropertyBag.prototype.hasProperty = function (propertyName) { return this._propertyNames.indexOf(propertyName) !== -1; }; function createConstantProperty(value) { return new ConstantProperty(value); } /** * Adds a property to this object. * * @param {String} propertyName The name of the property to add. * @param {*} [value] The value of the new property, if provided. * @param {Function} [createPropertyCallback] A function that will be called when the value of this new property is set to a value that is not a Property. * * @exception {DeveloperError} "propertyName" is already a registered property. */ PropertyBag.prototype.addProperty = function ( propertyName, value, createPropertyCallback ) { var propertyNames = this._propertyNames; //>>includeStart('debug', pragmas.debug); if (!defined(propertyName)) { throw new DeveloperError("propertyName is required."); } if (propertyNames.indexOf(propertyName) !== -1) { throw new DeveloperError( propertyName + " is already a registered property." ); } //>>includeEnd('debug'); propertyNames.push(propertyName); Object.defineProperty( this, propertyName, createPropertyDescriptor( propertyName, true, defaultValue(createPropertyCallback, createConstantProperty) ) ); if (defined(value)) { this[propertyName] = value; } this._definitionChanged.raiseEvent(this); }; /** * Removed a property previously added with addProperty. * * @param {String} propertyName The name of the property to remove. * * @exception {DeveloperError} "propertyName" is not a registered property. */ PropertyBag.prototype.removeProperty = function (propertyName) { var propertyNames = this._propertyNames; var index = propertyNames.indexOf(propertyName); //>>includeStart('debug', pragmas.debug); if (!defined(propertyName)) { throw new DeveloperError("propertyName is required."); } if (index === -1) { throw new DeveloperError(propertyName + " is not a registered property."); } //>>includeEnd('debug'); this._propertyNames.splice(index, 1); delete this[propertyName]; this._definitionChanged.raiseEvent(this); }; /** * Gets the value of this property. Each contained property will be evaluated at the given time, and the overall * result will be an object, mapping property names to those values. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * Note that any properties in result which are not part of this PropertyBag will be left as-is. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ PropertyBag.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = {}; } var propertyNames = this._propertyNames; for (var i = 0, len = propertyNames.length; i < len; i++) { var propertyName = propertyNames[i]; result[propertyName] = Property.getValueOrUndefined( this[propertyName], time, result[propertyName] ); } return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {Object} source The object to be merged into this object. * @param {Function} [createPropertyCallback] A function that will be called when the value of any of the properties in value are not a Property. */ PropertyBag.prototype.merge = function (source, createPropertyCallback) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); var propertyNames = this._propertyNames; var sourcePropertyNames = defined(source._propertyNames) ? source._propertyNames : Object.keys(source); for (var i = 0, len = sourcePropertyNames.length; i < len; i++) { var name = sourcePropertyNames[i]; var targetProperty = this[name]; var sourceProperty = source[name]; //Custom properties that are registered on the source must also be added to this. if (targetProperty === undefined && propertyNames.indexOf(name) === -1) { this.addProperty(name, undefined, createPropertyCallback); } if (sourceProperty !== undefined) { if (targetProperty !== undefined) { if (defined(targetProperty) && defined(targetProperty.merge)) { targetProperty.merge(sourceProperty); } } else if ( defined(sourceProperty) && defined(sourceProperty.merge) && defined(sourceProperty.clone) ) { this[name] = sourceProperty.clone(); } else { this[name] = sourceProperty; } } } }; function propertiesEqual(a, b) { var aPropertyNames = a._propertyNames; var bPropertyNames = b._propertyNames; var len = aPropertyNames.length; if (len !== bPropertyNames.length) { return false; } for (var aIndex = 0; aIndex < len; ++aIndex) { var name = aPropertyNames[aIndex]; var bIndex = bPropertyNames.indexOf(name); if (bIndex === -1) { return false; } if (!Property.equals(a[name], b[name])) { return false; } } return true; } /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PropertyBag.prototype.equals = function (other) { return ( this === other || // (other instanceof PropertyBag && // propertiesEqual(this, other)) ); }; function createNodeTransformationProperty(value) { return new NodeTransformationProperty(value); } function createNodeTransformationPropertyBag(value) { return new PropertyBag(value, createNodeTransformationProperty); } function createArticulationStagePropertyBag(value) { return new PropertyBag(value); } /** * @typedef {Object} ModelGraphics.ConstructorOptions * * Initialization options for the ModelGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the model. * @property {Property | string | Resource} [uri] A string or Resource Property specifying the URI of the glTF asset. * @property {Property | number} [scale=1.0] A numeric Property specifying a uniform linear scale. * @property {Property | number} [minimumPixelSize=0.0] A numeric Property specifying the approximate minimum pixel size of the model regardless of zoom. * @property {Property | number} [maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize. * @property {Property | boolean} [incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @property {Property | boolean} [runAnimations=true] A boolean Property specifying if glTF animations specified in the model should be started. * @property {Property | boolean} [clampAnimations=true] A boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes. * @property {Property | ShadowMode} [shadows=ShadowMode.ENABLED] An enum Property specifying whether the model casts or receives shadows from light sources. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | Color} [silhouetteColor=Color.RED] A Property specifying the {@link Color} of the silhouette. * @property {Property | number} [silhouetteSize=0.0] A numeric Property specifying the size of the silhouette in pixels. * @property {Property | Color} [color=Color.WHITE] A Property specifying the {@link Color} that blends with the model's rendered color. * @property {Property | ColorBlendMode} [colorBlendMode=ColorBlendMode.HIGHLIGHT] An enum Property specifying how the color blends with the model. * @property {Property | number} [colorBlendAmount=0.5] A numeric Property specifying the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @property {Property | Cartesian2} [imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] A property specifying the contribution from diffuse and specular image-based lighting. * @property {Property | Color} [lightColor] A property specifying the light color when shading the model. When undefined the scene's light color is used instead. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this model will be displayed. * @property {PropertyBag | Object.} [nodeTransformations] An object, where keys are names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node. The transformation is applied after the node's existing transformation as specified in the glTF, and does not replace the node's existing transformation. * @property {PropertyBag | Object.} [articulations] An object, where keys are composed of an articulation name, a single space, and a stage name, and the values are numeric properties. * @property {Property | ClippingPlaneCollection} [clippingPlanes] A property specifying the {@link ClippingPlaneCollection} used to selectively disable rendering the model. */ /** * A 3D model based on {@link https://github.com/KhronosGroup/glTF|glTF}, the runtime asset format for WebGL, OpenGL ES, and OpenGL. * The position and orientation of the model is determined by the containing {@link Entity}. *

* Cesium includes support for glTF geometry, materials, animations, and skinning. * Cameras and lights are not currently supported. *

* * @alias ModelGraphics * @constructor * * @param {ModelGraphics.ConstructorOptions} [options] Object describing initialization options * * @see {@link https://cesium.com/docs/tutorials/3d-models/|3D Models Tutorial} * @demo {@link https://sandcastle.cesium.com/index.html?src=3D%20Models.html|Cesium Sandcastle 3D Models Demo} */ function ModelGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._uri = undefined; this._uriSubscription = undefined; this._scale = undefined; this._scaleSubscription = undefined; this._minimumPixelSize = undefined; this._minimumPixelSizeSubscription = undefined; this._maximumScale = undefined; this._maximumScaleSubscription = undefined; this._incrementallyLoadTextures = undefined; this._incrementallyLoadTexturesSubscription = undefined; this._runAnimations = undefined; this._runAnimationsSubscription = undefined; this._clampAnimations = undefined; this._clampAnimationsSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._silhouetteColor = undefined; this._silhouetteColorSubscription = undefined; this._silhouetteSize = undefined; this._silhouetteSizeSubscription = undefined; this._color = undefined; this._colorSubscription = undefined; this._colorBlendMode = undefined; this._colorBlendModeSubscription = undefined; this._colorBlendAmount = undefined; this._colorBlendAmountSubscription = undefined; this._imageBasedLightingFactor = undefined; this._imageBasedLightingFactorSubscription = undefined; this._lightColor = undefined; this._lightColorSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._nodeTransformations = undefined; this._nodeTransformationsSubscription = undefined; this._articulations = undefined; this._articulationsSubscription = undefined; this._clippingPlanes = undefined; this._clippingPlanesSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(ModelGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof ModelGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the model. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the string Property specifying the URI of the glTF asset. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ uri: createPropertyDescriptor("uri"), /** * Gets or sets the numeric Property specifying a uniform linear scale * for this model. Values greater than 1.0 increase the size of the model while * values less than 1.0 decrease it. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default 1.0 */ scale: createPropertyDescriptor("scale"), /** * Gets or sets the numeric Property specifying the approximate minimum * pixel size of the model regardless of zoom. This can be used to ensure that * a model is visible even when the viewer zooms out. When 0.0, * no minimum size is enforced. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default 0.0 */ minimumPixelSize: createPropertyDescriptor("minimumPixelSize"), /** * Gets or sets the numeric Property specifying the maximum scale * size of a model. This property is used as an upper limit for * {@link ModelGraphics#minimumPixelSize}. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ maximumScale: createPropertyDescriptor("maximumScale"), /** * Get or sets the boolean Property specifying whether textures * may continue to stream in after the model is loaded. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ incrementallyLoadTextures: createPropertyDescriptor( "incrementallyLoadTextures" ), /** * Gets or sets the boolean Property specifying if glTF animations should be run. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default true */ runAnimations: createPropertyDescriptor("runAnimations"), /** * Gets or sets the boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default true */ clampAnimations: createPropertyDescriptor("clampAnimations"), /** * Get or sets the enum Property specifying whether the model * casts or receives shadows from light sources. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default ShadowMode.ENABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the Property specifying the {@link Color} of the silhouette. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default Color.RED */ silhouetteColor: createPropertyDescriptor("silhouetteColor"), /** * Gets or sets the numeric Property specifying the size of the silhouette in pixels. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default 0.0 */ silhouetteSize: createPropertyDescriptor("silhouetteSize"), /** * Gets or sets the Property specifying the {@link Color} that blends with the model's rendered color. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), /** * Gets or sets the enum Property specifying how the color blends with the model. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default ColorBlendMode.HIGHLIGHT */ colorBlendMode: createPropertyDescriptor("colorBlendMode"), /** * A numeric Property specifying the color strength when the colorBlendMode is MIX. * A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with * any value in-between resulting in a mix of the two. * @memberof ModelGraphics.prototype * @type {Property|undefined} * @default 0.5 */ colorBlendAmount: createPropertyDescriptor("colorBlendAmount"), /** * A property specifying the {@link Cartesian2} used to scale the diffuse and specular image-based lighting contribution to the final color. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ imageBasedLightingFactor: createPropertyDescriptor( "imageBasedLightingFactor" ), /** * A property specifying the {@link Cartesian3} light color when shading the model. When undefined the scene's light color is used instead. * @memberOf ModelGraphics.prototype * @type {Property|undefined} */ lightColor: createPropertyDescriptor("lightColor"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this model will be displayed. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the set of node transformations to apply to this model. This is represented as an {@link PropertyBag}, where keys are * names of nodes, and values are {@link TranslationRotationScale} Properties describing the transformation to apply to that node. * The transformation is applied after the node's existing transformation as specified in the glTF, and does not replace the node's existing transformation. * @memberof ModelGraphics.prototype * @type {PropertyBag} */ nodeTransformations: createPropertyDescriptor( "nodeTransformations", undefined, createNodeTransformationPropertyBag ), /** * Gets or sets the set of articulation values to apply to this model. This is represented as an {@link PropertyBag}, where keys are * composed as the name of the articulation, a single space, and the name of the stage. * @memberof ModelGraphics.prototype * @type {PropertyBag} */ articulations: createPropertyDescriptor( "articulations", undefined, createArticulationStagePropertyBag ), /** * A property specifying the {@link ClippingPlaneCollection} used to selectively disable rendering the model. * @memberof ModelGraphics.prototype * @type {Property|undefined} */ clippingPlanes: createPropertyDescriptor("clippingPlanes"), }); /** * Duplicates this instance. * * @param {ModelGraphics} [result] The object onto which to store the result. * @returns {ModelGraphics} The modified result parameter or a new instance if one was not provided. */ ModelGraphics.prototype.clone = function (result) { if (!defined(result)) { return new ModelGraphics(this); } result.show = this.show; result.uri = this.uri; result.scale = this.scale; result.minimumPixelSize = this.minimumPixelSize; result.maximumScale = this.maximumScale; result.incrementallyLoadTextures = this.incrementallyLoadTextures; result.runAnimations = this.runAnimations; result.clampAnimations = this.clampAnimations; result.heightReference = this._heightReference; result.silhouetteColor = this.silhouetteColor; result.silhouetteSize = this.silhouetteSize; result.color = this.color; result.colorBlendMode = this.colorBlendMode; result.colorBlendAmount = this.colorBlendAmount; result.imageBasedLightingFactor = this.imageBasedLightingFactor; result.lightColor = this.lightColor; result.distanceDisplayCondition = this.distanceDisplayCondition; result.nodeTransformations = this.nodeTransformations; result.articulations = this.articulations; result.clippingPlanes = this.clippingPlanes; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {ModelGraphics} source The object to be merged into this object. */ ModelGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.uri = defaultValue(this.uri, source.uri); this.scale = defaultValue(this.scale, source.scale); this.minimumPixelSize = defaultValue( this.minimumPixelSize, source.minimumPixelSize ); this.maximumScale = defaultValue(this.maximumScale, source.maximumScale); this.incrementallyLoadTextures = defaultValue( this.incrementallyLoadTextures, source.incrementallyLoadTextures ); this.runAnimations = defaultValue(this.runAnimations, source.runAnimations); this.clampAnimations = defaultValue( this.clampAnimations, source.clampAnimations ); this.shadows = defaultValue(this.shadows, source.shadows); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.silhouetteColor = defaultValue( this.silhouetteColor, source.silhouetteColor ); this.silhouetteSize = defaultValue( this.silhouetteSize, source.silhouetteSize ); this.color = defaultValue(this.color, source.color); this.colorBlendMode = defaultValue( this.colorBlendMode, source.colorBlendMode ); this.colorBlendAmount = defaultValue( this.colorBlendAmount, source.colorBlendAmount ); this.imageBasedLightingFactor = defaultValue( this.imageBasedLightingFactor, source.imageBasedLightingFactor ); this.lightColor = defaultValue(this.lightColor, source.lightColor); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.clippingPlanes = defaultValue( this.clippingPlanes, source.clippingPlanes ); var sourceNodeTransformations = source.nodeTransformations; if (defined(sourceNodeTransformations)) { var targetNodeTransformations = this.nodeTransformations; if (defined(targetNodeTransformations)) { targetNodeTransformations.merge(sourceNodeTransformations); } else { this.nodeTransformations = new PropertyBag( sourceNodeTransformations, createNodeTransformationProperty ); } } var sourceArticulations = source.articulations; if (defined(sourceArticulations)) { var targetArticulations = this.articulations; if (defined(targetArticulations)) { targetArticulations.merge(sourceArticulations); } else { this.articulations = new PropertyBag(sourceArticulations); } } }; /** * @typedef {Object} Cesium3DTilesetGraphics.ConstructorOptions * * Initialization options for the Cesium3DTilesetGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the tileset. * @property {Property | string | Resource} [uri] A string or Resource Property specifying the URI of the tileset. * @property {Property | number} [maximumScreenSpaceError] A number or Property specifying the maximum screen space error used to drive level of detail refinement. */ /** * A 3D Tiles tileset represented by an {@link Entity}. * The tileset modelMatrix is determined by the containing Entity position and orientation * or is left unset if position is undefined. * * @alias Cesium3DTilesetGraphics * @constructor * * @param {Cesium3DTilesetGraphics.ConstructorOptions} [options] Object describing initialization options */ function Cesium3DTilesetGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._uri = undefined; this._uriSubscription = undefined; this._maximumScreenSpaceError = undefined; this._maximumScreenSpaceErrorSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(Cesium3DTilesetGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof Cesium3DTilesetGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the model. * @memberof Cesium3DTilesetGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the string Property specifying the URI of the glTF asset. * @memberof Cesium3DTilesetGraphics.prototype * @type {Property|undefined} */ uri: createPropertyDescriptor("uri"), /** * Gets or sets the maximum screen space error used to drive level of detail refinement. * @memberof Cesium3DTilesetGraphics.prototype * @type {Property|undefined} */ maximumScreenSpaceError: createPropertyDescriptor("maximumScreenSpaceError"), }); /** * Duplicates this instance. * * @param {Cesium3DTilesetGraphics} [result] The object onto which to store the result. * @returns {Cesium3DTilesetGraphics} The modified result parameter or a new instance if one was not provided. */ Cesium3DTilesetGraphics.prototype.clone = function (result) { if (!defined(result)) { return new Cesium3DTilesetGraphics(this); } result.show = this.show; result.uri = this.uri; result.maximumScreenSpaceError = this.maximumScreenSpaceError; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {Cesium3DTilesetGraphics} source The object to be merged into this object. */ Cesium3DTilesetGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.uri = defaultValue(this.uri, source.uri); this.maximumScreenSpaceError = defaultValue( this.maximumScreenSpaceError, source.maximumScreenSpaceError ); }; /** * @typedef {Object} PathGraphics.ConstructorOptions * * Initialization options for the PathGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the path. * @property {Property | number} [leadTime] A Property specifying the number of seconds in front the object to show. * @property {Property | number} [trailTime] A Property specifying the number of seconds behind of the object to show. * @property {Property | number} [width=1.0] A numeric Property specifying the width in pixels. * @property {Property | number} [resolution=60] A numeric Property specifying the maximum number of seconds to step when sampling the position. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to draw the path. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this path will be displayed. */ /** * Describes a polyline defined as the path made by an {@link Entity} as it moves over time. * * @alias PathGraphics * @constructor * * @param {PathGraphics.ConstructorOptions} [options] Object describing initialization options */ function PathGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._leadTime = undefined; this._leadTimeSubscription = undefined; this._trailTime = undefined; this._trailTimeSubscription = undefined; this._width = undefined; this._widthSubscription = undefined; this._resolution = undefined; this._resolutionSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PathGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PathGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the path. * @memberof PathGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the number of seconds in front of the object to show. * @memberof PathGraphics.prototype * @type {Property|undefined} */ leadTime: createPropertyDescriptor("leadTime"), /** * Gets or sets the Property specifying the number of seconds behind the object to show. * @memberof PathGraphics.prototype * @type {Property|undefined} */ trailTime: createPropertyDescriptor("trailTime"), /** * Gets or sets the numeric Property specifying the width in pixels. * @memberof PathGraphics.prototype * @type {Property|undefined} * @default 1.0 */ width: createPropertyDescriptor("width"), /** * Gets or sets the Property specifying the maximum number of seconds to step when sampling the position. * @memberof PathGraphics.prototype * @type {Property|undefined} * @default 60 */ resolution: createPropertyDescriptor("resolution"), /** * Gets or sets the Property specifying the material used to draw the path. * @memberof PathGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this path will be displayed. * @memberof PathGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {PathGraphics} [result] The object onto which to store the result. * @returns {PathGraphics} The modified result parameter or a new instance if one was not provided. */ PathGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PathGraphics(this); } result.show = this.show; result.leadTime = this.leadTime; result.trailTime = this.trailTime; result.width = this.width; result.resolution = this.resolution; result.material = this.material; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PathGraphics} source The object to be merged into this object. */ PathGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.leadTime = defaultValue(this.leadTime, source.leadTime); this.trailTime = defaultValue(this.trailTime, source.trailTime); this.width = defaultValue(this.width, source.width); this.resolution = defaultValue(this.resolution, source.resolution); this.material = defaultValue(this.material, source.material); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * @typedef {Object} PlaneGraphics.ConstructorOptions * * Initialization options for the PlaneGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the plane. * @property {Property | Plane} [plane] A {@link Plane} Property specifying the normal and distance for the plane. * @property {Property | Cartesian2} [dimensions] A {@link Cartesian2} Property specifying the width and height of the plane. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the plane is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the plane. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the plane is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the plane casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this plane will be displayed. */ /** * Describes a plane. The center position and orientation are determined by the containing {@link Entity}. * * @alias PlaneGraphics * @constructor * * @param {PlaneGraphics.ConstructorOptions} [options] Object describing initialization options * * @demo {@link https://sandcastle.cesium.com/index.html?src=Plane.html|Cesium Sandcastle Plane Demo} */ function PlaneGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._plane = undefined; this._planeSubscription = undefined; this._dimensions = undefined; this._dimensionsSubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PlaneGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PlaneGraphics.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the plane. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the {@link Plane} Property specifying the normal and distance of the plane. * * @memberof PlaneGraphics.prototype * @type {Property|undefined} */ plane: createPropertyDescriptor("plane"), /** * Gets or sets the {@link Cartesian2} Property specifying the width and height of the plane. * * @memberof PlaneGraphics.prototype * @type {Property|undefined} */ dimensions: createPropertyDescriptor("dimensions"), /** * Gets or sets the boolean Property specifying whether the plane is filled with the provided material. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the material used to fill the plane. * @memberof PlaneGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the plane is outlined. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the plane * casts or receives shadows from light sources. * @memberof PlaneGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this plane will be displayed. * @memberof PlaneGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {PlaneGraphics} [result] The object onto which to store the result. * @returns {PlaneGraphics} The modified result parameter or a new instance if one was not provided. */ PlaneGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PlaneGraphics(this); } result.show = this.show; result.plane = this.plane; result.dimensions = this.dimensions; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PlaneGraphics} source The object to be merged into this object. */ PlaneGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.plane = defaultValue(this.plane, source.plane); this.dimensions = defaultValue(this.dimensions, source.dimensions); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * @typedef {Object} PointGraphics.ConstructorOptions * * Initialization options for the PointGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the point. * @property {Property | number} [pixelSize=1] A numeric Property specifying the size in pixels. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | Color} [color=Color.WHITE] A Property specifying the {@link Color} of the point. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=0] A numeric Property specifying the the outline width in pixels. * @property {Property | NearFarScalar} [scaleByDistance] A {@link NearFarScalar} Property used to scale the point based on distance. * @property {Property | NearFarScalar} [translucencyByDistance] A {@link NearFarScalar} Property used to set translucency based on distance from the camera. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this point will be displayed. * @property {Property | number} [disableDepthTestDistance] A Property specifying the distance from the camera at which to disable the depth test to. */ /** * Describes a graphical point located at the position of the containing {@link Entity}. * * @alias PointGraphics * @constructor * * @param {PointGraphics.ConstructorOptions} [options] Object describing initialization options */ function PointGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._pixelSize = undefined; this._pixelSizeSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._color = undefined; this._colorSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._scaleByDistance = undefined; this._scaleByDistanceSubscription = undefined; this._translucencyByDistance = undefined; this._translucencyByDistanceSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._disableDepthTestDistance = undefined; this._disableDepthTestDistanceSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PointGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PointGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the point. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the numeric Property specifying the size in pixels. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default 1 */ pixelSize: createPropertyDescriptor("pixelSize"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the Property specifying the {@link Color} of the point. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the the outline width in pixels. * @memberof PointGraphics.prototype * @type {Property|undefined} * @default 0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets the {@link NearFarScalar} Property used to scale the point based on distance. * If undefined, a constant size is used. * @memberof PointGraphics.prototype * @type {Property|undefined} */ scaleByDistance: createPropertyDescriptor("scaleByDistance"), /** * Gets or sets {@link NearFarScalar} Property specifying the translucency of the point based on the distance from the camera. * A point's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the points's translucency remains clamped to the nearest bound. * @memberof PointGraphics.prototype * @type {Property|undefined} */ translucencyByDistance: createPropertyDescriptor("translucencyByDistance"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this point will be displayed. * @memberof PointGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof PointGraphics.prototype * @type {Property|undefined} */ disableDepthTestDistance: createPropertyDescriptor( "disableDepthTestDistance" ), }); /** * Duplicates this instance. * * @param {PointGraphics} [result] The object onto which to store the result. * @returns {PointGraphics} The modified result parameter or a new instance if one was not provided. */ PointGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PointGraphics(this); } result.show = this.show; result.pixelSize = this.pixelSize; result.heightReference = this.heightReference; result.color = this.color; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.scaleByDistance = this.scaleByDistance; result.translucencyByDistance = this._translucencyByDistance; result.distanceDisplayCondition = this.distanceDisplayCondition; result.disableDepthTestDistance = this.disableDepthTestDistance; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PointGraphics} source The object to be merged into this object. */ PointGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.pixelSize = defaultValue(this.pixelSize, source.pixelSize); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.color = defaultValue(this.color, source.color); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.scaleByDistance = defaultValue( this.scaleByDistance, source.scaleByDistance ); this.translucencyByDistance = defaultValue( this._translucencyByDistance, source.translucencyByDistance ); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.disableDepthTestDistance = defaultValue( this.disableDepthTestDistance, source.disableDepthTestDistance ); }; function createPolygonHierarchyProperty(value) { if (Array.isArray(value)) { // convert array of positions to PolygonHierarchy object value = new PolygonHierarchy(value); } return new ConstantProperty(value); } /** * @typedef {Object} PolygonGraphics.ConstructorOptions * * Initialization options for the PolygonGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the polygon. * @property {Property | PolygonHierarchy} [hierarchy] A Property specifying the {@link PolygonHierarchy}. * @property {Property | number} [height=0] A numeric Property specifying the altitude of the polygon relative to the ellipsoid surface. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | number} [extrudedHeight] A numeric Property specifying the altitude of the polygon's extruded face relative to the ellipsoid surface. * @property {Property | HeightReference} [extrudedHeightReference=HeightReference.NONE] A Property specifying what the extrudedHeight is relative to. * @property {Property | number} [stRotation=0.0] A numeric property specifying the rotation of the polygon texture counter-clockwise from north. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between each latitude and longitude point. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the polygon is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the polygon. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the polygon is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | boolean} [perPositionHeight=false] A boolean specifying whether or not the height of each position is used. * @property {Boolean | boolean} [closeTop=true] When false, leaves off the top of an extruded polygon open. * @property {Boolean | boolean} [closeBottom=true] When false, leaves off the bottom of an extruded polygon open. * @property {Property | ArcType} [arcType=ArcType.GEODESIC] The type of line the polygon edges must follow. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the polygon casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this polygon will be displayed. * @property {Property | ClassificationType} [classificationType=ClassificationType.BOTH] An enum Property specifying whether this polygon will classify terrain, 3D Tiles, or both when on the ground. * @property {ConstantProperty | number} [zIndex=0] A property specifying the zIndex used for ordering ground geometry. Only has an effect if the polygon is constant and neither height or extrudedHeight are specified. */ /** * Describes a polygon defined by an hierarchy of linear rings which make up the outer shape and any nested holes. * The polygon conforms to the curvature of the globe and can be placed on the surface or * at altitude and can optionally be extruded into a volume. * * @alias PolygonGraphics * @constructor * * @param {PolygonGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Polygon.html|Cesium Sandcastle Polygon Demo} */ function PolygonGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._hierarchy = undefined; this._hierarchySubscription = undefined; this._height = undefined; this._heightSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; this._extrudedHeightReference = undefined; this._extrudedHeightReferenceSubscription = undefined; this._stRotation = undefined; this._stRotationSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._perPositionHeight = undefined; this._perPositionHeightSubscription = undefined; this._closeTop = undefined; this._closeTopSubscription = undefined; this._closeBottom = undefined; this._closeBottomSubscription = undefined; this._arcType = undefined; this._arcTypeSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._classificationType = undefined; this._classificationTypeSubscription = undefined; this._zIndex = undefined; this._zIndexSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PolygonGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PolygonGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the polygon. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the {@link PolygonHierarchy}. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ hierarchy: createPropertyDescriptor( "hierarchy", undefined, createPolygonHierarchyProperty ), /** * Gets or sets the numeric Property specifying the constant altitude of the polygon. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default 0.0 */ height: createPropertyDescriptor("height"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the numeric Property specifying the altitude of the polygon extrusion. * If {@link PolygonGraphics#perPositionHeight} is false, the volume starts at {@link PolygonGraphics#height} and ends at this altitude. * If {@link PolygonGraphics#perPositionHeight} is true, the volume starts at the height of each {@link PolygonGraphics#hierarchy} position and ends at this altitude. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ extrudedHeight: createPropertyDescriptor("extrudedHeight"), /** * Gets or sets the Property specifying the extruded {@link HeightReference}. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ extrudedHeightReference: createPropertyDescriptor("extrudedHeightReference"), /** * Gets or sets the numeric property specifying the rotation of the polygon texture counter-clockwise from north. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default 0 */ stRotation: createPropertyDescriptor("stRotation"), /** * Gets or sets the numeric Property specifying the angular distance between points on the polygon. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the polygon is filled with the provided material. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the polygon. * @memberof PolygonGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the polygon is outlined. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Gets or sets the boolean specifying whether or not the the height of each position is used. * If true, the shape will have non-uniform altitude defined by the height of each {@link PolygonGraphics#hierarchy} position. * If false, the shape will have a constant altitude as specified by {@link PolygonGraphics#height}. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ perPositionHeight: createPropertyDescriptor("perPositionHeight"), /** * Gets or sets a boolean specifying whether or not the top of an extruded polygon is included. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ closeTop: createPropertyDescriptor("closeTop"), /** * Gets or sets a boolean specifying whether or not the bottom of an extruded polygon is included. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ closeBottom: createPropertyDescriptor("closeBottom"), /** * Gets or sets the {@link ArcType} Property specifying the type of lines the polygon edges use. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default ArcType.GEODESIC */ arcType: createPropertyDescriptor("arcType"), /** * Get or sets the enum Property specifying whether the polygon * casts or receives shadows from light sources. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this polygon will be displayed. * @memberof PolygonGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the {@link ClassificationType} Property specifying whether this polygon will classify terrain, 3D Tiles, or both when on the ground. * @memberof PolygonGraphics.prototype * @type {Property|undefined} * @default ClassificationType.BOTH */ classificationType: createPropertyDescriptor("classificationType"), /** * Gets or sets the zIndex Prperty specifying the ordering of ground geometry. Only has an effect if the polygon is constant and neither height or extrudedHeight are specified. * @memberof PolygonGraphics.prototype * @type {ConstantProperty|undefined} * @default 0 */ zIndex: createPropertyDescriptor("zIndex"), }); /** * Duplicates this instance. * * @param {PolygonGraphics} [result] The object onto which to store the result. * @returns {PolygonGraphics} The modified result parameter or a new instance if one was not provided. */ PolygonGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PolygonGraphics(this); } result.show = this.show; result.hierarchy = this.hierarchy; result.height = this.height; result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; result.extrudedHeightReference = this.extrudedHeightReference; result.stRotation = this.stRotation; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.perPositionHeight = this.perPositionHeight; result.closeTop = this.closeTop; result.closeBottom = this.closeBottom; result.arcType = this.arcType; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; result.classificationType = this.classificationType; result.zIndex = this.zIndex; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PolygonGraphics} source The object to be merged into this object. */ PolygonGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.hierarchy = defaultValue(this.hierarchy, source.hierarchy); this.height = defaultValue(this.height, source.height); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.extrudedHeight = defaultValue( this.extrudedHeight, source.extrudedHeight ); this.extrudedHeightReference = defaultValue( this.extrudedHeightReference, source.extrudedHeightReference ); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.perPositionHeight = defaultValue( this.perPositionHeight, source.perPositionHeight ); this.closeTop = defaultValue(this.closeTop, source.closeTop); this.closeBottom = defaultValue(this.closeBottom, source.closeBottom); this.arcType = defaultValue(this.arcType, source.arcType); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.classificationType = defaultValue( this.classificationType, source.classificationType ); this.zIndex = defaultValue(this.zIndex, source.zIndex); }; /** * @typedef {Object} PolylineGraphics.ConstructorOptions * * Initialization options for the PolylineGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the polyline. * @property {Property | Array} [positions] A Property specifying the array of {@link Cartesian3} positions that define the line strip. * @property {Property | number} [width=1.0] A numeric Property specifying the width in pixels. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between each latitude and longitude if arcType is not ArcType.NONE. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to draw the polyline. * @property {MaterialProperty | Color} [depthFailMaterial] A property specifying the material used to draw the polyline when it is below the terrain. * @property {Property | ArcType} [arcType=ArcType.GEODESIC] The type of line the polyline segments must follow. * @property {Property | boolean} [clampToGround=false] A boolean Property specifying whether the Polyline should be clamped to the ground. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the polyline casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this polyline will be displayed. * @property {Property | ClassificationType} [classificationType=ClassificationType.BOTH] An enum Property specifying whether this polyline will classify terrain, 3D Tiles, or both when on the ground. * @property {Property | number} [zIndex=0] A Property specifying the zIndex used for ordering ground geometry. Only has an effect if `clampToGround` is true and polylines on terrain is supported. */ /** * Describes a polyline. The first two positions define a line segment, * and each additional position defines a line segment from the previous position. The segments * can be linear connected points, great arcs, or clamped to terrain. * * @alias PolylineGraphics * @constructor * * @param {PolylineGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Polyline.html|Cesium Sandcastle Polyline Demo} */ function PolylineGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._positions = undefined; this._positionsSubscription = undefined; this._width = undefined; this._widthSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._depthFailMaterial = undefined; this._depthFailMaterialSubscription = undefined; this._arcType = undefined; this._arcTypeSubscription = undefined; this._clampToGround = undefined; this._clampToGroundSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this._classificationType = undefined; this._classificationTypeSubscription = undefined; this._zIndex = undefined; this._zIndexSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PolylineGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PolylineGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the polyline. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the array of {@link Cartesian3} * positions that define the line strip. * @memberof PolylineGraphics.prototype * @type {Property|undefined} */ positions: createPropertyDescriptor("positions"), /** * Gets or sets the numeric Property specifying the width in pixels. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default 1.0 */ width: createPropertyDescriptor("width"), /** * Gets or sets the numeric Property specifying the angular distance between each latitude and longitude if arcType is not ArcType.NONE and clampToGround is false. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default Cesium.Math.RADIANS_PER_DEGREE */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the Property specifying the material used to draw the polyline. * @memberof PolylineGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying the material used to draw the polyline when it fails the depth test. *

* Requires the EXT_frag_depth WebGL extension to render properly. If the extension is not supported, * there may be artifacts. *

* @memberof PolylineGraphics.prototype * @type {MaterialProperty} * @default undefined */ depthFailMaterial: createMaterialPropertyDescriptor("depthFailMaterial"), /** * Gets or sets the {@link ArcType} Property specifying whether the line segments should be great arcs, rhumb lines or linearly connected. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default ArcType.GEODESIC */ arcType: createPropertyDescriptor("arcType"), /** * Gets or sets the boolean Property specifying whether the polyline * should be clamped to the ground. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default false */ clampToGround: createPropertyDescriptor("clampToGround"), /** * Get or sets the enum Property specifying whether the polyline * casts or receives shadows from light sources. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this polyline will be displayed. * @memberof PolylineGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the {@link ClassificationType} Property specifying whether this polyline will classify terrain, 3D Tiles, or both when on the ground. * @memberof PolylineGraphics.prototype * @type {Property|undefined} * @default ClassificationType.BOTH */ classificationType: createPropertyDescriptor("classificationType"), /** * Gets or sets the zIndex Property specifying the ordering of the polyline. Only has an effect if `clampToGround` is true and polylines on terrain is supported. * @memberof PolylineGraphics.prototype * @type {ConstantProperty|undefined} * @default 0 */ zIndex: createPropertyDescriptor("zIndex"), }); /** * Duplicates this instance. * * @param {PolylineGraphics} [result] The object onto which to store the result. * @returns {PolylineGraphics} The modified result parameter or a new instance if one was not provided. */ PolylineGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PolylineGraphics(this); } result.show = this.show; result.positions = this.positions; result.width = this.width; result.granularity = this.granularity; result.material = this.material; result.depthFailMaterial = this.depthFailMaterial; result.arcType = this.arcType; result.clampToGround = this.clampToGround; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; result.classificationType = this.classificationType; result.zIndex = this.zIndex; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PolylineGraphics} source The object to be merged into this object. */ PolylineGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.positions = defaultValue(this.positions, source.positions); this.width = defaultValue(this.width, source.width); this.granularity = defaultValue(this.granularity, source.granularity); this.material = defaultValue(this.material, source.material); this.depthFailMaterial = defaultValue( this.depthFailMaterial, source.depthFailMaterial ); this.arcType = defaultValue(this.arcType, source.arcType); this.clampToGround = defaultValue(this.clampToGround, source.clampToGround); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.classificationType = defaultValue( this.classificationType, source.classificationType ); this.zIndex = defaultValue(this.zIndex, source.zIndex); }; /** * @typedef {Object} PolylineVolumeGraphics.ConstructorOptions * * Initialization options for the PolylineVolumeGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the volume. * @property {Property | Array} [positions] A Property specifying the array of {@link Cartesian3} positions which define the line strip. * @property {Property | Array} [shape] A Property specifying the array of {@link Cartesian2} positions which define the shape to be extruded. * @property {Property | CornerType} [cornerType=CornerType.ROUNDED] A {@link CornerType} Property specifying the style of the corners. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between each latitude and longitude point. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the volume is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the volume. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the volume is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the volume casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this volume will be displayed. */ /** * Describes a polyline volume defined as a line strip and corresponding two dimensional shape which is extruded along it. * The resulting volume conforms to the curvature of the globe. * * @alias PolylineVolumeGraphics * @constructor * * @param {PolylineVolumeGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Polyline%20Volume.html|Cesium Sandcastle Polyline Volume Demo} */ function PolylineVolumeGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._positions = undefined; this._positionsSubscription = undefined; this._shape = undefined; this._shapeSubscription = undefined; this._cornerType = undefined; this._cornerTypeSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubsription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(PolylineVolumeGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof PolylineVolumeGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the volume. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the array of {@link Cartesian3} positions which define the line strip. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} */ positions: createPropertyDescriptor("positions"), /** * Gets or sets the Property specifying the array of {@link Cartesian2} positions which define the shape to be extruded. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} */ shape: createPropertyDescriptor("shape"), /** * Gets or sets the {@link CornerType} Property specifying the style of the corners. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default CornerType.ROUNDED */ cornerType: createPropertyDescriptor("cornerType"), /** * Gets or sets the numeric Property specifying the angular distance between points on the volume. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the volume is filled with the provided material. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the volume. * @memberof PolylineVolumeGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the volume is outlined. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the volume * casts or receives shadows from light sources. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this volume will be displayed. * @memberof PolylineVolumeGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {PolylineVolumeGraphics} [result] The object onto which to store the result. * @returns {PolylineVolumeGraphics} The modified result parameter or a new instance if one was not provided. */ PolylineVolumeGraphics.prototype.clone = function (result) { if (!defined(result)) { return new PolylineVolumeGraphics(this); } result.show = this.show; result.positions = this.positions; result.shape = this.shape; result.cornerType = this.cornerType; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {PolylineVolumeGraphics} source The object to be merged into this object. */ PolylineVolumeGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.positions = defaultValue(this.positions, source.positions); this.shape = defaultValue(this.shape, source.shape); this.cornerType = defaultValue(this.cornerType, source.cornerType); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; /** * @typedef {Object} RectangleGraphics.ConstructorOptions * * Initialization options for the RectangleGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the rectangle. * @property {Property | Rectangle} [coordinates] The Property specifying the {@link Rectangle}. * @property {Property | number} [height=0] A numeric Property specifying the altitude of the rectangle relative to the ellipsoid surface. * @property {Property | HeightReference} [heightReference=HeightReference.NONE] A Property specifying what the height is relative to. * @property {Property | number} [extrudedHeight] A numeric Property specifying the altitude of the rectangle's extruded face relative to the ellipsoid surface. * @property {Property | HeightReference} [extrudedHeightReference=HeightReference.NONE] A Property specifying what the extrudedHeight is relative to. * @property {Property | number} [rotation=0.0] A numeric property specifying the rotation of the rectangle clockwise from north. * @property {Property | number} [stRotation=0.0] A numeric property specifying the rotation of the rectangle texture counter-clockwise from north. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between points on the rectangle. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the rectangle is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the rectangle. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the rectangle is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the rectangle casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this rectangle will be displayed. * @property {Property | ClassificationType} [classificationType=ClassificationType.BOTH] An enum Property specifying whether this rectangle will classify terrain, 3D Tiles, or both when on the ground. * @property {Property | number} [zIndex=0] A Property specifying the zIndex used for ordering ground geometry. Only has an effect if the rectangle is constant and neither height or extrudedHeight are specified. */ /** * Describes graphics for a {@link Rectangle}. * The rectangle conforms to the curvature of the globe and can be placed on the surface or * at altitude and can optionally be extruded into a volume. * * @alias RectangleGraphics * @constructor * * @param {RectangleGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Rectangle.html|Cesium Sandcastle Rectangle Demo} */ function RectangleGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._coordinates = undefined; this._coordinatesSubscription = undefined; this._height = undefined; this._heightSubscription = undefined; this._heightReference = undefined; this._heightReferenceSubscription = undefined; this._extrudedHeight = undefined; this._extrudedHeightSubscription = undefined; this._extrudedHeightReference = undefined; this._extrudedHeightReferenceSubscription = undefined; this._rotation = undefined; this._rotationSubscription = undefined; this._stRotation = undefined; this._stRotationSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distancedisplayConditionSubscription = undefined; this._classificationType = undefined; this._classificationTypeSubscription = undefined; this._zIndex = undefined; this._zIndexSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(RectangleGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof RectangleGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the rectangle. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the {@link Rectangle}. * @memberof RectangleGraphics.prototype * @type {Property|undefined} */ coordinates: createPropertyDescriptor("coordinates"), /** * Gets or sets the numeric Property specifying the altitude of the rectangle. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default 0.0 */ height: createPropertyDescriptor("height"), /** * Gets or sets the Property specifying the {@link HeightReference}. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ heightReference: createPropertyDescriptor("heightReference"), /** * Gets or sets the numeric Property specifying the altitude of the rectangle extrusion. * Setting this property creates volume starting at height and ending at this altitude. * @memberof RectangleGraphics.prototype * @type {Property|undefined} */ extrudedHeight: createPropertyDescriptor("extrudedHeight"), /** * Gets or sets the Property specifying the extruded {@link HeightReference}. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default HeightReference.NONE */ extrudedHeightReference: createPropertyDescriptor("extrudedHeightReference"), /** * Gets or sets the numeric property specifying the rotation of the rectangle clockwise from north. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default 0 */ rotation: createPropertyDescriptor("rotation"), /** * Gets or sets the numeric property specifying the rotation of the rectangle texture counter-clockwise from north. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default 0 */ stRotation: createPropertyDescriptor("stRotation"), /** * Gets or sets the numeric Property specifying the angular distance between points on the rectangle. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the rectangle is filled with the provided material. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the rectangle. * @memberof RectangleGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the rectangle is outlined. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the rectangle * casts or receives shadows from light sources. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this rectangle will be displayed. * @memberof RectangleGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), /** * Gets or sets the {@link ClassificationType} Property specifying whether this rectangle will classify terrain, 3D Tiles, or both when on the ground. * @memberof RectangleGraphics.prototype * @type {Property|undefined} * @default ClassificationType.BOTH */ classificationType: createPropertyDescriptor("classificationType"), /** * Gets or sets the zIndex Property specifying the ordering of the rectangle. Only has an effect if the rectangle is constant and neither height or extrudedHeight are specified. * @memberof RectangleGraphics.prototype * @type {ConstantProperty|undefined} * @default 0 */ zIndex: createPropertyDescriptor("zIndex"), }); /** * Duplicates this instance. * * @param {RectangleGraphics} [result] The object onto which to store the result. * @returns {RectangleGraphics} The modified result parameter or a new instance if one was not provided. */ RectangleGraphics.prototype.clone = function (result) { if (!defined(result)) { return new RectangleGraphics(this); } result.show = this.show; result.coordinates = this.coordinates; result.height = this.height; result.heightReference = this.heightReference; result.extrudedHeight = this.extrudedHeight; result.extrudedHeightReference = this.extrudedHeightReference; result.rotation = this.rotation; result.stRotation = this.stRotation; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; result.classificationType = this.classificationType; result.zIndex = this.zIndex; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {RectangleGraphics} source The object to be merged into this object. */ RectangleGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.coordinates = defaultValue(this.coordinates, source.coordinates); this.height = defaultValue(this.height, source.height); this.heightReference = defaultValue( this.heightReference, source.heightReference ); this.extrudedHeight = defaultValue( this.extrudedHeight, source.extrudedHeight ); this.extrudedHeightReference = defaultValue( this.extrudedHeightReference, source.extrudedHeightReference ); this.rotation = defaultValue(this.rotation, source.rotation); this.stRotation = defaultValue(this.stRotation, source.stRotation); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); this.classificationType = defaultValue( this.classificationType, source.classificationType ); this.zIndex = defaultValue(this.zIndex, source.zIndex); }; /** * @typedef {Object} WallGraphics.ConstructorOptions * * Initialization options for the WallGraphics constructor * * @property {Property | boolean} [show=true] A boolean Property specifying the visibility of the wall. * @property {Property | Array} [positions] A Property specifying the array of {@link Cartesian3} positions which define the top of the wall. * @property {Property | Array} [minimumHeights] A Property specifying an array of heights to be used for the bottom of the wall instead of the globe surface. * @property {Property | Array} [maximumHeights] A Property specifying an array of heights to be used for the top of the wall instead of the height of each position. * @property {Property | number} [granularity=Cesium.Math.RADIANS_PER_DEGREE] A numeric Property specifying the angular distance between each latitude and longitude point. * @property {Property | boolean} [fill=true] A boolean Property specifying whether the wall is filled with the provided material. * @property {MaterialProperty | Color} [material=Color.WHITE] A Property specifying the material used to fill the wall. * @property {Property | boolean} [outline=false] A boolean Property specifying whether the wall is outlined. * @property {Property | Color} [outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @property {Property | number} [outlineWidth=1.0] A numeric Property specifying the width of the outline. * @property {Property | ShadowMode} [shadows=ShadowMode.DISABLED] An enum Property specifying whether the wall casts or receives shadows from light sources. * @property {Property | DistanceDisplayCondition} [distanceDisplayCondition] A Property specifying at what distance from the camera that this wall will be displayed. */ /** * Describes a two dimensional wall defined as a line strip and optional maximum and minimum heights. * The wall conforms to the curvature of the globe and can be placed along the surface or at altitude. * * @alias WallGraphics * @constructor * * @param {WallGraphics.ConstructorOptions} [options] Object describing initialization options * * @see Entity * @demo {@link https://sandcastle.cesium.com/index.html?src=Wall.html|Cesium Sandcastle Wall Demo} */ function WallGraphics(options) { this._definitionChanged = new Event(); this._show = undefined; this._showSubscription = undefined; this._positions = undefined; this._positionsSubscription = undefined; this._minimumHeights = undefined; this._minimumHeightsSubscription = undefined; this._maximumHeights = undefined; this._maximumHeightsSubscription = undefined; this._granularity = undefined; this._granularitySubscription = undefined; this._fill = undefined; this._fillSubscription = undefined; this._material = undefined; this._materialSubscription = undefined; this._outline = undefined; this._outlineSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this._shadows = undefined; this._shadowsSubscription = undefined; this._distanceDisplayCondition = undefined; this._distanceDisplayConditionSubscription = undefined; this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT)); } Object.defineProperties(WallGraphics.prototype, { /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof WallGraphics.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the boolean Property specifying the visibility of the wall. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default true */ show: createPropertyDescriptor("show"), /** * Gets or sets the Property specifying the array of {@link Cartesian3} positions which define the top of the wall. * @memberof WallGraphics.prototype * @type {Property|undefined} */ positions: createPropertyDescriptor("positions"), /** * Gets or sets the Property specifying an array of heights to be used for the bottom of the wall instead of the surface of the globe. * If defined, the array must be the same length as {@link Wall#positions}. * @memberof WallGraphics.prototype * @type {Property|undefined} */ minimumHeights: createPropertyDescriptor("minimumHeights"), /** * Gets or sets the Property specifying an array of heights to be used for the top of the wall instead of the height of each position. * If defined, the array must be the same length as {@link Wall#positions}. * @memberof WallGraphics.prototype * @type {Property|undefined} */ maximumHeights: createPropertyDescriptor("maximumHeights"), /** * Gets or sets the numeric Property specifying the angular distance between points on the wall. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default {CesiumMath.RADIANS_PER_DEGREE} */ granularity: createPropertyDescriptor("granularity"), /** * Gets or sets the boolean Property specifying whether the wall is filled with the provided material. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default true */ fill: createPropertyDescriptor("fill"), /** * Gets or sets the Property specifying the material used to fill the wall. * @memberof WallGraphics.prototype * @type {MaterialProperty} * @default Color.WHITE */ material: createMaterialPropertyDescriptor("material"), /** * Gets or sets the Property specifying whether the wall is outlined. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default false */ outline: createPropertyDescriptor("outline"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), /** * Get or sets the enum Property specifying whether the wall * casts or receives shadows from light sources. * @memberof WallGraphics.prototype * @type {Property|undefined} * @default ShadowMode.DISABLED */ shadows: createPropertyDescriptor("shadows"), /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this wall will be displayed. * @memberof WallGraphics.prototype * @type {Property|undefined} */ distanceDisplayCondition: createPropertyDescriptor( "distanceDisplayCondition" ), }); /** * Duplicates this instance. * * @param {WallGraphics} [result] The object onto which to store the result. * @returns {WallGraphics} The modified result parameter or a new instance if one was not provided. */ WallGraphics.prototype.clone = function (result) { if (!defined(result)) { return new WallGraphics(this); } result.show = this.show; result.positions = this.positions; result.minimumHeights = this.minimumHeights; result.maximumHeights = this.maximumHeights; result.granularity = this.granularity; result.fill = this.fill; result.material = this.material; result.outline = this.outline; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; result.shadows = this.shadows; result.distanceDisplayCondition = this.distanceDisplayCondition; return result; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {WallGraphics} source The object to be merged into this object. */ WallGraphics.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.show = defaultValue(this.show, source.show); this.positions = defaultValue(this.positions, source.positions); this.minimumHeights = defaultValue( this.minimumHeights, source.minimumHeights ); this.maximumHeights = defaultValue( this.maximumHeights, source.maximumHeights ); this.granularity = defaultValue(this.granularity, source.granularity); this.fill = defaultValue(this.fill, source.fill); this.material = defaultValue(this.material, source.material); this.outline = defaultValue(this.outline, source.outline); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); this.shadows = defaultValue(this.shadows, source.shadows); this.distanceDisplayCondition = defaultValue( this.distanceDisplayCondition, source.distanceDisplayCondition ); }; var cartoScratch$1 = new Cartographic(); function createConstantPositionProperty(value) { return new ConstantPositionProperty(value); } function createPositionPropertyDescriptor(name) { return createPropertyDescriptor( name, undefined, createConstantPositionProperty ); } function createPropertyTypeDescriptor(name, Type) { return createPropertyDescriptor(name, undefined, function (value) { if (value instanceof Type) { return value; } return new Type(value); }); } /** * @typedef {Object} Entity.ConstructorOptions * * Initialization options for the Entity constructor * * @property {String} [id] A unique identifier for this object. If none is provided, a GUID is generated. * @property {String} [name] A human readable name to display to users. It does not have to be unique. * @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object. * @property {Boolean} [show] A boolean value indicating if the entity and its children are displayed. * @property {Property | string} [description] A string Property specifying an HTML description for this entity. * @property {PositionProperty | Cartesian3} [position] A Property specifying the entity position. * @property {Property} [orientation] A Property specifying the entity orientation. * @property {Property} [viewFrom] A suggested initial offset for viewing this object. * @property {Entity} [parent] A parent entity to associate with this entity. * @property {BillboardGraphics | BillboardGraphics.ConstructorOptions} [billboard] A billboard to associate with this entity. * @property {BoxGraphics | BoxGraphics.ConstructorOptions} [box] A box to associate with this entity. * @property {CorridorGraphics | CorridorGraphics.ConstructorOptions} [corridor] A corridor to associate with this entity. * @property {CylinderGraphics | CylinderGraphics.ConstructorOptions} [cylinder] A cylinder to associate with this entity. * @property {EllipseGraphics | EllipseGraphics.ConstructorOptions} [ellipse] A ellipse to associate with this entity. * @property {EllipsoidGraphics | EllipsoidGraphics.ConstructorOptions} [ellipsoid] A ellipsoid to associate with this entity. * @property {LabelGraphics | LabelGraphics.ConstructorOptions} [label] A options.label to associate with this entity. * @property {ModelGraphics | ModelGraphics.ConstructorOptions} [model] A model to associate with this entity. * @property {Cesium3DTilesetGraphics | Cesium3DTilesetGraphics.ConstructorOptions} [tileset] A 3D Tiles tileset to associate with this entity. * @property {PathGraphics | PathGraphics.ConstructorOptions} [path] A path to associate with this entity. * @property {PlaneGraphics | PlaneGraphics.ConstructorOptions} [plane] A plane to associate with this entity. * @property {PointGraphics | PointGraphics.ConstructorOptions} [point] A point to associate with this entity. * @property {PolygonGraphics | PolygonGraphics.ConstructorOptions} [polygon] A polygon to associate with this entity. * @property {PolylineGraphics | PolylineGraphics.ConstructorOptions} [polyline] A polyline to associate with this entity. * @property {PropertyBag | Object.} [properties] Arbitrary properties to associate with this entity. * @property {PolylineVolumeGraphics | PolylineVolumeGraphics.ConstructorOptions} [polylineVolume] A polylineVolume to associate with this entity. * @property {RectangleGraphics | RectangleGraphics.ConstructorOptions} [rectangle] A rectangle to associate with this entity. * @property {WallGraphics | WallGraphics.ConstructorOptions} [wall] A wall to associate with this entity. */ /** * Entity instances aggregate multiple forms of visualization into a single high-level object. * They can be created manually and added to {@link Viewer#entities} or be produced by * data sources, such as {@link CzmlDataSource} and {@link GeoJsonDataSource}. * @alias Entity * @constructor * * @param {Entity.ConstructorOptions} [options] Object describing initialization options * * @see {@link https://cesium.com/docs/tutorials/creating-entities/|Creating Entities} */ function Entity(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var id = options.id; if (!defined(id)) { id = createGuid(); } this._availability = undefined; this._id = id; this._definitionChanged = new Event(); this._name = options.name; this._show = defaultValue(options.show, true); this._parent = undefined; this._propertyNames = [ "billboard", "box", "corridor", "cylinder", "description", "ellipse", // "ellipsoid", "label", "model", "tileset", "orientation", "path", "plane", "point", "polygon", // "polyline", "polylineVolume", "position", "properties", "rectangle", "viewFrom", "wall", ]; this._billboard = undefined; this._billboardSubscription = undefined; this._box = undefined; this._boxSubscription = undefined; this._corridor = undefined; this._corridorSubscription = undefined; this._cylinder = undefined; this._cylinderSubscription = undefined; this._description = undefined; this._descriptionSubscription = undefined; this._ellipse = undefined; this._ellipseSubscription = undefined; this._ellipsoid = undefined; this._ellipsoidSubscription = undefined; this._label = undefined; this._labelSubscription = undefined; this._model = undefined; this._modelSubscription = undefined; this._tileset = undefined; this._tilesetSubscription = undefined; this._orientation = undefined; this._orientationSubscription = undefined; this._path = undefined; this._pathSubscription = undefined; this._plane = undefined; this._planeSubscription = undefined; this._point = undefined; this._pointSubscription = undefined; this._polygon = undefined; this._polygonSubscription = undefined; this._polyline = undefined; this._polylineSubscription = undefined; this._polylineVolume = undefined; this._polylineVolumeSubscription = undefined; this._position = undefined; this._positionSubscription = undefined; this._properties = undefined; this._propertiesSubscription = undefined; this._rectangle = undefined; this._rectangleSubscription = undefined; this._viewFrom = undefined; this._viewFromSubscription = undefined; this._wall = undefined; this._wallSubscription = undefined; this._children = []; /** * Gets or sets the entity collection that this entity belongs to. * @type {EntityCollection} */ this.entityCollection = undefined; this.parent = options.parent; this.merge(options); } function updateShow(entity, children, isShowing) { var length = children.length; for (var i = 0; i < length; i++) { var child = children[i]; var childShow = child._show; var oldValue = !isShowing && childShow; var newValue = isShowing && childShow; if (oldValue !== newValue) { updateShow(child, child._children, isShowing); } } entity._definitionChanged.raiseEvent( entity, "isShowing", isShowing, !isShowing ); } Object.defineProperties(Entity.prototype, { /** * The availability, if any, associated with this object. * If availability is undefined, it is assumed that this object's * other properties will return valid data for any provided time. * If availability exists, the objects other properties will only * provide valid data if queried within the given interval. * @memberof Entity.prototype * @type {TimeIntervalCollection|undefined} */ availability: createRawPropertyDescriptor("availability"), /** * Gets the unique ID associated with this object. * @memberof Entity.prototype * @type {String} */ id: { get: function () { return this._id; }, }, /** * Gets the event that is raised whenever a property or sub-property is changed or modified. * @memberof Entity.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the name of the object. The name is intended for end-user * consumption and does not need to be unique. * @memberof Entity.prototype * @type {String|undefined} */ name: createRawPropertyDescriptor("name"), /** * Gets or sets whether this entity should be displayed. When set to true, * the entity is only displayed if the parent entity's show property is also true. * @memberof Entity.prototype * @type {Boolean} */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (value === this._show) { return; } var wasShowing = this.isShowing; this._show = value; var isShowing = this.isShowing; if (wasShowing !== isShowing) { updateShow(this, this._children, isShowing); } this._definitionChanged.raiseEvent(this, "show", value, !value); }, }, /** * Gets whether this entity is being displayed, taking into account * the visibility of any ancestor entities. * @memberof Entity.prototype * @type {Boolean} */ isShowing: { get: function () { return ( this._show && (!defined(this.entityCollection) || this.entityCollection.show) && (!defined(this._parent) || this._parent.isShowing) ); }, }, /** * Gets or sets the parent object. * @memberof Entity.prototype * @type {Entity|undefined} */ parent: { get: function () { return this._parent; }, set: function (value) { var oldValue = this._parent; if (oldValue === value) { return; } var wasShowing = this.isShowing; if (defined(oldValue)) { var index = oldValue._children.indexOf(this); oldValue._children.splice(index, 1); } this._parent = value; if (defined(value)) { value._children.push(this); } var isShowing = this.isShowing; if (wasShowing !== isShowing) { updateShow(this, this._children, isShowing); } this._definitionChanged.raiseEvent(this, "parent", value, oldValue); }, }, /** * Gets the names of all properties registered on this instance. * @memberof Entity.prototype * @type {string[]} */ propertyNames: { get: function () { return this._propertyNames; }, }, /** * Gets or sets the billboard. * @memberof Entity.prototype * @type {BillboardGraphics|undefined} */ billboard: createPropertyTypeDescriptor("billboard", BillboardGraphics), /** * Gets or sets the box. * @memberof Entity.prototype * @type {BoxGraphics|undefined} */ box: createPropertyTypeDescriptor("box", BoxGraphics), /** * Gets or sets the corridor. * @memberof Entity.prototype * @type {CorridorGraphics|undefined} */ corridor: createPropertyTypeDescriptor("corridor", CorridorGraphics), /** * Gets or sets the cylinder. * @memberof Entity.prototype * @type {CylinderGraphics|undefined} */ cylinder: createPropertyTypeDescriptor("cylinder", CylinderGraphics), /** * Gets or sets the description. * @memberof Entity.prototype * @type {Property|undefined} */ description: createPropertyDescriptor("description"), /** * Gets or sets the ellipse. * @memberof Entity.prototype * @type {EllipseGraphics|undefined} */ ellipse: createPropertyTypeDescriptor("ellipse", EllipseGraphics), /** * Gets or sets the ellipsoid. * @memberof Entity.prototype * @type {EllipsoidGraphics|undefined} */ ellipsoid: createPropertyTypeDescriptor("ellipsoid", EllipsoidGraphics), /** * Gets or sets the label. * @memberof Entity.prototype * @type {LabelGraphics|undefined} */ label: createPropertyTypeDescriptor("label", LabelGraphics), /** * Gets or sets the model. * @memberof Entity.prototype * @type {ModelGraphics|undefined} */ model: createPropertyTypeDescriptor("model", ModelGraphics), /** * Gets or sets the tileset. * @memberof Entity.prototype * @type {Cesium3DTilesetGraphics|undefined} */ tileset: createPropertyTypeDescriptor("tileset", Cesium3DTilesetGraphics), /** * Gets or sets the orientation. * @memberof Entity.prototype * @type {Property|undefined} */ orientation: createPropertyDescriptor("orientation"), /** * Gets or sets the path. * @memberof Entity.prototype * @type {PathGraphics|undefined} */ path: createPropertyTypeDescriptor("path", PathGraphics), /** * Gets or sets the plane. * @memberof Entity.prototype * @type {PlaneGraphics|undefined} */ plane: createPropertyTypeDescriptor("plane", PlaneGraphics), /** * Gets or sets the point graphic. * @memberof Entity.prototype * @type {PointGraphics|undefined} */ point: createPropertyTypeDescriptor("point", PointGraphics), /** * Gets or sets the polygon. * @memberof Entity.prototype * @type {PolygonGraphics|undefined} */ polygon: createPropertyTypeDescriptor("polygon", PolygonGraphics), /** * Gets or sets the polyline. * @memberof Entity.prototype * @type {PolylineGraphics|undefined} */ polyline: createPropertyTypeDescriptor("polyline", PolylineGraphics), /** * Gets or sets the polyline volume. * @memberof Entity.prototype * @type {PolylineVolumeGraphics|undefined} */ polylineVolume: createPropertyTypeDescriptor( "polylineVolume", PolylineVolumeGraphics ), /** * Gets or sets the bag of arbitrary properties associated with this entity. * @memberof Entity.prototype * @type {PropertyBag|undefined} */ properties: createPropertyTypeDescriptor("properties", PropertyBag), /** * Gets or sets the position. * @memberof Entity.prototype * @type {PositionProperty|undefined} */ position: createPositionPropertyDescriptor("position"), /** * Gets or sets the rectangle. * @memberof Entity.prototype * @type {RectangleGraphics|undefined} */ rectangle: createPropertyTypeDescriptor("rectangle", RectangleGraphics), /** * Gets or sets the suggested initial offset when tracking this object. * The offset is typically defined in the east-north-up reference frame, * but may be another frame depending on the object's velocity. * @memberof Entity.prototype * @type {Property|undefined} */ viewFrom: createPropertyDescriptor("viewFrom"), /** * Gets or sets the wall. * @memberof Entity.prototype * @type {WallGraphics|undefined} */ wall: createPropertyTypeDescriptor("wall", WallGraphics), }); /** * Given a time, returns true if this object should have data during that time. * * @param {JulianDate} time The time to check availability for. * @returns {Boolean} true if the object should have data during the provided time, false otherwise. */ Entity.prototype.isAvailable = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var availability = this._availability; return !defined(availability) || availability.contains(time); }; /** * Adds a property to this object. Once a property is added, it can be * observed with {@link Entity#definitionChanged} and composited * with {@link CompositeEntityCollection} * * @param {String} propertyName The name of the property to add. * * @exception {DeveloperError} "propertyName" is a reserved property name. * @exception {DeveloperError} "propertyName" is already a registered property. */ Entity.prototype.addProperty = function (propertyName) { var propertyNames = this._propertyNames; //>>includeStart('debug', pragmas.debug); if (!defined(propertyName)) { throw new DeveloperError("propertyName is required."); } if (propertyNames.indexOf(propertyName) !== -1) { throw new DeveloperError( propertyName + " is already a registered property." ); } if (propertyName in this) { throw new DeveloperError(propertyName + " is a reserved property name."); } //>>includeEnd('debug'); propertyNames.push(propertyName); Object.defineProperty( this, propertyName, createRawPropertyDescriptor(propertyName, true) ); }; /** * Removed a property previously added with addProperty. * * @param {String} propertyName The name of the property to remove. * * @exception {DeveloperError} "propertyName" is a reserved property name. * @exception {DeveloperError} "propertyName" is not a registered property. */ Entity.prototype.removeProperty = function (propertyName) { var propertyNames = this._propertyNames; var index = propertyNames.indexOf(propertyName); //>>includeStart('debug', pragmas.debug); if (!defined(propertyName)) { throw new DeveloperError("propertyName is required."); } if (index === -1) { throw new DeveloperError(propertyName + " is not a registered property."); } //>>includeEnd('debug'); this._propertyNames.splice(index, 1); delete this[propertyName]; }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {Entity} source The object to be merged into this object. */ Entity.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); //Name, show, and availability are not Property objects and are currently handled differently. //source.show is intentionally ignored because this.show always has a value. this.name = defaultValue(this.name, source.name); this.availability = defaultValue(this.availability, source.availability); var propertyNames = this._propertyNames; var sourcePropertyNames = defined(source._propertyNames) ? source._propertyNames : Object.keys(source); var propertyNamesLength = sourcePropertyNames.length; for (var i = 0; i < propertyNamesLength; i++) { var name = sourcePropertyNames[i]; //While source is required by the API to be an Entity, we internally call this method from the //constructor with an options object to configure initial custom properties. //So we need to ignore reserved-non-property. if (name === "parent" || name === "name" || name === "availability") { continue; } var targetProperty = this[name]; var sourceProperty = source[name]; //Custom properties that are registered on the source entity must also //get registered on this entity. if (!defined(targetProperty) && propertyNames.indexOf(name) === -1) { this.addProperty(name); } if (defined(sourceProperty)) { if (defined(targetProperty)) { if (defined(targetProperty.merge)) { targetProperty.merge(sourceProperty); } } else if ( defined(sourceProperty.merge) && defined(sourceProperty.clone) ) { this[name] = sourceProperty.clone(); } else { this[name] = sourceProperty; } } } }; var matrix3Scratch = new Matrix3(); var positionScratch$6 = new Cartesian3(); var orientationScratch = new Quaternion(); /** * Computes the model matrix for the entity's transform at specified time. Returns undefined if orientation or position * are undefined. * * @param {JulianDate} time The time to retrieve model matrix for. * @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. Result is undefined if position or orientation are undefined. */ Entity.prototype.computeModelMatrix = function (time, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("time", time); //>>includeEnd('debug'); var position = Property.getValueOrUndefined( this._position, time, positionScratch$6 ); if (!defined(position)) { return undefined; } var orientation = Property.getValueOrUndefined( this._orientation, time, orientationScratch ); if (!defined(orientation)) { result = Transforms.eastNorthUpToFixedFrame(position, undefined, result); } else { result = Matrix4.fromRotationTranslation( Matrix3.fromQuaternion(orientation, matrix3Scratch), position, result ); } return result; }; /** * @private */ Entity.prototype.computeModelMatrixForHeightReference = function ( time, heightReferenceProperty, heightOffset, ellipsoid, result ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("time", time); //>>includeEnd('debug'); var heightReference = Property.getValueOrDefault( heightReferenceProperty, time, HeightReference$1.NONE ); var position = Property.getValueOrUndefined( this._position, time, positionScratch$6 ); if ( heightReference === HeightReference$1.NONE || !defined(position) || Cartesian3.equalsEpsilon(position, Cartesian3.ZERO, CesiumMath.EPSILON8) ) { return this.computeModelMatrix(time, result); } var carto = ellipsoid.cartesianToCartographic(position, cartoScratch$1); if (heightReference === HeightReference$1.CLAMP_TO_GROUND) { carto.height = heightOffset; } else { carto.height += heightOffset; } position = ellipsoid.cartographicToCartesian(carto, position); var orientation = Property.getValueOrUndefined( this._orientation, time, orientationScratch ); if (!defined(orientation)) { result = Transforms.eastNorthUpToFixedFrame(position, undefined, result); } else { result = Matrix4.fromRotationTranslation( Matrix3.fromQuaternion(orientation, matrix3Scratch), position, result ); } return result; }; /** * Checks if the given Scene supports materials besides Color on Entities draped on terrain or 3D Tiles. * If this feature is not supported, Entities with non-color materials but no `height` will * instead be rendered as if height is 0. * * @param {Scene} scene The current scene. * @returns {Boolean} Whether or not the current scene supports materials for entities on terrain. */ Entity.supportsMaterialsforEntitiesOnTerrain = function (scene) { return GroundPrimitive.supportsMaterials(scene); }; /** * Checks if the given Scene supports polylines clamped to terrain or 3D Tiles. * If this feature is not supported, Entities with PolylineGraphics will be rendered with vertices at * the provided heights and using the `arcType` parameter instead of clamped to the ground. * * @param {Scene} scene The current scene. * @returns {Boolean} Whether or not the current scene supports polylines on terrain or 3D TIles. */ Entity.supportsPolylinesOnTerrain = function (scene) { return GroundPolylinePrimitive.isSupported(scene); }; var defaultMaterial$2 = new ColorMaterialProperty(Color.WHITE); var defaultShow$1 = new ConstantProperty(true); var defaultFill$1 = new ConstantProperty(true); var defaultOutline = new ConstantProperty(false); var defaultOutlineColor$3 = new ConstantProperty(Color.BLACK); var defaultShadows$2 = new ConstantProperty(ShadowMode$1.DISABLED); var defaultDistanceDisplayCondition$7 = new ConstantProperty( new DistanceDisplayCondition() ); var defaultClassificationType$1 = new ConstantProperty(ClassificationType$1.BOTH); /** * An abstract class for updating geometry entities. * @alias GeometryUpdater * @constructor * * @param {Object} options An object with the following properties: * @param {Entity} options.entity The entity containing the geometry to be visualized. * @param {Scene} options.scene The scene where visualization is taking place. * @param {Object} options.geometryOptions Options for the geometry * @param {String} options.geometryPropertyName The geometry property name * @param {String[]} options.observedPropertyNames The entity properties this geometry cares about */ function GeometryUpdater(options) { //>>includeStart('debug', pragmas.debug); Check.defined("options.entity", options.entity); Check.defined("options.scene", options.scene); Check.defined("options.geometryOptions", options.geometryOptions); Check.defined("options.geometryPropertyName", options.geometryPropertyName); Check.defined("options.observedPropertyNames", options.observedPropertyNames); //>>includeEnd('debug'); var entity = options.entity; var geometryPropertyName = options.geometryPropertyName; this._entity = entity; this._scene = options.scene; this._fillEnabled = false; this._isClosed = false; this._onTerrain = false; this._dynamic = false; this._outlineEnabled = false; this._geometryChanged = new Event(); this._showProperty = undefined; this._materialProperty = undefined; this._showOutlineProperty = undefined; this._outlineColorProperty = undefined; this._outlineWidth = 1.0; this._shadowsProperty = undefined; this._distanceDisplayConditionProperty = undefined; this._classificationTypeProperty = undefined; this._options = options.geometryOptions; this._geometryPropertyName = geometryPropertyName; this._id = geometryPropertyName + "-" + entity.id; this._observedPropertyNames = options.observedPropertyNames; this._supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain( options.scene ); } Object.defineProperties(GeometryUpdater.prototype, { /** * Gets the unique ID associated with this updater * @memberof GeometryUpdater.prototype * @type {String} * @readonly */ id: { get: function () { return this._id; }, }, /** * Gets the entity associated with this geometry. * @memberof GeometryUpdater.prototype * * @type {Entity} * @readonly */ entity: { get: function () { return this._entity; }, }, /** * Gets a value indicating if the geometry has a fill component. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ fillEnabled: { get: function () { return this._fillEnabled; }, }, /** * Gets a value indicating if fill visibility varies with simulation time. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ hasConstantFill: { get: function () { return ( !this._fillEnabled || (!defined(this._entity.availability) && Property.isConstant(this._showProperty) && Property.isConstant(this._fillProperty)) ); }, }, /** * Gets the material property used to fill the geometry. * @memberof GeometryUpdater.prototype * * @type {MaterialProperty} * @readonly */ fillMaterialProperty: { get: function () { return this._materialProperty; }, }, /** * Gets a value indicating if the geometry has an outline component. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ outlineEnabled: { get: function () { return this._outlineEnabled; }, }, /** * Gets a value indicating if the geometry has an outline component. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ hasConstantOutline: { get: function () { return ( !this._outlineEnabled || (!defined(this._entity.availability) && Property.isConstant(this._showProperty) && Property.isConstant(this._showOutlineProperty)) ); }, }, /** * Gets the {@link Color} property for the geometry outline. * @memberof GeometryUpdater.prototype * * @type {Property} * @readonly */ outlineColorProperty: { get: function () { return this._outlineColorProperty; }, }, /** * Gets the constant with of the geometry outline, in pixels. * This value is only valid if isDynamic is false. * @memberof GeometryUpdater.prototype * * @type {Number} * @readonly */ outlineWidth: { get: function () { return this._outlineWidth; }, }, /** * Gets the property specifying whether the geometry * casts or receives shadows from light sources. * @memberof GeometryUpdater.prototype * * @type {Property} * @readonly */ shadowsProperty: { get: function () { return this._shadowsProperty; }, }, /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this geometry will be displayed. * @memberof GeometryUpdater.prototype * * @type {Property} * @readonly */ distanceDisplayConditionProperty: { get: function () { return this._distanceDisplayConditionProperty; }, }, /** * Gets or sets the {@link ClassificationType} Property specifying if this geometry will classify terrain, 3D Tiles, or both when on the ground. * @memberof GeometryUpdater.prototype * * @type {Property} * @readonly */ classificationTypeProperty: { get: function () { return this._classificationTypeProperty; }, }, /** * Gets a value indicating if the geometry is time-varying. * If true, all visualization is delegated to a DynamicGeometryUpdater * returned by GeometryUpdater#createDynamicUpdater. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ isDynamic: { get: function () { return this._dynamic; }, }, /** * Gets a value indicating if the geometry is closed. * This property is only valid for static geometry. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ isClosed: { get: function () { return this._isClosed; }, }, /** * Gets a value indicating if the geometry should be drawn on terrain. * @memberof EllipseGeometryUpdater.prototype * * @type {Boolean} * @readonly */ onTerrain: { get: function () { return this._onTerrain; }, }, /** * Gets an event that is raised whenever the public properties * of this updater change. * @memberof GeometryUpdater.prototype * * @type {Boolean} * @readonly */ geometryChanged: { get: function () { return this._geometryChanged; }, }, }); /** * Checks if the geometry is outlined at the provided time. * * @param {JulianDate} time The time for which to retrieve visibility. * @returns {Boolean} true if geometry is outlined at the provided time, false otherwise. */ GeometryUpdater.prototype.isOutlineVisible = function (time) { var entity = this._entity; var visible = this._outlineEnabled && entity.isAvailable(time) && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time); return defaultValue(visible, false); }; /** * Checks if the geometry is filled at the provided time. * * @param {JulianDate} time The time for which to retrieve visibility. * @returns {Boolean} true if geometry is filled at the provided time, false otherwise. */ GeometryUpdater.prototype.isFilled = function (time) { var entity = this._entity; var visible = this._fillEnabled && entity.isAvailable(time) && this._showProperty.getValue(time) && this._fillProperty.getValue(time); return defaultValue(visible, false); }; /** * Creates the geometry instance which represents the fill of the geometry. * * @function * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ GeometryUpdater.prototype.createFillGeometryInstance = DeveloperError.throwInstantiationError; /** * Creates the geometry instance which represents the outline of the geometry. * * @function * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ GeometryUpdater.prototype.createOutlineGeometryInstance = DeveloperError.throwInstantiationError; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ GeometryUpdater.prototype.isDestroyed = function () { return false; }; /** * Destroys and resources used by the object. Once an object is destroyed, it should not be used. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ GeometryUpdater.prototype.destroy = function () { destroyObject(this); }; /** * @param {Entity} entity * @param {Object} geometry * @private */ GeometryUpdater.prototype._isHidden = function (entity, geometry) { var show = geometry.show; return ( defined(show) && show.isConstant && !show.getValue(Iso8601.MINIMUM_VALUE) ); }; /** * @param {Entity} entity * @param {Object} geometry * @private */ GeometryUpdater.prototype._isOnTerrain = function (entity, geometry) { return false; }; /** * @param {GeometryOptions} options * @private */ GeometryUpdater.prototype._getIsClosed = function (options) { return true; }; /** * @param {Entity} entity * @param {Object} geometry * @private */ GeometryUpdater.prototype._isDynamic = DeveloperError.throwInstantiationError; /** * @param {Entity} entity * @param {Object} geometry * @private */ GeometryUpdater.prototype._setStaticOptions = DeveloperError.throwInstantiationError; /** * @param {Entity} entity * @param {String} propertyName * @param {*} newValue * @param {*} oldValue * @private */ GeometryUpdater.prototype._onEntityPropertyChanged = function ( entity, propertyName, newValue, oldValue ) { if (this._observedPropertyNames.indexOf(propertyName) === -1) { return; } var geometry = this._entity[this._geometryPropertyName]; if (!defined(geometry)) { if (this._fillEnabled || this._outlineEnabled) { this._fillEnabled = false; this._outlineEnabled = false; this._geometryChanged.raiseEvent(this); } return; } var fillProperty = geometry.fill; var fillEnabled = defined(fillProperty) && fillProperty.isConstant ? fillProperty.getValue(Iso8601.MINIMUM_VALUE) : true; var outlineProperty = geometry.outline; var outlineEnabled = defined(outlineProperty); if (outlineEnabled && outlineProperty.isConstant) { outlineEnabled = outlineProperty.getValue(Iso8601.MINIMUM_VALUE); } if (!fillEnabled && !outlineEnabled) { if (this._fillEnabled || this._outlineEnabled) { this._fillEnabled = false; this._outlineEnabled = false; this._geometryChanged.raiseEvent(this); } return; } var show = geometry.show; if (this._isHidden(entity, geometry)) { if (this._fillEnabled || this._outlineEnabled) { this._fillEnabled = false; this._outlineEnabled = false; this._geometryChanged.raiseEvent(this); } return; } this._materialProperty = defaultValue(geometry.material, defaultMaterial$2); this._fillProperty = defaultValue(fillProperty, defaultFill$1); this._showProperty = defaultValue(show, defaultShow$1); this._showOutlineProperty = defaultValue(geometry.outline, defaultOutline); this._outlineColorProperty = outlineEnabled ? defaultValue(geometry.outlineColor, defaultOutlineColor$3) : undefined; this._shadowsProperty = defaultValue(geometry.shadows, defaultShadows$2); this._distanceDisplayConditionProperty = defaultValue( geometry.distanceDisplayCondition, defaultDistanceDisplayCondition$7 ); this._classificationTypeProperty = defaultValue( geometry.classificationType, defaultClassificationType$1 ); this._fillEnabled = fillEnabled; var onTerrain = this._isOnTerrain(entity, geometry) && (this._supportsMaterialsforEntitiesOnTerrain || this._materialProperty instanceof ColorMaterialProperty); if (outlineEnabled && onTerrain) { oneTimeWarning(oneTimeWarning.geometryOutlines); outlineEnabled = false; } this._onTerrain = onTerrain; this._outlineEnabled = outlineEnabled; if (this._isDynamic(entity, geometry)) { if (!this._dynamic) { this._dynamic = true; this._geometryChanged.raiseEvent(this); } } else { this._setStaticOptions(entity, geometry); this._isClosed = this._getIsClosed(this._options); var outlineWidth = geometry.outlineWidth; this._outlineWidth = defined(outlineWidth) ? outlineWidth.getValue(Iso8601.MINIMUM_VALUE) : 1.0; this._dynamic = false; this._geometryChanged.raiseEvent(this); } }; /** * Creates the dynamic updater to be used when GeometryUpdater#isDynamic is true. * * @param {PrimitiveCollection} primitives The primitive collection to use. * @param {PrimitiveCollection} [groundPrimitives] The primitive collection to use for ground primitives. * * @returns {DynamicGeometryUpdater} The dynamic updater used to update the geometry each frame. * * @exception {DeveloperError} This instance does not represent dynamic geometry. * @private */ GeometryUpdater.prototype.createDynamicUpdater = function ( primitives, groundPrimitives ) { //>>includeStart('debug', pragmas.debug); Check.defined("primitives", primitives); Check.defined("groundPrimitives", groundPrimitives); if (!this._dynamic) { throw new DeveloperError( "This instance does not represent dynamic geometry." ); } //>>includeEnd('debug'); return new this.constructor.DynamicGeometryUpdater( this, primitives, groundPrimitives ); }; /** * A {@link Property} whose value is lazily evaluated by a callback function. * * @alias CallbackProperty * @constructor * * @param {CallbackProperty.Callback} callback The function to be called when the property is evaluated. * @param {Boolean} isConstant true when the callback function returns the same value every time, false if the value will change. */ function CallbackProperty(callback, isConstant) { this._callback = undefined; this._isConstant = undefined; this._definitionChanged = new Event(); this.setCallback(callback, isConstant); } Object.defineProperties(CallbackProperty.prototype, { /** * Gets a value indicating if this property is constant. * @memberof CallbackProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._isConstant; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setCallback is called. * @memberof CallbackProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, }); /** * Gets the value of the property. * * @param {JulianDate} [time] The time for which to retrieve the value. This parameter is unused since the value does not change with respect to time. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied or is unsupported. */ CallbackProperty.prototype.getValue = function (time, result) { return this._callback(time, result); }; /** * Sets the callback to be used. * * @param {CallbackProperty.Callback} callback The function to be called when the property is evaluated. * @param {Boolean} isConstant true when the callback function returns the same value every time, false if the value will change. */ CallbackProperty.prototype.setCallback = function (callback, isConstant) { //>>includeStart('debug', pragmas.debug); if (!defined(callback)) { throw new DeveloperError("callback is required."); } if (!defined(isConstant)) { throw new DeveloperError("isConstant is required."); } //>>includeEnd('debug'); var changed = this._callback !== callback || this._isConstant !== isConstant; this._callback = callback; this._isConstant = isConstant; if (changed) { this._definitionChanged.raiseEvent(this); } }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ CallbackProperty.prototype.equals = function (other) { return ( this === other || (other instanceof CallbackProperty && this._callback === other._callback && this._isConstant === other._isConstant) ); }; var scratchPosition$7 = new Cartesian3(); var scratchCarto$1 = new Cartographic(); /** * @private */ function TerrainOffsetProperty( scene, positionProperty, heightReferenceProperty, extrudedHeightReferenceProperty ) { //>>includeStart('debug', pragmas.debug); Check.defined("scene", scene); Check.defined("positionProperty", positionProperty); //>>includeEnd('debug'); this._scene = scene; this._heightReference = heightReferenceProperty; this._extrudedHeightReference = extrudedHeightReferenceProperty; this._positionProperty = positionProperty; this._position = new Cartesian3(); this._cartographicPosition = new Cartographic(); this._normal = new Cartesian3(); this._definitionChanged = new Event(); this._terrainHeight = 0; this._removeCallbackFunc = undefined; this._removeEventListener = undefined; this._removeModeListener = undefined; var that = this; if (defined(scene.globe)) { this._removeEventListener = scene.terrainProviderChanged.addEventListener( function () { that._updateClamping(); } ); this._removeModeListener = scene.morphComplete.addEventListener( function () { that._updateClamping(); } ); } if (positionProperty.isConstant) { var position = positionProperty.getValue( Iso8601.MINIMUM_VALUE, scratchPosition$7 ); if ( !defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe) ) { return; } this._position = Cartesian3.clone(position, this._position); this._updateClamping(); this._normal = scene.globe.ellipsoid.geodeticSurfaceNormal( position, this._normal ); } } Object.defineProperties(TerrainOffsetProperty.prototype, { /** * Gets a value indicating if this property is constant. * @memberof TerrainOffsetProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return false; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * @memberof TerrainOffsetProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, }); /** * @private */ TerrainOffsetProperty.prototype._updateClamping = function () { if (defined(this._removeCallbackFunc)) { this._removeCallbackFunc(); } var scene = this._scene; var globe = scene.globe; var position = this._position; if (!defined(globe) || Cartesian3.equals(position, Cartesian3.ZERO)) { this._terrainHeight = 0; return; } var ellipsoid = globe.ellipsoid; var surface = globe._surface; var that = this; var cartographicPosition = ellipsoid.cartesianToCartographic( position, this._cartographicPosition ); var height = globe.getHeight(cartographicPosition); if (defined(height)) { this._terrainHeight = height; } else { this._terrainHeight = 0; } function updateFunction(clampedPosition) { if (scene.mode === SceneMode$1.SCENE3D) { var carto = ellipsoid.cartesianToCartographic( clampedPosition, scratchCarto$1 ); that._terrainHeight = carto.height; } else { that._terrainHeight = clampedPosition.x; } that.definitionChanged.raiseEvent(); } this._removeCallbackFunc = surface.updateHeight( cartographicPosition, updateFunction ); }; /** * Gets the height relative to the terrain based on the positions. * * @returns {Cartesian3} The offset */ TerrainOffsetProperty.prototype.getValue = function (time, result) { var heightReference = Property.getValueOrDefault( this._heightReference, time, HeightReference$1.NONE ); var extrudedHeightReference = Property.getValueOrDefault( this._extrudedHeightReference, time, HeightReference$1.NONE ); if ( heightReference === HeightReference$1.NONE && extrudedHeightReference !== HeightReference$1.RELATIVE_TO_GROUND ) { this._position = Cartesian3.clone(Cartesian3.ZERO, this._position); return Cartesian3.clone(Cartesian3.ZERO, result); } if (this._positionProperty.isConstant) { return Cartesian3.multiplyByScalar( this._normal, this._terrainHeight, result ); } var scene = this._scene; var position = this._positionProperty.getValue(time, scratchPosition$7); if ( !defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe) ) { return Cartesian3.clone(Cartesian3.ZERO, result); } if ( Cartesian3.equalsEpsilon(this._position, position, CesiumMath.EPSILON10) ) { return Cartesian3.multiplyByScalar( this._normal, this._terrainHeight, result ); } this._position = Cartesian3.clone(position, this._position); this._updateClamping(); var normal = scene.globe.ellipsoid.geodeticSurfaceNormal( position, this._normal ); return Cartesian3.multiplyByScalar(normal, this._terrainHeight, result); }; TerrainOffsetProperty.prototype.isDestroyed = function () { return false; }; TerrainOffsetProperty.prototype.destroy = function () { if (defined(this._removeEventListener)) { this._removeEventListener(); } if (defined(this._removeModeListener)) { this._removeModeListener(); } if (defined(this._removeCallbackFunc)) { this._removeCallbackFunc(); } return destroyObject(this); }; function heightReferenceOnEntityPropertyChanged( entity, propertyName, newValue, oldValue ) { GeometryUpdater.prototype._onEntityPropertyChanged.call( this, entity, propertyName, newValue, oldValue ); if (this._observedPropertyNames.indexOf(propertyName) === -1) { return; } var geometry = this._entity[this._geometryPropertyName]; if (!defined(geometry)) { return; } if (defined(this._terrainOffsetProperty)) { this._terrainOffsetProperty.destroy(); this._terrainOffsetProperty = undefined; } var heightReferenceProperty = geometry.heightReference; if (defined(heightReferenceProperty)) { var centerPosition = new CallbackProperty( this._computeCenter.bind(this), !this._dynamic ); this._terrainOffsetProperty = new TerrainOffsetProperty( this._scene, centerPosition, heightReferenceProperty ); } } var defaultOffset$a = Cartesian3.ZERO; var offsetScratch$9 = new Cartesian3(); var positionScratch$5 = new Cartesian3(); var scratchColor$m = new Color(); function BoxGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.dimensions = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for boxes. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias BoxGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function BoxGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new BoxGeometryOptions(entity), geometryPropertyName: "box", observedPropertyNames: ["availability", "position", "orientation", "box"], }); this._onEntityPropertyChanged(entity, "box", entity.box, undefined); } if (defined(Object.create)) { BoxGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); BoxGeometryUpdater.prototype.constructor = BoxGeometryUpdater; } Object.defineProperties(BoxGeometryUpdater.prototype, { /** * Gets the terrain offset property * @type {TerrainOffsetProperty} * @memberof BoxGeometryUpdater.prototype * @readonly * @private */ terrainOffsetProperty: { get: function () { return this._terrainOffsetProperty; }, }, }); /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ BoxGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); var attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: undefined, offset: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$m); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$a, offsetScratch$9 ) ); } return new GeometryInstance({ id: entity, geometry: BoxGeometry.fromDimensions(this._options), modelMatrix: entity.computeModelMatrixForHeightReference( time, entity.box.heightReference, this._options.dimensions.z * 0.5, this._scene.mapProjection.ellipsoid ), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ BoxGeometryUpdater.prototype.createOutlineGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$m ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$a, offsetScratch$9 ) ); } return new GeometryInstance({ id: entity, geometry: BoxOutlineGeometry.fromDimensions(this._options), modelMatrix: entity.computeModelMatrixForHeightReference( time, entity.box.heightReference, this._options.dimensions.z * 0.5, this._scene.mapProjection.ellipsoid ), attributes: attributes, }); }; BoxGeometryUpdater.prototype._computeCenter = function (time, result) { return Property.getValueOrUndefined(this._entity.position, time, result); }; BoxGeometryUpdater.prototype._isHidden = function (entity, box) { return ( !defined(box.dimensions) || !defined(entity.position) || GeometryUpdater.prototype._isHidden.call(this, entity, box) ); }; BoxGeometryUpdater.prototype._isDynamic = function (entity, box) { return ( !entity.position.isConstant || !Property.isConstant(entity.orientation) || !box.dimensions.isConstant || !Property.isConstant(box.outlineWidth) ); }; BoxGeometryUpdater.prototype._setStaticOptions = function (entity, box) { var heightReference = Property.getValueOrDefault( box.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var options = this._options; options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.dimensions = box.dimensions.getValue( Iso8601.MINIMUM_VALUE, options.dimensions ); options.offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; }; BoxGeometryUpdater.prototype._onEntityPropertyChanged = heightReferenceOnEntityPropertyChanged; BoxGeometryUpdater.DynamicGeometryUpdater = DynamicBoxGeometryUpdater; /** * @private */ function DynamicBoxGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicBoxGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicBoxGeometryUpdater.prototype.constructor = DynamicBoxGeometryUpdater; } DynamicBoxGeometryUpdater.prototype._isHidden = function (entity, box, time) { var position = Property.getValueOrUndefined( entity.position, time, positionScratch$5 ); var dimensions = this._options.dimensions; return ( !defined(position) || !defined(dimensions) || DynamicGeometryUpdater$1.prototype._isHidden.call(this, entity, box, time) ); }; DynamicBoxGeometryUpdater.prototype._setOptions = function (entity, box, time) { var heightReference = Property.getValueOrDefault( box.heightReference, time, HeightReference$1.NONE ); var options = this._options; options.dimensions = Property.getValueOrUndefined( box.dimensions, time, options.dimensions ); options.offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; }; /** * Represents a command to the renderer for clearing a framebuffer. * * @private * @constructor */ function ClearCommand(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The value to clear the color buffer to. When undefined, the color buffer is not cleared. * * @type {Color} * * @default undefined */ this.color = options.color; /** * The value to clear the depth buffer to. When undefined, the depth buffer is not cleared. * * @type {Number} * * @default undefined */ this.depth = options.depth; /** * The value to clear the stencil buffer to. When undefined, the stencil buffer is not cleared. * * @type {Number} * * @default undefined */ this.stencil = options.stencil; /** * The render state to apply when executing the clear command. The following states affect clearing: * scissor test, color mask, depth mask, and stencil mask. When the render state is * undefined, the default render state is used. * * @type {RenderState} * * @default undefined */ this.renderState = options.renderState; /** * The framebuffer to clear. * * @type {Framebuffer} * * @default undefined */ this.framebuffer = options.framebuffer; /** * The object who created this command. This is useful for debugging command * execution; it allows you to see who created a command when you only have a * reference to the command, and can be used to selectively execute commands * with {@link Scene#debugCommandFilter}. * * @type {Object} * * @default undefined * * @see Scene#debugCommandFilter */ this.owner = options.owner; /** * The pass in which to run this command. * * @type {Pass} * * @default undefined */ this.pass = options.pass; } /** * Clears color to (0.0, 0.0, 0.0, 0.0); depth to 1.0; and stencil to 0. * * @type {ClearCommand} * * @constant */ ClearCommand.ALL = Object.freeze( new ClearCommand({ color: new Color(0.0, 0.0, 0.0, 0.0), depth: 1.0, stencil: 0.0, }) ); ClearCommand.prototype.execute = function (context, passState) { context.clear(this, passState); }; /** * An enum describing the x, y, and z axes and helper conversion functions. * * @enum {Number} */ var Axis = { /** * Denotes the x-axis. * * @type {Number} * @constant */ X: 0, /** * Denotes the y-axis. * * @type {Number} * @constant */ Y: 1, /** * Denotes the z-axis. * * @type {Number} * @constant */ Z: 2, }; /** * Matrix used to convert from y-up to z-up * * @type {Matrix4} * @constant */ Axis.Y_UP_TO_Z_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationX(CesiumMath.PI_OVER_TWO) ); /** * Matrix used to convert from z-up to y-up * * @type {Matrix4} * @constant */ Axis.Z_UP_TO_Y_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationX(-CesiumMath.PI_OVER_TWO) ); /** * Matrix used to convert from x-up to z-up * * @type {Matrix4} * @constant */ Axis.X_UP_TO_Z_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationY(-CesiumMath.PI_OVER_TWO) ); /** * Matrix used to convert from z-up to x-up * * @type {Matrix4} * @constant */ Axis.Z_UP_TO_X_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationY(CesiumMath.PI_OVER_TWO) ); /** * Matrix used to convert from x-up to y-up * * @type {Matrix4} * @constant */ Axis.X_UP_TO_Y_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationZ(CesiumMath.PI_OVER_TWO) ); /** * Matrix used to convert from y-up to x-up * * @type {Matrix4} * @constant */ Axis.Y_UP_TO_X_UP = Matrix4.fromRotationTranslation( Matrix3.fromRotationZ(-CesiumMath.PI_OVER_TWO) ); /** * Gets the axis by name * * @param {String} name The name of the axis. * @returns {Number} The axis enum. */ Axis.fromName = function (name) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("name", name); //>>includeEnd('debug'); return Axis[name]; }; var Axis$1 = Object.freeze(Axis); /** * An enum describing the attribute type for glTF and 3D Tiles. * * @enum {String} * * @private */ var AttributeType = { /** * The attribute is a single component. * * @type {String} * @constant */ SCALAR: "SCALAR", /** * The attribute is a two-component vector. * * @type {String} * @constant */ VEC2: "VEC2", /** * The attribute is a three-component vector. * * @type {String} * @constant */ VEC3: "VEC3", /** * The attribute is a four-component vector. * * @type {String} * @constant */ VEC4: "VEC4", /** * The attribute is a 2x2 matrix. * * @type {String} * @constant */ MAT2: "MAT2", /** * The attribute is a 3x3 matrix. * * @type {String} * @constant */ MAT3: "MAT3", /** * The attribute is a 4x4 matrix. * * @type {String} * @constant */ MAT4: "MAT4", }; var AttributeType$1 = Object.freeze(AttributeType); /** * Defines how per-feature colors set from the Cesium API or declarative styling blend with the source colors from * the original feature, e.g. glTF material or per-point color in the tile. *

* When REPLACE or MIX are used and the source color is a glTF material, the technique must assign the * _3DTILESDIFFUSE semantic to the diffuse color parameter. Otherwise only HIGHLIGHT is supported. *

*

* A feature whose color evaluates to white (1.0, 1.0, 1.0) is always rendered without color blending, regardless of the * tileset's color blend mode. *

*

	 * "techniques": {
	 *   "technique0": {
	 *     "parameters": {
	 *       "diffuse": {
	 *         "semantic": "_3DTILESDIFFUSE",
	 *         "type": 35666
	 *       }
	 *     }
	 *   }
	 * }
	 * 
* * @enum {Number} */ var Cesium3DTileColorBlendMode = { /** * Multiplies the source color by the feature color. * * @type {Number} * @constant */ HIGHLIGHT: 0, /** * Replaces the source color with the feature color. * * @type {Number} * @constant */ REPLACE: 1, /** * Blends the source color and feature color together. * * @type {Number} * @constant */ MIX: 2, }; var Cesium3DTileColorBlendMode$1 = Object.freeze(Cesium3DTileColorBlendMode); var ComponentsPerAttribute = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16, }; var ClassPerType = { SCALAR: undefined, VEC2: Cartesian2, VEC3: Cartesian3, VEC4: Cartesian4, MAT2: Matrix2, MAT3: Matrix3, MAT4: Matrix4, }; /** * @private */ function getBinaryAccessor(accessor) { var componentType = accessor.componentType; var componentDatatype; if (typeof componentType === "string") { componentDatatype = ComponentDatatype$1.fromName(componentType); } else { componentDatatype = componentType; } var componentsPerAttribute = ComponentsPerAttribute[accessor.type]; var classType = ClassPerType[accessor.type]; return { componentsPerAttribute: componentsPerAttribute, classType: classType, createArrayBufferView: function (buffer, byteOffset, length) { return ComponentDatatype$1.createArrayBufferView( componentDatatype, buffer, byteOffset, componentsPerAttribute * length ); }, }; } var DEFAULT_COLOR_VALUE$2 = Color.WHITE; var DEFAULT_SHOW_VALUE$2 = true; /** * @private * @constructor */ function Cesium3DTileBatchTable( content, featuresLength, batchTableJson, batchTableBinary, colorChangedCallback ) { /** * @readonly */ this.featuresLength = featuresLength; this._translucentFeaturesLength = 0; // Number of features in the tile that are translucent var extensions; if (defined(batchTableJson)) { extensions = batchTableJson.extensions; } this._extensions = defaultValue(extensions, {}); var properties = initializeProperties(batchTableJson); this._properties = properties; this._batchTableHierarchy = initializeHierarchy( this, batchTableJson, batchTableBinary ); this._batchTableBinaryProperties = getBinaryProperties( featuresLength, properties, batchTableBinary ); // PERFORMANCE_IDEA: These parallel arrays probably generate cache misses in get/set color/show // and use A LOT of memory. How can we use less memory? this._showAlphaProperties = undefined; // [Show (0 or 255), Alpha (0 to 255)] property for each feature this._batchValues = undefined; // Per-feature RGBA (A is based on the color's alpha and feature's show property) this._batchValuesDirty = false; this._batchTexture = undefined; this._defaultTexture = undefined; this._pickTexture = undefined; this._pickIds = []; this._content = content; this._colorChangedCallback = colorChangedCallback; // Dimensions for batch and pick textures var textureDimensions; var textureStep; if (featuresLength > 0) { // PERFORMANCE_IDEA: this can waste memory in the last row in the uncommon case // when more than one row is needed (e.g., > 16K features in one tile) var width = Math.min(featuresLength, ContextLimits.maximumTextureSize); var height = Math.ceil(featuresLength / ContextLimits.maximumTextureSize); var stepX = 1.0 / width; var centerX = stepX * 0.5; var stepY = 1.0 / height; var centerY = stepY * 0.5; textureDimensions = new Cartesian2(width, height); textureStep = new Cartesian4(stepX, centerX, stepY, centerY); } this._textureDimensions = textureDimensions; this._textureStep = textureStep; } // This can be overridden for testing purposes Cesium3DTileBatchTable._deprecationWarning = deprecationWarning; Object.defineProperties(Cesium3DTileBatchTable.prototype, { memorySizeInBytes: { get: function () { var memory = 0; if (defined(this._pickTexture)) { memory += this._pickTexture.sizeInBytes; } if (defined(this._batchTexture)) { memory += this._batchTexture.sizeInBytes; } return memory; }, }, }); function initializeProperties(jsonHeader) { var properties = {}; if (!defined(jsonHeader)) { return properties; } for (var propertyName in jsonHeader) { if ( jsonHeader.hasOwnProperty(propertyName) && propertyName !== "HIERARCHY" && // Deprecated HIERARCHY property propertyName !== "extensions" && propertyName !== "extras" ) { properties[propertyName] = clone$1(jsonHeader[propertyName], true); } } return properties; } function initializeHierarchy(batchTable, jsonHeader, binaryBody) { if (!defined(jsonHeader)) { return; } var hierarchy = batchTable._extensions["3DTILES_batch_table_hierarchy"]; var legacyHierarchy = jsonHeader.HIERARCHY; if (defined(legacyHierarchy)) { Cesium3DTileBatchTable._deprecationWarning( "batchTableHierarchyExtension", "The batch table HIERARCHY property has been moved to an extension. Use extensions.3DTILES_batch_table_hierarchy instead." ); batchTable._extensions["3DTILES_batch_table_hierarchy"] = legacyHierarchy; hierarchy = legacyHierarchy; } if (!defined(hierarchy)) { return; } return initializeHierarchyValues(hierarchy, binaryBody); } function initializeHierarchyValues(hierarchyJson, binaryBody) { var i; var classId; var binaryAccessor; var instancesLength = hierarchyJson.instancesLength; var classes = hierarchyJson.classes; var classIds = hierarchyJson.classIds; var parentCounts = hierarchyJson.parentCounts; var parentIds = hierarchyJson.parentIds; var parentIdsLength = instancesLength; if (defined(classIds.byteOffset)) { classIds.componentType = defaultValue( classIds.componentType, ComponentDatatype$1.UNSIGNED_SHORT ); classIds.type = AttributeType$1.SCALAR; binaryAccessor = getBinaryAccessor(classIds); classIds = binaryAccessor.createArrayBufferView( binaryBody.buffer, binaryBody.byteOffset + classIds.byteOffset, instancesLength ); } var parentIndexes; if (defined(parentCounts)) { if (defined(parentCounts.byteOffset)) { parentCounts.componentType = defaultValue( parentCounts.componentType, ComponentDatatype$1.UNSIGNED_SHORT ); parentCounts.type = AttributeType$1.SCALAR; binaryAccessor = getBinaryAccessor(parentCounts); parentCounts = binaryAccessor.createArrayBufferView( binaryBody.buffer, binaryBody.byteOffset + parentCounts.byteOffset, instancesLength ); } parentIndexes = new Uint16Array(instancesLength); parentIdsLength = 0; for (i = 0; i < instancesLength; ++i) { parentIndexes[i] = parentIdsLength; parentIdsLength += parentCounts[i]; } } if (defined(parentIds) && defined(parentIds.byteOffset)) { parentIds.componentType = defaultValue( parentIds.componentType, ComponentDatatype$1.UNSIGNED_SHORT ); parentIds.type = AttributeType$1.SCALAR; binaryAccessor = getBinaryAccessor(parentIds); parentIds = binaryAccessor.createArrayBufferView( binaryBody.buffer, binaryBody.byteOffset + parentIds.byteOffset, parentIdsLength ); } var classesLength = classes.length; for (i = 0; i < classesLength; ++i) { var classInstancesLength = classes[i].length; var properties = classes[i].instances; var binaryProperties = getBinaryProperties( classInstancesLength, properties, binaryBody ); classes[i].instances = combine$2(binaryProperties, properties); } var classCounts = arrayFill(new Array(classesLength), 0); var classIndexes = new Uint16Array(instancesLength); for (i = 0; i < instancesLength; ++i) { classId = classIds[i]; classIndexes[i] = classCounts[classId]; ++classCounts[classId]; } var hierarchy = { classes: classes, classIds: classIds, classIndexes: classIndexes, parentCounts: parentCounts, parentIndexes: parentIndexes, parentIds: parentIds, }; //>>includeStart('debug', pragmas.debug); validateHierarchy(hierarchy); //>>includeEnd('debug'); return hierarchy; } //>>includeStart('debug', pragmas.debug); var scratchValidateStack = []; function validateHierarchy(hierarchy) { var stack = scratchValidateStack; stack.length = 0; var classIds = hierarchy.classIds; var instancesLength = classIds.length; for (var i = 0; i < instancesLength; ++i) { validateInstance(hierarchy, i, stack); } } function validateInstance(hierarchy, instanceIndex, stack) { var parentCounts = hierarchy.parentCounts; var parentIds = hierarchy.parentIds; var parentIndexes = hierarchy.parentIndexes; var classIds = hierarchy.classIds; var instancesLength = classIds.length; if (!defined(parentIds)) { // No need to validate if there are no parents return; } if (instanceIndex >= instancesLength) { throw new DeveloperError( "Parent index " + instanceIndex + " exceeds the total number of instances: " + instancesLength ); } if (stack.indexOf(instanceIndex) > -1) { throw new DeveloperError( "Circular dependency detected in the batch table hierarchy." ); } stack.push(instanceIndex); var parentCount = defined(parentCounts) ? parentCounts[instanceIndex] : 1; var parentIndex = defined(parentCounts) ? parentIndexes[instanceIndex] : instanceIndex; for (var i = 0; i < parentCount; ++i) { var parentId = parentIds[parentIndex + i]; // Stop the traversal when the instance has no parent (its parentId equals itself), else continue the traversal. if (parentId !== instanceIndex) { validateInstance(hierarchy, parentId, stack); } } stack.pop(instanceIndex); } //>>includeEnd('debug'); function getBinaryProperties(featuresLength, properties, binaryBody) { var binaryProperties; for (var name in properties) { if (properties.hasOwnProperty(name)) { var property = properties[name]; var byteOffset = property.byteOffset; if (defined(byteOffset)) { // This is a binary property var componentType = property.componentType; var type = property.type; if (!defined(componentType)) { throw new RuntimeError("componentType is required."); } if (!defined(type)) { throw new RuntimeError("type is required."); } if (!defined(binaryBody)) { throw new RuntimeError( "Property " + name + " requires a batch table binary." ); } var binaryAccessor = getBinaryAccessor(property); var componentCount = binaryAccessor.componentsPerAttribute; var classType = binaryAccessor.classType; var typedArray = binaryAccessor.createArrayBufferView( binaryBody.buffer, binaryBody.byteOffset + byteOffset, featuresLength ); if (!defined(binaryProperties)) { binaryProperties = {}; } // Store any information needed to access the binary data, including the typed array, // componentCount (e.g. a VEC4 would be 4), and the type used to pack and unpack (e.g. Cartesian4). binaryProperties[name] = { typedArray: typedArray, componentCount: componentCount, type: classType, }; } } } return binaryProperties; } Cesium3DTileBatchTable.getBinaryProperties = function ( featuresLength, batchTableJson, batchTableBinary ) { return getBinaryProperties(featuresLength, batchTableJson, batchTableBinary); }; function getByteLength(batchTable) { var dimensions = batchTable._textureDimensions; return dimensions.x * dimensions.y * 4; } function getBatchValues(batchTable) { if (!defined(batchTable._batchValues)) { // Default batch texture to RGBA = 255: white highlight (RGB) and show/alpha = true/255 (A). var byteLength = getByteLength(batchTable); var bytes = new Uint8Array(byteLength); arrayFill(bytes, 255); batchTable._batchValues = bytes; } return batchTable._batchValues; } function getShowAlphaProperties(batchTable) { if (!defined(batchTable._showAlphaProperties)) { var byteLength = 2 * batchTable.featuresLength; var bytes = new Uint8Array(byteLength); // [Show = true, Alpha = 255] arrayFill(bytes, 255); batchTable._showAlphaProperties = bytes; } return batchTable._showAlphaProperties; } function checkBatchId(batchId, featuresLength) { if (!defined(batchId) || batchId < 0 || batchId > featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + featuresLength - +")." ); } } Cesium3DTileBatchTable.prototype.setShow = function (batchId, show) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.bool("show", show); //>>includeEnd('debug'); if (show && !defined(this._showAlphaProperties)) { // Avoid allocating since the default is show = true return; } var showAlphaProperties = getShowAlphaProperties(this); var propertyOffset = batchId * 2; var newShow = show ? 255 : 0; if (showAlphaProperties[propertyOffset] !== newShow) { showAlphaProperties[propertyOffset] = newShow; var batchValues = getBatchValues(this); // Compute alpha used in the shader based on show and color.alpha properties var offset = batchId * 4 + 3; batchValues[offset] = show ? showAlphaProperties[propertyOffset + 1] : 0; this._batchValuesDirty = true; } }; Cesium3DTileBatchTable.prototype.setAllShow = function (show) { //>>includeStart('debug', pragmas.debug); Check.typeOf.bool("show", show); //>>includeEnd('debug'); var featuresLength = this.featuresLength; for (var i = 0; i < featuresLength; ++i) { this.setShow(i, show); } }; Cesium3DTileBatchTable.prototype.getShow = function (batchId) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); //>>includeEnd('debug'); if (!defined(this._showAlphaProperties)) { // Avoid allocating since the default is show = true return true; } var offset = batchId * 2; return this._showAlphaProperties[offset] === 255; }; var scratchColorBytes$1 = new Array(4); Cesium3DTileBatchTable.prototype.setColor = function (batchId, color) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.object("color", color); //>>includeEnd('debug'); if (Color.equals(color, DEFAULT_COLOR_VALUE$2) && !defined(this._batchValues)) { // Avoid allocating since the default is white return; } var newColor = color.toBytes(scratchColorBytes$1); var newAlpha = newColor[3]; var batchValues = getBatchValues(this); var offset = batchId * 4; var showAlphaProperties = getShowAlphaProperties(this); var propertyOffset = batchId * 2; if ( batchValues[offset] !== newColor[0] || batchValues[offset + 1] !== newColor[1] || batchValues[offset + 2] !== newColor[2] || showAlphaProperties[propertyOffset + 1] !== newAlpha ) { batchValues[offset] = newColor[0]; batchValues[offset + 1] = newColor[1]; batchValues[offset + 2] = newColor[2]; var wasTranslucent = showAlphaProperties[propertyOffset + 1] !== 255; // Compute alpha used in the shader based on show and color.alpha properties var show = showAlphaProperties[propertyOffset] !== 0; batchValues[offset + 3] = show ? newAlpha : 0; showAlphaProperties[propertyOffset + 1] = newAlpha; // Track number of translucent features so we know if this tile needs // opaque commands, translucent commands, or both for rendering. var isTranslucent = newAlpha !== 255; if (isTranslucent && !wasTranslucent) { ++this._translucentFeaturesLength; } else if (!isTranslucent && wasTranslucent) { --this._translucentFeaturesLength; } this._batchValuesDirty = true; if (defined(this._colorChangedCallback)) { this._colorChangedCallback(batchId, color); } } }; Cesium3DTileBatchTable.prototype.setAllColor = function (color) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("color", color); //>>includeEnd('debug'); var featuresLength = this.featuresLength; for (var i = 0; i < featuresLength; ++i) { this.setColor(i, color); } }; Cesium3DTileBatchTable.prototype.getColor = function (batchId, result) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.object("result", result); //>>includeEnd('debug'); if (!defined(this._batchValues)) { return Color.clone(DEFAULT_COLOR_VALUE$2, result); } var batchValues = this._batchValues; var offset = batchId * 4; var showAlphaProperties = this._showAlphaProperties; var propertyOffset = batchId * 2; return Color.fromBytes( batchValues[offset], batchValues[offset + 1], batchValues[offset + 2], showAlphaProperties[propertyOffset + 1], result ); }; Cesium3DTileBatchTable.prototype.getPickColor = function (batchId) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); //>>includeEnd('debug'); return this._pickIds[batchId]; }; var scratchColor$l = new Color(); Cesium3DTileBatchTable.prototype.applyStyle = function (style) { if (!defined(style)) { this.setAllColor(DEFAULT_COLOR_VALUE$2); this.setAllShow(DEFAULT_SHOW_VALUE$2); return; } var content = this._content; var length = this.featuresLength; for (var i = 0; i < length; ++i) { var feature = content.getFeature(i); var color = defined(style.color) ? style.color.evaluateColor(feature, scratchColor$l) : DEFAULT_COLOR_VALUE$2; var show = defined(style.show) ? style.show.evaluate(feature) : DEFAULT_SHOW_VALUE$2; this.setColor(i, color); this.setShow(i, show); } }; function getBinaryProperty(binaryProperty, index) { var typedArray = binaryProperty.typedArray; var componentCount = binaryProperty.componentCount; if (componentCount === 1) { return typedArray[index]; } return binaryProperty.type.unpack(typedArray, index * componentCount); } function setBinaryProperty(binaryProperty, index, value) { var typedArray = binaryProperty.typedArray; var componentCount = binaryProperty.componentCount; if (componentCount === 1) { typedArray[index] = value; } else { binaryProperty.type.pack(value, typedArray, index * componentCount); } } // The size of this array equals the maximum instance count among all loaded tiles, which has the potential to be large. var scratchVisited = []; var scratchStack$1 = []; var marker = 0; function traverseHierarchyMultipleParents( hierarchy, instanceIndex, endConditionCallback ) { var classIds = hierarchy.classIds; var parentCounts = hierarchy.parentCounts; var parentIds = hierarchy.parentIds; var parentIndexes = hierarchy.parentIndexes; var instancesLength = classIds.length; // Ignore instances that have already been visited. This occurs in diamond inheritance situations. // Use a marker value to indicate that an instance has been visited, which increments with each run. // This is more efficient than clearing the visited array every time. var visited = scratchVisited; visited.length = Math.max(visited.length, instancesLength); var visitedMarker = ++marker; var stack = scratchStack$1; stack.length = 0; stack.push(instanceIndex); while (stack.length > 0) { instanceIndex = stack.pop(); if (visited[instanceIndex] === visitedMarker) { // This instance has already been visited, stop traversal continue; } visited[instanceIndex] = visitedMarker; var result = endConditionCallback(hierarchy, instanceIndex); if (defined(result)) { // The end condition was met, stop the traversal and return the result return result; } var parentCount = parentCounts[instanceIndex]; var parentIndex = parentIndexes[instanceIndex]; for (var i = 0; i < parentCount; ++i) { var parentId = parentIds[parentIndex + i]; // Stop the traversal when the instance has no parent (its parentId equals itself) // else add the parent to the stack to continue the traversal. if (parentId !== instanceIndex) { stack.push(parentId); } } } } function traverseHierarchySingleParent( hierarchy, instanceIndex, endConditionCallback ) { var hasParent = true; while (hasParent) { var result = endConditionCallback(hierarchy, instanceIndex); if (defined(result)) { // The end condition was met, stop the traversal and return the result return result; } var parentId = hierarchy.parentIds[instanceIndex]; hasParent = parentId !== instanceIndex; instanceIndex = parentId; } } function traverseHierarchy(hierarchy, instanceIndex, endConditionCallback) { // Traverse over the hierarchy and process each instance with the endConditionCallback. // When the endConditionCallback returns a value, the traversal stops and that value is returned. var parentCounts = hierarchy.parentCounts; var parentIds = hierarchy.parentIds; if (!defined(parentIds)) { return endConditionCallback(hierarchy, instanceIndex); } else if (defined(parentCounts)) { return traverseHierarchyMultipleParents( hierarchy, instanceIndex, endConditionCallback ); } return traverseHierarchySingleParent( hierarchy, instanceIndex, endConditionCallback ); } function hasPropertyInHierarchy(batchTable, batchId, name) { var hierarchy = batchTable._batchTableHierarchy; var result = traverseHierarchy(hierarchy, batchId, function ( hierarchy, instanceIndex ) { var classId = hierarchy.classIds[instanceIndex]; var instances = hierarchy.classes[classId].instances; if (defined(instances[name])) { return true; } }); return defined(result); } function getPropertyNamesInHierarchy(batchTable, batchId, results) { var hierarchy = batchTable._batchTableHierarchy; traverseHierarchy(hierarchy, batchId, function (hierarchy, instanceIndex) { var classId = hierarchy.classIds[instanceIndex]; var instances = hierarchy.classes[classId].instances; for (var name in instances) { if (instances.hasOwnProperty(name)) { if (results.indexOf(name) === -1) { results.push(name); } } } }); } function getHierarchyProperty(batchTable, batchId, name) { var hierarchy = batchTable._batchTableHierarchy; return traverseHierarchy(hierarchy, batchId, function ( hierarchy, instanceIndex ) { var classId = hierarchy.classIds[instanceIndex]; var instanceClass = hierarchy.classes[classId]; var indexInClass = hierarchy.classIndexes[instanceIndex]; var propertyValues = instanceClass.instances[name]; if (defined(propertyValues)) { if (defined(propertyValues.typedArray)) { return getBinaryProperty(propertyValues, indexInClass); } return clone$1(propertyValues[indexInClass], true); } }); } function setHierarchyProperty(batchTable, batchId, name, value) { var hierarchy = batchTable._batchTableHierarchy; var result = traverseHierarchy(hierarchy, batchId, function ( hierarchy, instanceIndex ) { var classId = hierarchy.classIds[instanceIndex]; var instanceClass = hierarchy.classes[classId]; var indexInClass = hierarchy.classIndexes[instanceIndex]; var propertyValues = instanceClass.instances[name]; if (defined(propertyValues)) { //>>includeStart('debug', pragmas.debug); if (instanceIndex !== batchId) { throw new DeveloperError( 'Inherited property "' + name + '" is read-only.' ); } //>>includeEnd('debug'); if (defined(propertyValues.typedArray)) { setBinaryProperty(propertyValues, indexInClass, value); } else { propertyValues[indexInClass] = clone$1(value, true); } return true; } }); return defined(result); } Cesium3DTileBatchTable.prototype.isClass = function (batchId, className) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.string("className", className); //>>includeEnd('debug'); // PERFORMANCE_IDEA : cache results in the ancestor classes to speed up this check if this area becomes a hotspot var hierarchy = this._batchTableHierarchy; if (!defined(hierarchy)) { return false; } // PERFORMANCE_IDEA : treat class names as integers for faster comparisons var result = traverseHierarchy(hierarchy, batchId, function ( hierarchy, instanceIndex ) { var classId = hierarchy.classIds[instanceIndex]; var instanceClass = hierarchy.classes[classId]; if (instanceClass.name === className) { return true; } }); return defined(result); }; Cesium3DTileBatchTable.prototype.isExactClass = function (batchId, className) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("className", className); //>>includeEnd('debug'); return this.getExactClassName(batchId) === className; }; Cesium3DTileBatchTable.prototype.getExactClassName = function (batchId) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); //>>includeEnd('debug'); var hierarchy = this._batchTableHierarchy; if (!defined(hierarchy)) { return undefined; } var classId = hierarchy.classIds[batchId]; var instanceClass = hierarchy.classes[classId]; return instanceClass.name; }; Cesium3DTileBatchTable.prototype.hasProperty = function (batchId, name) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.string("name", name); //>>includeEnd('debug'); return ( defined(this._properties[name]) || (defined(this._batchTableHierarchy) && hasPropertyInHierarchy(this, batchId, name)) ); }; Cesium3DTileBatchTable.prototype.getPropertyNames = function ( batchId, results ) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); //>>includeEnd('debug'); results = defined(results) ? results : []; results.length = 0; var propertyNames = Object.keys(this._properties); results.push.apply(results, propertyNames); if (defined(this._batchTableHierarchy)) { getPropertyNamesInHierarchy(this, batchId, results); } return results; }; Cesium3DTileBatchTable.prototype.getProperty = function (batchId, name) { //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, this.featuresLength); Check.typeOf.string("name", name); //>>includeEnd('debug'); if (defined(this._batchTableBinaryProperties)) { var binaryProperty = this._batchTableBinaryProperties[name]; if (defined(binaryProperty)) { return getBinaryProperty(binaryProperty, batchId); } } var propertyValues = this._properties[name]; if (defined(propertyValues)) { return clone$1(propertyValues[batchId], true); } if (defined(this._batchTableHierarchy)) { var hierarchyProperty = getHierarchyProperty(this, batchId, name); if (defined(hierarchyProperty)) { return hierarchyProperty; } } return undefined; }; Cesium3DTileBatchTable.prototype.setProperty = function (batchId, name, value) { var featuresLength = this.featuresLength; //>>includeStart('debug', pragmas.debug); checkBatchId(batchId, featuresLength); Check.typeOf.string("name", name); //>>includeEnd('debug'); if (defined(this._batchTableBinaryProperties)) { var binaryProperty = this._batchTableBinaryProperties[name]; if (defined(binaryProperty)) { setBinaryProperty(binaryProperty, batchId, value); return; } } if (defined(this._batchTableHierarchy)) { if (setHierarchyProperty(this, batchId, name, value)) { return; } } var propertyValues = this._properties[name]; if (!defined(propertyValues)) { // Property does not exist. Create it. this._properties[name] = new Array(featuresLength); propertyValues = this._properties[name]; } propertyValues[batchId] = clone$1(value, true); }; function getGlslComputeSt(batchTable) { // GLSL batchId is zero-based: [0, featuresLength - 1] if (batchTable._textureDimensions.y === 1) { return ( "uniform vec4 tile_textureStep; \n" + "vec2 computeSt(float batchId) \n" + "{ \n" + " float stepX = tile_textureStep.x; \n" + " float centerX = tile_textureStep.y; \n" + " return vec2(centerX + (batchId * stepX), 0.5); \n" + "} \n" ); } return ( "uniform vec4 tile_textureStep; \n" + "uniform vec2 tile_textureDimensions; \n" + "vec2 computeSt(float batchId) \n" + "{ \n" + " float stepX = tile_textureStep.x; \n" + " float centerX = tile_textureStep.y; \n" + " float stepY = tile_textureStep.z; \n" + " float centerY = tile_textureStep.w; \n" + " float xId = mod(batchId, tile_textureDimensions.x); \n" + " float yId = floor(batchId / tile_textureDimensions.x); \n" + " return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n" + "} \n" ); } Cesium3DTileBatchTable.prototype.getVertexShaderCallback = function ( handleTranslucent, batchIdAttributeName, diffuseAttributeOrUniformName ) { if (this.featuresLength === 0) { return; } var that = this; return function (source) { // If the color blend mode is HIGHLIGHT, the highlight color will always be applied in the fragment shader. // No need to apply the highlight color in the vertex shader as well. var renamedSource = modifyDiffuse( source, diffuseAttributeOrUniformName, false ); var newMain; if (ContextLimits.maximumVertexTextureImageUnits > 0) { // When VTF is supported, perform per-feature show/hide in the vertex shader newMain = ""; if (handleTranslucent) { newMain += "uniform bool tile_translucentCommand; \n"; } newMain += "uniform sampler2D tile_batchTexture; \n" + "varying vec4 tile_featureColor; \n" + "varying vec2 tile_featureSt; \n" + "void main() \n" + "{ \n" + " vec2 st = computeSt(" + batchIdAttributeName + "); \n" + " vec4 featureProperties = texture2D(tile_batchTexture, st); \n" + " tile_color(featureProperties); \n" + " float show = ceil(featureProperties.a); \n" + // 0 - false, non-zeo - true " gl_Position *= show; \n"; // Per-feature show/hide if (handleTranslucent) { newMain += " bool isStyleTranslucent = (featureProperties.a != 1.0); \n" + " if (czm_pass == czm_passTranslucent) \n" + " { \n" + " if (!isStyleTranslucent && !tile_translucentCommand) \n" + // Do not render opaque features in the translucent pass " { \n" + " gl_Position *= 0.0; \n" + " } \n" + " } \n" + " else \n" + " { \n" + " if (isStyleTranslucent) \n" + // Do not render translucent features in the opaque pass " { \n" + " gl_Position *= 0.0; \n" + " } \n" + " } \n"; } newMain += " tile_featureColor = featureProperties; \n" + " tile_featureSt = st; \n" + "}"; } else { // When VTF is not supported, color blend mode MIX will look incorrect due to the feature's color not being available in the vertex shader newMain = "varying vec2 tile_featureSt; \n" + "void main() \n" + "{ \n" + " tile_color(vec4(1.0)); \n" + " tile_featureSt = computeSt(" + batchIdAttributeName + "); \n" + "}"; } return renamedSource + "\n" + getGlslComputeSt(that) + newMain; }; }; function getDefaultShader(source, applyHighlight) { source = ShaderSource.replaceMain(source, "tile_main"); if (!applyHighlight) { return ( source + "void tile_color(vec4 tile_featureColor) \n" + "{ \n" + " tile_main(); \n" + "} \n" ); } // The color blend mode is intended for the RGB channels so alpha is always just multiplied. // gl_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight) return ( source + "uniform float tile_colorBlend; \n" + "void tile_color(vec4 tile_featureColor) \n" + "{ \n" + " tile_main(); \n" + " tile_featureColor = czm_gammaCorrect(tile_featureColor); \n" + " gl_FragColor.a *= tile_featureColor.a; \n" + " float highlight = ceil(tile_colorBlend); \n" + " gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n" + "} \n" ); } function replaceDiffuseTextureCalls(source, diffuseAttributeOrUniformName) { var functionCall = "texture2D(" + diffuseAttributeOrUniformName; var fromIndex = 0; var startIndex = source.indexOf(functionCall, fromIndex); var endIndex; while (startIndex > -1) { var nestedLevel = 0; for (var i = startIndex; i < source.length; ++i) { var character = source.charAt(i); if (character === "(") { ++nestedLevel; } else if (character === ")") { --nestedLevel; if (nestedLevel === 0) { endIndex = i + 1; break; } } } var extractedFunction = source.slice(startIndex, endIndex); var replacedFunction = "tile_diffuse_final(" + extractedFunction + ", tile_diffuse)"; source = source.slice(0, startIndex) + replacedFunction + source.slice(endIndex); fromIndex = startIndex + replacedFunction.length; startIndex = source.indexOf(functionCall, fromIndex); } return source; } function modifyDiffuse(source, diffuseAttributeOrUniformName, applyHighlight) { // If the glTF does not specify the _3DTILESDIFFUSE semantic, return the default shader. // Otherwise if _3DTILESDIFFUSE is defined prefer the shader below that can switch the color mode at runtime. if (!defined(diffuseAttributeOrUniformName)) { return getDefaultShader(source, applyHighlight); } // Find the diffuse uniform. Examples matches: // uniform vec3 u_diffuseColor; // uniform sampler2D diffuseTexture; var regex = new RegExp( "(uniform|attribute|in)\\s+(vec[34]|sampler2D)\\s+" + diffuseAttributeOrUniformName + ";" ); var uniformMatch = source.match(regex); if (!defined(uniformMatch)) { // Could not find uniform declaration of type vec3, vec4, or sampler2D return getDefaultShader(source, applyHighlight); } var declaration = uniformMatch[0]; var type = uniformMatch[2]; source = ShaderSource.replaceMain(source, "tile_main"); source = source.replace(declaration, ""); // Remove uniform declaration for now so the replace below doesn't affect it // If the tile color is white, use the source color. This implies the feature has not been styled. // Highlight: tile_colorBlend is 0.0 and the source color is used // Replace: tile_colorBlend is 1.0 and the tile color is used // Mix: tile_colorBlend is between 0.0 and 1.0, causing the source color and tile color to mix var finalDiffuseFunction = "bool isWhite(vec3 color) \n" + "{ \n" + " return all(greaterThan(color, vec3(1.0 - czm_epsilon3))); \n" + "} \n" + "vec4 tile_diffuse_final(vec4 sourceDiffuse, vec4 tileDiffuse) \n" + "{ \n" + " vec4 blendDiffuse = mix(sourceDiffuse, tileDiffuse, tile_colorBlend); \n" + " vec4 diffuse = isWhite(tileDiffuse.rgb) ? sourceDiffuse : blendDiffuse; \n" + " return vec4(diffuse.rgb, sourceDiffuse.a); \n" + "} \n"; // The color blend mode is intended for the RGB channels so alpha is always just multiplied. // gl_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight) var highlight = " tile_featureColor = czm_gammaCorrect(tile_featureColor); \n" + " gl_FragColor.a *= tile_featureColor.a; \n" + " float highlight = ceil(tile_colorBlend); \n" + " gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n"; var setColor; if (type === "vec3" || type === "vec4") { var sourceDiffuse = type === "vec3" ? "vec4(" + diffuseAttributeOrUniformName + ", 1.0)" : diffuseAttributeOrUniformName; var replaceDiffuse = type === "vec3" ? "tile_diffuse.xyz" : "tile_diffuse"; regex = new RegExp(diffuseAttributeOrUniformName, "g"); source = source.replace(regex, replaceDiffuse); setColor = " vec4 source = " + sourceDiffuse + "; \n" + " tile_diffuse = tile_diffuse_final(source, tile_featureColor); \n" + " tile_main(); \n"; } else if (type === "sampler2D") { // Handles any number of nested parentheses // E.g. texture2D(u_diffuse, uv) // E.g. texture2D(u_diffuse, computeUV(index)) source = replaceDiffuseTextureCalls(source, diffuseAttributeOrUniformName); setColor = " tile_diffuse = tile_featureColor; \n" + " tile_main(); \n"; } source = "uniform float tile_colorBlend; \n" + "vec4 tile_diffuse = vec4(1.0); \n" + finalDiffuseFunction + declaration + "\n" + source + "\n" + "void tile_color(vec4 tile_featureColor) \n" + "{ \n" + setColor; if (applyHighlight) { source += highlight; } source += "} \n"; return source; } Cesium3DTileBatchTable.prototype.getFragmentShaderCallback = function ( handleTranslucent, diffuseAttributeOrUniformName ) { if (this.featuresLength === 0) { return; } return function (source) { source = modifyDiffuse(source, diffuseAttributeOrUniformName, true); if (ContextLimits.maximumVertexTextureImageUnits > 0) { // When VTF is supported, per-feature show/hide already happened in the fragment shader source += "uniform sampler2D tile_pickTexture; \n" + "varying vec2 tile_featureSt; \n" + "varying vec4 tile_featureColor; \n" + "void main() \n" + "{ \n" + " tile_color(tile_featureColor); \n" + "}"; } else { if (handleTranslucent) { source += "uniform bool tile_translucentCommand; \n"; } source += "uniform sampler2D tile_pickTexture; \n" + "uniform sampler2D tile_batchTexture; \n" + "varying vec2 tile_featureSt; \n" + "void main() \n" + "{ \n" + " vec4 featureProperties = texture2D(tile_batchTexture, tile_featureSt); \n" + " if (featureProperties.a == 0.0) { \n" + // show: alpha == 0 - false, non-zeo - true " discard; \n" + " } \n"; if (handleTranslucent) { source += " bool isStyleTranslucent = (featureProperties.a != 1.0); \n" + " if (czm_pass == czm_passTranslucent) \n" + " { \n" + " if (!isStyleTranslucent && !tile_translucentCommand) \n" + // Do not render opaque features in the translucent pass " { \n" + " discard; \n" + " } \n" + " } \n" + " else \n" + " { \n" + " if (isStyleTranslucent) \n" + // Do not render translucent features in the opaque pass " { \n" + " discard; \n" + " } \n" + " } \n"; } source += " tile_color(featureProperties); \n" + "} \n"; } return source; }; }; Cesium3DTileBatchTable.prototype.getClassificationFragmentShaderCallback = function () { if (this.featuresLength === 0) { return; } return function (source) { source = ShaderSource.replaceMain(source, "tile_main"); if (ContextLimits.maximumVertexTextureImageUnits > 0) { // When VTF is supported, per-feature show/hide already happened in the fragment shader source += "uniform sampler2D tile_pickTexture;\n" + "varying vec2 tile_featureSt; \n" + "varying vec4 tile_featureColor; \n" + "void main() \n" + "{ \n" + " tile_main(); \n" + " gl_FragColor = tile_featureColor; \n" + "}"; } else { source += "uniform sampler2D tile_batchTexture; \n" + "uniform sampler2D tile_pickTexture;\n" + "varying vec2 tile_featureSt; \n" + "void main() \n" + "{ \n" + " tile_main(); \n" + " vec4 featureProperties = texture2D(tile_batchTexture, tile_featureSt); \n" + " if (featureProperties.a == 0.0) { \n" + // show: alpha == 0 - false, non-zeo - true " discard; \n" + " } \n" + " gl_FragColor = featureProperties; \n" + "} \n"; } return source; }; }; function getColorBlend(batchTable) { var tileset = batchTable._content.tileset; var colorBlendMode = tileset.colorBlendMode; var colorBlendAmount = tileset.colorBlendAmount; if (colorBlendMode === Cesium3DTileColorBlendMode$1.HIGHLIGHT) { return 0.0; } if (colorBlendMode === Cesium3DTileColorBlendMode$1.REPLACE) { return 1.0; } if (colorBlendMode === Cesium3DTileColorBlendMode$1.MIX) { // The value 0.0 is reserved for highlight, so clamp to just above 0.0. return CesiumMath.clamp(colorBlendAmount, CesiumMath.EPSILON4, 1.0); } //>>includeStart('debug', pragmas.debug); throw new DeveloperError( 'Invalid color blend mode "' + colorBlendMode + '".' ); //>>includeEnd('debug'); } Cesium3DTileBatchTable.prototype.getUniformMapCallback = function () { if (this.featuresLength === 0) { return; } var that = this; return function (uniformMap) { var batchUniformMap = { tile_batchTexture: function () { // PERFORMANCE_IDEA: we could also use a custom shader that avoids the texture read. return defaultValue(that._batchTexture, that._defaultTexture); }, tile_textureDimensions: function () { return that._textureDimensions; }, tile_textureStep: function () { return that._textureStep; }, tile_colorBlend: function () { return getColorBlend(that); }, tile_pickTexture: function () { return that._pickTexture; }, }; return combine$2(uniformMap, batchUniformMap); }; }; Cesium3DTileBatchTable.prototype.getPickId = function () { return "texture2D(tile_pickTexture, tile_featureSt)"; }; /////////////////////////////////////////////////////////////////////////// var StyleCommandsNeeded = { ALL_OPAQUE: 0, ALL_TRANSLUCENT: 1, OPAQUE_AND_TRANSLUCENT: 2, }; Cesium3DTileBatchTable.prototype.addDerivedCommands = function ( frameState, commandStart ) { var commandList = frameState.commandList; var commandEnd = commandList.length; var tile = this._content._tile; var finalResolution = tile._finalResolution; var tileset = tile.tileset; var bivariateVisibilityTest = tileset._skipLevelOfDetail && tileset._hasMixedContent && frameState.context.stencilBuffer; var styleCommandsNeeded = getStyleCommandsNeeded(this); for (var i = commandStart; i < commandEnd; ++i) { var command = commandList[i]; var derivedCommands = command.derivedCommands.tileset; if (!defined(derivedCommands) || command.dirty) { derivedCommands = {}; command.derivedCommands.tileset = derivedCommands; derivedCommands.originalCommand = deriveCommand(command); command.dirty = false; } var originalCommand = derivedCommands.originalCommand; if ( styleCommandsNeeded !== StyleCommandsNeeded.ALL_OPAQUE && command.pass !== Pass$1.TRANSLUCENT ) { if (!defined(derivedCommands.translucent)) { derivedCommands.translucent = deriveTranslucentCommand$1(originalCommand); } } if ( styleCommandsNeeded !== StyleCommandsNeeded.ALL_TRANSLUCENT && command.pass !== Pass$1.TRANSLUCENT ) { if (!defined(derivedCommands.opaque)) { derivedCommands.opaque = deriveOpaqueCommand(originalCommand); } if (bivariateVisibilityTest) { if (!finalResolution) { if (!defined(derivedCommands.zback)) { derivedCommands.zback = deriveZBackfaceCommand( frameState.context, originalCommand ); } tileset._backfaceCommands.push(derivedCommands.zback); } if ( !defined(derivedCommands.stencil) || tile._selectionDepth !== getLastSelectionDepth(derivedCommands.stencil) ) { if (command.renderState.depthMask) { derivedCommands.stencil = deriveStencilCommand( originalCommand, tile._selectionDepth ); } else { // Ignore if tile does not write depth derivedCommands.stencil = derivedCommands.opaque; } } } } var opaqueCommand = bivariateVisibilityTest ? derivedCommands.stencil : derivedCommands.opaque; var translucentCommand = derivedCommands.translucent; // If the command was originally opaque: // * If the styling applied to the tile is all opaque, use the opaque command // (with one additional uniform needed for the shader). // * If the styling is all translucent, use new (cached) derived commands (front // and back faces) with a translucent render state. // * If the styling causes both opaque and translucent features in this tile, // then use both sets of commands. if (command.pass !== Pass$1.TRANSLUCENT) { if (styleCommandsNeeded === StyleCommandsNeeded.ALL_OPAQUE) { commandList[i] = opaqueCommand; } if (styleCommandsNeeded === StyleCommandsNeeded.ALL_TRANSLUCENT) { commandList[i] = translucentCommand; } if (styleCommandsNeeded === StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT) { // PERFORMANCE_IDEA: if the tile has multiple commands, we do not know what features are in what // commands so this case may be overkill. commandList[i] = opaqueCommand; commandList.push(translucentCommand); } } else { // Command was originally translucent so no need to derive new commands; // as of now, a style can't change an originally translucent feature to // opaque since the style's alpha is modulated, not a replacement. When // this changes, we need to derive new opaque commands here. commandList[i] = originalCommand; } } }; function getStyleCommandsNeeded(batchTable) { var translucentFeaturesLength = batchTable._translucentFeaturesLength; if (translucentFeaturesLength === 0) { return StyleCommandsNeeded.ALL_OPAQUE; } else if (translucentFeaturesLength === batchTable.featuresLength) { return StyleCommandsNeeded.ALL_TRANSLUCENT; } return StyleCommandsNeeded.OPAQUE_AND_TRANSLUCENT; } function deriveCommand(command) { var derivedCommand = DrawCommand.shallowClone(command); // Add a uniform to indicate if the original command was translucent so // the shader knows not to cull vertices that were originally transparent // even though their style is opaque. var translucentCommand = derivedCommand.pass === Pass$1.TRANSLUCENT; derivedCommand.uniformMap = defined(derivedCommand.uniformMap) ? derivedCommand.uniformMap : {}; derivedCommand.uniformMap.tile_translucentCommand = function () { return translucentCommand; }; return derivedCommand; } function deriveTranslucentCommand$1(command) { var derivedCommand = DrawCommand.shallowClone(command); derivedCommand.pass = Pass$1.TRANSLUCENT; derivedCommand.renderState = getTranslucentRenderState$2(command.renderState); return derivedCommand; } function deriveOpaqueCommand(command) { var derivedCommand = DrawCommand.shallowClone(command); derivedCommand.renderState = getOpaqueRenderState(command.renderState); return derivedCommand; } function getLogDepthPolygonOffsetFragmentShaderProgram(context, shaderProgram) { var shader = context.shaderCache.getDerivedShaderProgram( shaderProgram, "zBackfaceLogDepth" ); if (!defined(shader)) { var fs = shaderProgram.fragmentShaderSource.clone(); fs.defines = defined(fs.defines) ? fs.defines.slice(0) : []; fs.defines.push("POLYGON_OFFSET"); fs.sources.unshift( "#ifdef GL_OES_standard_derivatives\n#extension GL_OES_standard_derivatives : enable\n#endif\n" ); shader = context.shaderCache.createDerivedShaderProgram( shaderProgram, "zBackfaceLogDepth", { vertexShaderSource: shaderProgram.vertexShaderSource, fragmentShaderSource: fs, attributeLocations: shaderProgram._attributeLocations, } ); } return shader; } function deriveZBackfaceCommand(context, command) { // Write just backface depth of unresolved tiles so resolved stenciled tiles do not appear in front var derivedCommand = DrawCommand.shallowClone(command); var rs = clone$1(derivedCommand.renderState, true); rs.cull.enabled = true; rs.cull.face = CullFace$1.FRONT; // Back faces do not need to write color. rs.colorMask = { red: false, green: false, blue: false, alpha: false, }; // Push back face depth away from the camera so it is less likely that back faces and front faces of the same tile // intersect and overlap. This helps avoid flickering for very thin double-sided walls. rs.polygonOffset = { enabled: true, factor: 5.0, units: 5.0, }; // Set the 3D Tiles bit rs.stencilTest = StencilConstants$1.setCesium3DTileBit(); rs.stencilMask = StencilConstants$1.CESIUM_3D_TILE_MASK; derivedCommand.renderState = RenderState.fromCache(rs); derivedCommand.castShadows = false; derivedCommand.receiveShadows = false; derivedCommand.uniformMap = clone$1(command.uniformMap); var polygonOffset = new Cartesian2(5.0, 5.0); derivedCommand.uniformMap.u_polygonOffset = function () { return polygonOffset; }; // Make the log depth depth fragment write account for the polygon offset, too. // Otherwise, the back face commands will cause the higher resolution // tiles to disappear. derivedCommand.shaderProgram = getLogDepthPolygonOffsetFragmentShaderProgram( context, command.shaderProgram ); return derivedCommand; } function deriveStencilCommand(command, reference) { // Tiles only draw if their selection depth is >= the tile drawn already. They write their // selection depth to the stencil buffer to prevent ancestor tiles from drawing on top var derivedCommand = DrawCommand.shallowClone(command); var rs = clone$1(derivedCommand.renderState, true); // Stencil test is masked to the most significant 3 bits so the reference is shifted. Writes 0 for the terrain bit rs.stencilTest.enabled = true; rs.stencilTest.mask = StencilConstants$1.SKIP_LOD_MASK; rs.stencilTest.reference = StencilConstants$1.CESIUM_3D_TILE_MASK | (reference << StencilConstants$1.SKIP_LOD_BIT_SHIFT); rs.stencilTest.frontFunction = StencilFunction$1.GREATER_OR_EQUAL; rs.stencilTest.frontOperation.zPass = StencilOperation$1.REPLACE; rs.stencilTest.backFunction = StencilFunction$1.GREATER_OR_EQUAL; rs.stencilTest.backOperation.zPass = StencilOperation$1.REPLACE; rs.stencilMask = StencilConstants$1.CESIUM_3D_TILE_MASK | StencilConstants$1.SKIP_LOD_MASK; derivedCommand.renderState = RenderState.fromCache(rs); return derivedCommand; } function getLastSelectionDepth(stencilCommand) { // Isolate the selection depth from the stencil reference. var reference = stencilCommand.renderState.stencilTest.reference; return ( (reference & StencilConstants$1.SKIP_LOD_MASK) >>> StencilConstants$1.SKIP_LOD_BIT_SHIFT ); } function getTranslucentRenderState$2(renderState) { var rs = clone$1(renderState, true); rs.cull.enabled = false; rs.depthTest.enabled = true; rs.depthMask = false; rs.blending = BlendingState$1.ALPHA_BLEND; return RenderState.fromCache(rs); } function getOpaqueRenderState(renderState) { var rs = clone$1(renderState, true); rs.stencilTest = StencilConstants$1.setCesium3DTileBit(); rs.stencilMask = StencilConstants$1.CESIUM_3D_TILE_MASK; return RenderState.fromCache(rs); } /////////////////////////////////////////////////////////////////////////// function createTexture$2(batchTable, context, bytes) { var dimensions = batchTable._textureDimensions; return new Texture({ context: context, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: PixelDatatype$1.UNSIGNED_BYTE, source: { width: dimensions.x, height: dimensions.y, arrayBufferView: bytes, }, flipY: false, sampler: Sampler.NEAREST, }); } function createPickTexture(batchTable, context) { var featuresLength = batchTable.featuresLength; if (!defined(batchTable._pickTexture) && featuresLength > 0) { var pickIds = batchTable._pickIds; var byteLength = getByteLength(batchTable); var bytes = new Uint8Array(byteLength); var content = batchTable._content; // PERFORMANCE_IDEA: we could skip the pick texture completely by allocating // a continuous range of pickIds and then converting the base pickId + batchId // to RGBA in the shader. The only consider is precision issues, which might // not be an issue in WebGL 2. for (var i = 0; i < featuresLength; ++i) { var pickId = context.createPickId(content.getFeature(i)); pickIds.push(pickId); var pickColor = pickId.color; var offset = i * 4; bytes[offset] = Color.floatToByte(pickColor.red); bytes[offset + 1] = Color.floatToByte(pickColor.green); bytes[offset + 2] = Color.floatToByte(pickColor.blue); bytes[offset + 3] = Color.floatToByte(pickColor.alpha); } batchTable._pickTexture = createTexture$2(batchTable, context, bytes); content.tileset._statistics.batchTableByteLength += batchTable._pickTexture.sizeInBytes; } } function updateBatchTexture(batchTable) { var dimensions = batchTable._textureDimensions; // PERFORMANCE_IDEA: Instead of rewriting the entire texture, use fine-grained // texture updates when less than, for example, 10%, of the values changed. Or // even just optimize the common case when one feature show/color changed. batchTable._batchTexture.copyFrom({ width: dimensions.x, height: dimensions.y, arrayBufferView: batchTable._batchValues, }); } Cesium3DTileBatchTable.prototype.update = function (tileset, frameState) { var context = frameState.context; this._defaultTexture = context.defaultTexture; var passes = frameState.passes; if (passes.pick || passes.postProcess) { createPickTexture(this, context); } if (this._batchValuesDirty) { this._batchValuesDirty = false; // Create batch texture on-demand if (!defined(this._batchTexture)) { this._batchTexture = createTexture$2(this, context, this._batchValues); tileset._statistics.batchTableByteLength += this._batchTexture.sizeInBytes; } updateBatchTexture(this); // Apply per-feature show/color updates } }; Cesium3DTileBatchTable.prototype.isDestroyed = function () { return false; }; Cesium3DTileBatchTable.prototype.destroy = function () { this._batchTexture = this._batchTexture && this._batchTexture.destroy(); this._pickTexture = this._pickTexture && this._pickTexture.destroy(); var pickIds = this._pickIds; var length = pickIds.length; for (var i = 0; i < length; ++i) { pickIds[i].destroy(); } return destroyObject(this); }; /** * A feature of a {@link Cesium3DTileset}. *

* Provides access to a feature's properties stored in the tile's batch table, as well * as the ability to show/hide a feature and change its highlight color via * {@link Cesium3DTileFeature#show} and {@link Cesium3DTileFeature#color}, respectively. *

*

* Modifications to a Cesium3DTileFeature object have the lifetime of the tile's * content. If the tile's content is unloaded, e.g., due to it going out of view and needing * to free space in the cache for visible tiles, listen to the {@link Cesium3DTileset#tileUnload} event to save any * modifications. Also listen to the {@link Cesium3DTileset#tileVisible} event to reapply any modifications. *

*

* Do not construct this directly. Access it through {@link Cesium3DTileContent#getFeature} * or picking using {@link Scene#pick} and {@link Scene#pickPosition}. *

* * @alias Cesium3DTileFeature * @constructor * * @example * // On mouse over, display all the properties for a feature in the console log. * handler.setInputAction(function(movement) { * var feature = scene.pick(movement.endPosition); * if (feature instanceof Cesium.Cesium3DTileFeature) { * var propertyNames = feature.getPropertyNames(); * var length = propertyNames.length; * for (var i = 0; i < length; ++i) { * var propertyName = propertyNames[i]; * console.log(propertyName + ': ' + feature.getProperty(propertyName)); * } * } * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); */ function Cesium3DTileFeature(content, batchId) { this._content = content; this._batchId = batchId; this._color = undefined; // for calling getColor } Object.defineProperties(Cesium3DTileFeature.prototype, { /** * Gets or sets if the feature will be shown. This is set for all features * when a style's show is evaluated. * * @memberof Cesium3DTileFeature.prototype * * @type {Boolean} * * @default true */ show: { get: function () { return this._content.batchTable.getShow(this._batchId); }, set: function (value) { this._content.batchTable.setShow(this._batchId, value); }, }, /** * Gets or sets the highlight color multiplied with the feature's color. When * this is white, the feature's color is not changed. This is set for all features * when a style's color is evaluated. * * @memberof Cesium3DTileFeature.prototype * * @type {Color} * * @default {@link Color.WHITE} */ color: { get: function () { if (!defined(this._color)) { this._color = new Color(); } return this._content.batchTable.getColor(this._batchId, this._color); }, set: function (value) { this._content.batchTable.setColor(this._batchId, value); }, }, /** * Gets the content of the tile containing the feature. * * @memberof Cesium3DTileFeature.prototype * * @type {Cesium3DTileContent} * * @readonly * @private */ content: { get: function () { return this._content; }, }, /** * Gets the tileset containing the feature. * * @memberof Cesium3DTileFeature.prototype * * @type {Cesium3DTileset} * * @readonly */ tileset: { get: function () { return this._content.tileset; }, }, /** * All objects returned by {@link Scene#pick} have a primitive property. This returns * the tileset containing the feature. * * @memberof Cesium3DTileFeature.prototype * * @type {Cesium3DTileset} * * @readonly */ primitive: { get: function () { return this._content.tileset; }, }, /** * @private */ pickId: { get: function () { return this._content.batchTable.getPickColor(this._batchId); }, }, }); /** * Returns whether the feature contains this property. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String} name The case-sensitive name of the property. * @returns {Boolean} Whether the feature contains this property. */ Cesium3DTileFeature.prototype.hasProperty = function (name) { return this._content.batchTable.hasProperty(this._batchId, name); }; /** * Returns an array of property names for the feature. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String[]} [results] An array into which to store the results. * @returns {String[]} The names of the feature's properties. */ Cesium3DTileFeature.prototype.getPropertyNames = function (results) { return this._content.batchTable.getPropertyNames(this._batchId, results); }; /** * Returns a copy of the value of the feature's property with the given name. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String} name The case-sensitive name of the property. * @returns {*} The value of the property or undefined if the property does not exist. * * @example * // Display all the properties for a feature in the console log. * var propertyNames = feature.getPropertyNames(); * var length = propertyNames.length; * for (var i = 0; i < length; ++i) { * var propertyName = propertyNames[i]; * console.log(propertyName + ': ' + feature.getProperty(propertyName)); * } */ Cesium3DTileFeature.prototype.getProperty = function (name) { return this._content.batchTable.getProperty(this._batchId, name); }; /** * Sets the value of the feature's property with the given name. *

* If a property with the given name doesn't exist, it is created. *

* * @param {String} name The case-sensitive name of the property. * @param {*} value The value of the property that will be copied. * * @exception {DeveloperError} Inherited batch table hierarchy property is read only. * * @example * var height = feature.getProperty('Height'); // e.g., the height of a building * * @example * var name = 'clicked'; * if (feature.getProperty(name)) { * console.log('already clicked'); * } else { * feature.setProperty(name, true); * console.log('first click'); * } */ Cesium3DTileFeature.prototype.setProperty = function (name, value) { this._content.batchTable.setProperty(this._batchId, name, value); // PERFORMANCE_IDEA: Probably overkill, but maybe only mark the tile dirty if the // property is in one of the style's expressions or - if it can be done quickly - // if the new property value changed the result of an expression. this._content.featurePropertiesDirty = true; }; /** * Returns whether the feature's class name equals className. Unlike {@link Cesium3DTileFeature#isClass} * this function only checks the feature's exact class and not inherited classes. *

* This function returns false if no batch table hierarchy is present. *

* * @param {String} className The name to check against. * @returns {Boolean} Whether the feature's class name equals className * * @private */ Cesium3DTileFeature.prototype.isExactClass = function (className) { return this._content.batchTable.isExactClass(this._batchId, className); }; /** * Returns whether the feature's class or any inherited classes are named className. *

* This function returns false if no batch table hierarchy is present. *

* * @param {String} className The name to check against. * @returns {Boolean} Whether the feature's class or inherited classes are named className * * @private */ Cesium3DTileFeature.prototype.isClass = function (className) { return this._content.batchTable.isClass(this._batchId, className); }; /** * Returns the feature's class name. *

* This function returns undefined if no batch table hierarchy is present. *

* * @returns {String} The feature's class name. * * @private */ Cesium3DTileFeature.prototype.getExactClassName = function () { return this._content.batchTable.getExactClassName(this._batchId); }; /** * @private */ function Cesium3DTileFeatureTable(featureTableJson, featureTableBinary) { this.json = featureTableJson; this.buffer = featureTableBinary; this._cachedTypedArrays = {}; this.featuresLength = 0; } function getTypedArrayFromBinary( featureTable, semantic, componentType, componentLength, count, byteOffset ) { var cachedTypedArrays = featureTable._cachedTypedArrays; var typedArray = cachedTypedArrays[semantic]; if (!defined(typedArray)) { typedArray = ComponentDatatype$1.createArrayBufferView( componentType, featureTable.buffer.buffer, featureTable.buffer.byteOffset + byteOffset, count * componentLength ); cachedTypedArrays[semantic] = typedArray; } return typedArray; } function getTypedArrayFromArray(featureTable, semantic, componentType, array) { var cachedTypedArrays = featureTable._cachedTypedArrays; var typedArray = cachedTypedArrays[semantic]; if (!defined(typedArray)) { typedArray = ComponentDatatype$1.createTypedArray(componentType, array); cachedTypedArrays[semantic] = typedArray; } return typedArray; } Cesium3DTileFeatureTable.prototype.getGlobalProperty = function ( semantic, componentType, componentLength ) { var jsonValue = this.json[semantic]; if (!defined(jsonValue)) { return undefined; } if (defined(jsonValue.byteOffset)) { componentType = defaultValue(componentType, ComponentDatatype$1.UNSIGNED_INT); componentLength = defaultValue(componentLength, 1); return getTypedArrayFromBinary( this, semantic, componentType, componentLength, 1, jsonValue.byteOffset ); } return jsonValue; }; Cesium3DTileFeatureTable.prototype.getPropertyArray = function ( semantic, componentType, componentLength ) { var jsonValue = this.json[semantic]; if (!defined(jsonValue)) { return undefined; } if (defined(jsonValue.byteOffset)) { if (defined(jsonValue.componentType)) { componentType = ComponentDatatype$1.fromName(jsonValue.componentType); } return getTypedArrayFromBinary( this, semantic, componentType, componentLength, this.featuresLength, jsonValue.byteOffset ); } return getTypedArrayFromArray(this, semantic, componentType, jsonValue); }; Cesium3DTileFeatureTable.prototype.getProperty = function ( semantic, componentType, componentLength, featureId, result ) { var jsonValue = this.json[semantic]; if (!defined(jsonValue)) { return undefined; } var typedArray = this.getPropertyArray( semantic, componentType, componentLength ); if (componentLength === 1) { return typedArray[featureId]; } for (var i = 0; i < componentLength; ++i) { result[i] = typedArray[componentLength * featureId + i]; } return result; }; /** * Adds an element to an array and returns the element's index. * * @param {Array} array The array to add to. * @param {Object} element The element to add. * @param {Boolean} [checkDuplicates=false] When true, if a duplicate element is found its index is returned and element is not added to the array. * * @private */ function addToArray(array, element, checkDuplicates) { checkDuplicates = defaultValue(checkDuplicates, false); if (checkDuplicates) { var index = array.indexOf(element); if (index > -1) { return index; } } array.push(element); return array.length - 1; } /** * Checks whether the glTF has the given extension. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String} extension The name of the extension. * @returns {Boolean} Whether the glTF has the given extension. * * @private */ function hasExtension(gltf, extension) { return defined(gltf.extensionsUsed) && (gltf.extensionsUsed.indexOf(extension) >= 0); } /** * Contains traversal functions for processing elements of the glTF hierarchy. * @constructor * * @private */ function ForEach() { } /** * Fallback for glTF 1.0 * @private */ ForEach.objectLegacy = function(objects, handler) { if (defined(objects)) { for (var objectId in objects) { if (Object.prototype.hasOwnProperty.call(objects, objectId)) { var object = objects[objectId]; var value = handler(object, objectId); if (defined(value)) { return value; } } } } }; /** * @private */ ForEach.object = function(arrayOfObjects, handler) { if (defined(arrayOfObjects)) { var length = arrayOfObjects.length; for (var i = 0; i < length; i++) { var object = arrayOfObjects[i]; var value = handler(object, i); if (defined(value)) { return value; } } } }; /** * Supports glTF 1.0 and 2.0 * @private */ ForEach.topLevel = function(gltf, name, handler) { var gltfProperty = gltf[name]; if (defined(gltfProperty) && !Array.isArray(gltfProperty)) { return ForEach.objectLegacy(gltfProperty, handler); } return ForEach.object(gltfProperty, handler); }; ForEach.accessor = function(gltf, handler) { return ForEach.topLevel(gltf, 'accessors', handler); }; ForEach.accessorWithSemantic = function(gltf, semantic, handler) { var visited = {}; return ForEach.mesh(gltf, function(mesh) { return ForEach.meshPrimitive(mesh, function(primitive) { var valueForEach = ForEach.meshPrimitiveAttribute(primitive, function(accessorId, attributeSemantic) { if (attributeSemantic.indexOf(semantic) === 0 && !defined(visited[accessorId])) { visited[accessorId] = true; var value = handler(accessorId); if (defined(value)) { return value; } } }); if (defined(valueForEach)) { return valueForEach; } return ForEach.meshPrimitiveTarget(primitive, function(target) { return ForEach.meshPrimitiveTargetAttribute(target, function(accessorId, attributeSemantic) { if (attributeSemantic.indexOf(semantic) === 0 && !defined(visited[accessorId])) { visited[accessorId] = true; var value = handler(accessorId); if (defined(value)) { return value; } } }); }); }); }); }; ForEach.accessorContainingVertexAttributeData = function(gltf, handler) { var visited = {}; return ForEach.mesh(gltf, function(mesh) { return ForEach.meshPrimitive(mesh, function(primitive) { var valueForEach = ForEach.meshPrimitiveAttribute(primitive, function(accessorId) { if (!defined(visited[accessorId])) { visited[accessorId] = true; var value = handler(accessorId); if (defined(value)) { return value; } } }); if (defined(valueForEach)) { return valueForEach; } return ForEach.meshPrimitiveTarget(primitive, function(target) { return ForEach.meshPrimitiveTargetAttribute(target, function(accessorId) { if (!defined(visited[accessorId])) { visited[accessorId] = true; var value = handler(accessorId); if (defined(value)) { return value; } } }); }); }); }); }; ForEach.accessorContainingIndexData = function(gltf, handler) { var visited = {}; return ForEach.mesh(gltf, function(mesh) { return ForEach.meshPrimitive(mesh, function(primitive) { var indices = primitive.indices; if (defined(indices) && !defined(visited[indices])) { visited[indices] = true; var value = handler(indices); if (defined(value)) { return value; } } }); }); }; ForEach.animation = function(gltf, handler) { return ForEach.topLevel(gltf, 'animations', handler); }; ForEach.animationChannel = function(animation, handler) { var channels = animation.channels; return ForEach.object(channels, handler); }; ForEach.animationSampler = function(animation, handler) { var samplers = animation.samplers; return ForEach.object(samplers, handler); }; ForEach.buffer = function(gltf, handler) { return ForEach.topLevel(gltf, 'buffers', handler); }; ForEach.bufferView = function(gltf, handler) { return ForEach.topLevel(gltf, 'bufferViews', handler); }; ForEach.camera = function(gltf, handler) { return ForEach.topLevel(gltf, 'cameras', handler); }; ForEach.image = function(gltf, handler) { return ForEach.topLevel(gltf, 'images', handler); }; ForEach.compressedImage = function(image, handler) { if (defined(image.extras)) { var compressedImages = image.extras.compressedImage3DTiles; for (var type in compressedImages) { if (Object.prototype.hasOwnProperty.call(compressedImages, type)) { var compressedImage = compressedImages[type]; var value = handler(compressedImage, type); if (defined(value)) { return value; } } } } }; ForEach.material = function(gltf, handler) { return ForEach.topLevel(gltf, 'materials', handler); }; ForEach.materialValue = function(material, handler) { var values = material.values; if (defined(material.extensions) && defined(material.extensions.KHR_techniques_webgl)) { values = material.extensions.KHR_techniques_webgl.values; } for (var name in values) { if (Object.prototype.hasOwnProperty.call(values, name)) { var value = handler(values[name], name); if (defined(value)) { return value; } } } }; ForEach.mesh = function(gltf, handler) { return ForEach.topLevel(gltf, 'meshes', handler); }; ForEach.meshPrimitive = function(mesh, handler) { var primitives = mesh.primitives; if (defined(primitives)) { var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; i++) { var primitive = primitives[i]; var value = handler(primitive, i); if (defined(value)) { return value; } } } }; ForEach.meshPrimitiveAttribute = function(primitive, handler) { var attributes = primitive.attributes; for (var semantic in attributes) { if (Object.prototype.hasOwnProperty.call(attributes, semantic)) { var value = handler(attributes[semantic], semantic); if (defined(value)) { return value; } } } }; ForEach.meshPrimitiveTarget = function(primitive, handler) { var targets = primitive.targets; if (defined(targets)) { var length = targets.length; for (var i = 0; i < length; ++i) { var value = handler(targets[i], i); if (defined(value)) { return value; } } } }; ForEach.meshPrimitiveTargetAttribute = function(target, handler) { for (var semantic in target) { if (Object.prototype.hasOwnProperty.call(target, semantic)) { var accessorId = target[semantic]; var value = handler(accessorId, semantic); if (defined(value)) { return value; } } } }; ForEach.node = function(gltf, handler) { return ForEach.topLevel(gltf, 'nodes', handler); }; ForEach.nodeInTree = function(gltf, nodeIds, handler) { var nodes = gltf.nodes; if (defined(nodes)) { var length = nodeIds.length; for (var i = 0; i < length; i++) { var nodeId = nodeIds[i]; var node = nodes[nodeId]; if (defined(node)) { var value = handler(node, nodeId); if (defined(value)) { return value; } var children = node.children; if (defined(children)) { value = ForEach.nodeInTree(gltf, children, handler); if (defined(value)) { return value; } } } } } }; ForEach.nodeInScene = function(gltf, scene, handler) { var sceneNodeIds = scene.nodes; if (defined(sceneNodeIds)) { return ForEach.nodeInTree(gltf, sceneNodeIds, handler); } }; ForEach.program = function(gltf, handler) { if (hasExtension(gltf, 'KHR_techniques_webgl')) { return ForEach.object(gltf.extensions.KHR_techniques_webgl.programs, handler); } return ForEach.topLevel(gltf, 'programs', handler); }; ForEach.sampler = function(gltf, handler) { return ForEach.topLevel(gltf, 'samplers', handler); }; ForEach.scene = function(gltf, handler) { return ForEach.topLevel(gltf, 'scenes', handler); }; ForEach.shader = function(gltf, handler) { if (hasExtension(gltf, 'KHR_techniques_webgl')) { return ForEach.object(gltf.extensions.KHR_techniques_webgl.shaders, handler); } return ForEach.topLevel(gltf, 'shaders', handler); }; ForEach.skin = function(gltf, handler) { return ForEach.topLevel(gltf, 'skins', handler); }; ForEach.skinJoint = function(skin, handler) { var joints = skin.joints; if (defined(joints)) { var jointsLength = joints.length; for (var i = 0; i < jointsLength; i++) { var joint = joints[i]; var value = handler(joint); if (defined(value)) { return value; } } } }; ForEach.techniqueAttribute = function(technique, handler) { var attributes = technique.attributes; for (var attributeName in attributes) { if (Object.prototype.hasOwnProperty.call(attributes, attributeName)) { var value = handler(attributes[attributeName], attributeName); if (defined(value)) { return value; } } } }; ForEach.techniqueUniform = function(technique, handler) { var uniforms = technique.uniforms; for (var uniformName in uniforms) { if (Object.prototype.hasOwnProperty.call(uniforms, uniformName)) { var value = handler(uniforms[uniformName], uniformName); if (defined(value)) { return value; } } } }; ForEach.techniqueParameter = function(technique, handler) { var parameters = technique.parameters; for (var parameterName in parameters) { if (Object.prototype.hasOwnProperty.call(parameters, parameterName)) { var value = handler(parameters[parameterName], parameterName); if (defined(value)) { return value; } } } }; ForEach.technique = function(gltf, handler) { if (hasExtension(gltf, 'KHR_techniques_webgl')) { return ForEach.object(gltf.extensions.KHR_techniques_webgl.techniques, handler); } return ForEach.topLevel(gltf, 'techniques', handler); }; ForEach.texture = function(gltf, handler) { return ForEach.topLevel(gltf, 'textures', handler); }; /** * Utility function for retrieving the number of components in a given type. * * @param {String} type glTF type * @returns {Number} The number of components in that type. * * @private */ function numberOfComponentsForType(type) { switch (type) { case 'SCALAR': return 1; case 'VEC2': return 2; case 'VEC3': return 3; case 'VEC4': case 'MAT2': return 4; case 'MAT3': return 9; case 'MAT4': return 16; } } /** * Returns the byte stride of the provided accessor. * If the byteStride is 0, it is calculated based on type and componentType * * @param {Object} gltf A javascript object containing a glTF asset. * @param {Object} accessor The accessor. * @returns {Number} The byte stride of the accessor. * * @private */ function getAccessorByteStride(gltf, accessor) { var bufferViewId = accessor.bufferView; if (defined(bufferViewId)) { var bufferView = gltf.bufferViews[bufferViewId]; if (defined(bufferView.byteStride) && bufferView.byteStride > 0) { return bufferView.byteStride; } } return ComponentDatatype$1.getSizeInBytes(accessor.componentType) * numberOfComponentsForType(accessor.type); } /** * Adds default glTF values if they don't exist. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The modified glTF. * * @private */ function addDefaults(gltf) { ForEach.accessor(gltf, function(accessor) { if (defined(accessor.bufferView)) { accessor.byteOffset = defaultValue(accessor.byteOffset, 0); } }); ForEach.bufferView(gltf, function(bufferView) { if (defined(bufferView.buffer)) { bufferView.byteOffset = defaultValue(bufferView.byteOffset, 0); } }); ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { primitive.mode = defaultValue(primitive.mode, WebGLConstants$1.TRIANGLES); if (!defined(primitive.material)) { if (!defined(gltf.materials)) { gltf.materials = []; } var defaultMaterial = { name: 'default' }; primitive.material = addToArray(gltf.materials, defaultMaterial); } }); }); ForEach.accessorContainingVertexAttributeData(gltf, function(accessorId) { var accessor = gltf.accessors[accessorId]; var bufferViewId = accessor.bufferView; accessor.normalized = defaultValue(accessor.normalized, false); if (defined(bufferViewId)) { var bufferView = gltf.bufferViews[bufferViewId]; bufferView.byteStride = getAccessorByteStride(gltf, accessor); bufferView.target = WebGLConstants$1.ARRAY_BUFFER; } }); ForEach.accessorContainingIndexData(gltf, function(accessorId) { var accessor = gltf.accessors[accessorId]; var bufferViewId = accessor.bufferView; if (defined(bufferViewId)) { var bufferView = gltf.bufferViews[bufferViewId]; bufferView.target = WebGLConstants$1.ELEMENT_ARRAY_BUFFER; } }); ForEach.material(gltf, function(material) { var extensions = defaultValue(material.extensions, defaultValue.EMPTY_OBJECT); var materialsCommon = extensions.KHR_materials_common; if (defined(materialsCommon)) { var technique = materialsCommon.technique; var values = defined(materialsCommon.values) ? materialsCommon.values : {}; materialsCommon.values = values; values.ambient = defined(values.ambient) ? values.ambient : [0.0, 0.0, 0.0, 1.0]; values.emission = defined(values.emission) ? values.emission : [0.0, 0.0, 0.0, 1.0]; values.transparency = defaultValue(values.transparency, 1.0); values.transparent = defaultValue(values.transparent, false); values.doubleSided = defaultValue(values.doubleSided, false); if (technique !== 'CONSTANT') { values.diffuse = defined(values.diffuse) ? values.diffuse : [0.0, 0.0, 0.0, 1.0]; if (technique !== 'LAMBERT') { values.specular = defined(values.specular) ? values.specular : [0.0, 0.0, 0.0, 1.0]; values.shininess = defaultValue(values.shininess, 0.0); } } return; } material.emissiveFactor = defaultValue(material.emissiveFactor, [0.0, 0.0, 0.0]); material.alphaMode = defaultValue(material.alphaMode, 'OPAQUE'); material.doubleSided = defaultValue(material.doubleSided, false); if (material.alphaMode === 'MASK') { material.alphaCutoff = defaultValue(material.alphaCutoff, 0.5); } var techniquesExtension = extensions.KHR_techniques_webgl; if (defined(techniquesExtension)) { ForEach.materialValue(material, function (materialValue) { // Check if material value is a TextureInfo object if (defined(materialValue.index)) { addTextureDefaults(materialValue); } }); } addTextureDefaults(material.emissiveTexture); addTextureDefaults(material.normalTexture); addTextureDefaults(material.occlusionTexture); var pbrMetallicRoughness = material.pbrMetallicRoughness; if (defined(pbrMetallicRoughness)) { pbrMetallicRoughness.baseColorFactor = defaultValue(pbrMetallicRoughness.baseColorFactor, [1.0, 1.0, 1.0, 1.0]); pbrMetallicRoughness.metallicFactor = defaultValue(pbrMetallicRoughness.metallicFactor, 1.0); pbrMetallicRoughness.roughnessFactor = defaultValue(pbrMetallicRoughness.roughnessFactor, 1.0); addTextureDefaults(pbrMetallicRoughness.baseColorTexture); addTextureDefaults(pbrMetallicRoughness.metallicRoughnessTexture); } var pbrSpecularGlossiness = extensions.pbrSpecularGlossiness; if (defined(pbrSpecularGlossiness)) { pbrSpecularGlossiness.diffuseFactor = defaultValue(pbrSpecularGlossiness.diffuseFactor, [1.0, 1.0, 1.0, 1.0]); pbrSpecularGlossiness.specularFactor = defaultValue(pbrSpecularGlossiness.specularFactor, [1.0, 1.0, 1.0]); pbrSpecularGlossiness.glossinessFactor = defaultValue(pbrSpecularGlossiness.glossinessFactor, 1.0); addTextureDefaults(pbrSpecularGlossiness.specularGlossinessTexture); } }); ForEach.animation(gltf, function(animation) { ForEach.animationSampler(animation, function(sampler) { sampler.interpolation = defaultValue(sampler.interpolation, 'LINEAR'); }); }); var animatedNodes = getAnimatedNodes(gltf); ForEach.node(gltf, function(node, id) { var animated = defined(animatedNodes[id]); if (animated || defined(node.translation) || defined(node.rotation) || defined(node.scale)) { node.translation = defaultValue(node.translation, [0.0, 0.0, 0.0]); node.rotation = defaultValue(node.rotation, [0.0, 0.0, 0.0, 1.0]); node.scale = defaultValue(node.scale, [1.0, 1.0, 1.0]); } else { node.matrix = defaultValue(node.matrix, [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]); } }); ForEach.sampler(gltf, function(sampler) { sampler.wrapS = defaultValue(sampler.wrapS, WebGLConstants$1.REPEAT); sampler.wrapT = defaultValue(sampler.wrapT, WebGLConstants$1.REPEAT); }); if (defined(gltf.scenes) && !defined(gltf.scene)) { gltf.scene = 0; } return gltf; } function getAnimatedNodes(gltf) { var nodes = {}; ForEach.animation(gltf, function(animation) { ForEach.animationChannel(animation, function(channel) { var target = channel.target; var nodeId = target.node; var path = target.path; // Ignore animations that target 'weights' if (path === 'translation' || path === 'rotation' || path === 'scale') { nodes[nodeId] = true; } }); }); return nodes; } function addTextureDefaults(texture) { if (defined(texture)) { texture.texCoord = defaultValue(texture.texCoord, 0); } } /** * Adds extras._pipeline to each object that can have extras in the glTF asset. * This stage runs before updateVersion and handles both glTF 1.0 and glTF 2.0 assets. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The glTF asset with the added pipeline extras. * * @private */ function addPipelineExtras(gltf) { ForEach.shader(gltf, function(shader) { addExtras(shader); }); ForEach.buffer(gltf, function(buffer) { addExtras(buffer); }); ForEach.image(gltf, function (image) { addExtras(image); ForEach.compressedImage(image, function(compressedImage) { addExtras(compressedImage); }); }); addExtras(gltf); return gltf; } function addExtras(object) { object.extras = defined(object.extras) ? object.extras : {}; object.extras._pipeline = defined(object.extras._pipeline) ? object.extras._pipeline : {}; } /** * Removes an extension from gltf.extensionsRequired if it is present. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String} extension The extension to remove. * * @private */ function removeExtensionsRequired(gltf, extension) { var extensionsRequired = gltf.extensionsRequired; if (defined(extensionsRequired)) { var index = extensionsRequired.indexOf(extension); if (index >= 0) { extensionsRequired.splice(index, 1); } if (extensionsRequired.length === 0) { delete gltf.extensionsRequired; } } } /** * Removes an extension from gltf.extensionsUsed and gltf.extensionsRequired if it is present. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String} extension The extension to remove. * * @private */ function removeExtensionsUsed(gltf, extension) { var extensionsUsed = gltf.extensionsUsed; if (defined(extensionsUsed)) { var index = extensionsUsed.indexOf(extension); if (index >= 0) { extensionsUsed.splice(index, 1); } removeExtensionsRequired(gltf, extension); if (extensionsUsed.length === 0) { delete gltf.extensionsUsed; } } } var sizeOfUint32$6 = 4; /** * Convert a binary glTF to glTF. * * The returned glTF has pipeline extras included. The embedded binary data is stored in gltf.buffers[0].extras._pipeline.source. * * @param {Buffer} glb The glb data to parse. * @returns {Object} A javascript object containing a glTF asset with pipeline extras included. * * @private */ function parseGlb(glb) { // Check that the magic string is present var magic = getMagic(glb); if (magic !== 'glTF') { throw new RuntimeError('File is not valid binary glTF'); } var header = readHeader(glb, 0, 5); var version = header[1]; if (version !== 1 && version !== 2) { throw new RuntimeError('Binary glTF version is not 1 or 2'); } if (version === 1) { return parseGlbVersion1(glb, header); } return parseGlbVersion2(glb, header); } function readHeader(glb, byteOffset, count) { var dataView = new DataView(glb.buffer); var header = new Array(count); for (var i = 0; i < count; ++i) { header[i] = dataView.getUint32(glb.byteOffset + byteOffset + i * sizeOfUint32$6, true); } return header; } function parseGlbVersion1(glb, header) { var length = header[2]; var contentLength = header[3]; var contentFormat = header[4]; // Check that the content format is 0, indicating that it is JSON if (contentFormat !== 0) { throw new RuntimeError('Binary glTF scene format is not JSON'); } var jsonStart = 20; var binaryStart = jsonStart + contentLength; var gltf = getJsonFromTypedArray(glb, jsonStart, contentLength); addPipelineExtras(gltf); var binaryBuffer = glb.subarray(binaryStart, length); var buffers = gltf.buffers; if (defined(buffers) && Object.keys(buffers).length > 0) { // In some older models, the binary glTF buffer is named KHR_binary_glTF var binaryGltfBuffer = defaultValue(buffers.binary_glTF, buffers.KHR_binary_glTF); if (defined(binaryGltfBuffer)) { binaryGltfBuffer.extras._pipeline.source = binaryBuffer; } } // Remove the KHR_binary_glTF extension removeExtensionsUsed(gltf, 'KHR_binary_glTF'); return gltf; } function parseGlbVersion2(glb, header) { var length = header[2]; var byteOffset = 12; var gltf; var binaryBuffer; while (byteOffset < length) { var chunkHeader = readHeader(glb, byteOffset, 2); var chunkLength = chunkHeader[0]; var chunkType = chunkHeader[1]; byteOffset += 8; var chunkBuffer = glb.subarray(byteOffset, byteOffset + chunkLength); byteOffset += chunkLength; // Load JSON chunk if (chunkType === 0x4E4F534A) { gltf = getJsonFromTypedArray(chunkBuffer); addPipelineExtras(gltf); } // Load Binary chunk else if (chunkType === 0x004E4942) { binaryBuffer = chunkBuffer; } } if (defined(gltf) && defined(binaryBuffer)) { var buffers = gltf.buffers; if (defined(buffers) && buffers.length > 0) { var buffer = buffers[0]; buffer.extras._pipeline.source = binaryBuffer; } } return gltf; } /** * Adds an extension to gltf.extensionsUsed if it does not already exist. * Initializes extensionsUsed if it is not defined. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String} extension The extension to add. * * @private */ function addExtensionsUsed(gltf, extension) { var extensionsUsed = gltf.extensionsUsed; if (!defined(extensionsUsed)) { extensionsUsed = []; gltf.extensionsUsed = extensionsUsed; } addToArray(extensionsUsed, extension, true); } /** * Returns a function to read and convert data from a DataView into an array. * * @param {Number} componentType Type to convert the data to. * @returns {ComponentReader} Function that reads and converts data. * * @private */ function getComponentReader(componentType) { switch (componentType) { case ComponentDatatype$1.BYTE: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getInt8(byteOffset + i * componentTypeByteLength); } }; case ComponentDatatype$1.UNSIGNED_BYTE: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getUint8(byteOffset + i * componentTypeByteLength); } }; case ComponentDatatype$1.SHORT: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getInt16(byteOffset + i * componentTypeByteLength, true); } }; case ComponentDatatype$1.UNSIGNED_SHORT: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getUint16(byteOffset + i * componentTypeByteLength, true); } }; case ComponentDatatype$1.INT: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getInt32(byteOffset + i * componentTypeByteLength, true); } }; case ComponentDatatype$1.UNSIGNED_INT: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getUint32(byteOffset + i * componentTypeByteLength, true); } }; case ComponentDatatype$1.FLOAT: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getFloat32(byteOffset + i * componentTypeByteLength, true); } }; case ComponentDatatype$1.DOUBLE: return function (dataView, byteOffset, numberOfComponents, componentTypeByteLength, result) { for (var i = 0; i < numberOfComponents; ++i) { result[i] = dataView.getFloat64(byteOffset + i * componentTypeByteLength, true); } }; } } /** * Finds the min and max values of the accessor. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {Object} accessor The accessor object from the glTF asset to read. * @returns {{min: Array, max: Array}} min holding the array of minimum values and max holding the array of maximum values. * * @private */ function findAccessorMinMax(gltf, accessor) { var bufferViews = gltf.bufferViews; var buffers = gltf.buffers; var bufferViewId = accessor.bufferView; var numberOfComponents = numberOfComponentsForType(accessor.type); // According to the spec, when bufferView is not defined, accessor must be initialized with zeros if (!defined(accessor.bufferView)) { return { min: arrayFill(new Array(numberOfComponents), 0.0), max: arrayFill(new Array(numberOfComponents), 0.0) }; } var min = arrayFill(new Array(numberOfComponents), Number.POSITIVE_INFINITY); var max = arrayFill(new Array(numberOfComponents), Number.NEGATIVE_INFINITY); var bufferView = bufferViews[bufferViewId]; var bufferId = bufferView.buffer; var buffer = buffers[bufferId]; var source = buffer.extras._pipeline.source; var count = accessor.count; var byteStride = getAccessorByteStride(gltf, accessor); var byteOffset = accessor.byteOffset + bufferView.byteOffset + source.byteOffset; var componentType = accessor.componentType; var componentTypeByteLength = ComponentDatatype$1.getSizeInBytes(componentType); var dataView = new DataView(source.buffer); var components = new Array(numberOfComponents); var componentReader = getComponentReader(componentType); for (var i = 0; i < count; i++) { componentReader(dataView, byteOffset, numberOfComponents, componentTypeByteLength, components); for (var j = 0; j < numberOfComponents; j++) { var value = components[j]; min[j] = Math.min(min[j], value); max[j] = Math.max(max[j], value); } byteOffset += byteStride; } return { min: min, max: max }; } var defaultBlendEquation = [ WebGLConstants$1.FUNC_ADD, WebGLConstants$1.FUNC_ADD ]; var defaultBlendFactors = [ WebGLConstants$1.ONE, WebGLConstants$1.ZERO, WebGLConstants$1.ONE, WebGLConstants$1.ZERO ]; function isStateEnabled(renderStates, state) { var enabled = renderStates.enable; if (!defined(enabled)) { return false; } return (enabled.indexOf(state) > -1); } var supportedBlendFactors = [ WebGLConstants$1.ZERO, WebGLConstants$1.ONE, WebGLConstants$1.SRC_COLOR, WebGLConstants$1.ONE_MINUS_SRC_COLOR, WebGLConstants$1.SRC_ALPHA, WebGLConstants$1.ONE_MINUS_SRC_ALPHA, WebGLConstants$1.DST_ALPHA, WebGLConstants$1.ONE_MINUS_DST_ALPHA, WebGLConstants$1.DST_COLOR, WebGLConstants$1.ONE_MINUS_DST_COLOR ]; // If any of the blend factors are not supported, return the default function getSupportedBlendFactors(value, defaultValue) { if (!defined(value)) { return defaultValue; } for (var i = 0; i < 4; i++) { if (supportedBlendFactors.indexOf(value[i]) === -1) { return defaultValue; } } return value; } /** * Move glTF 1.0 technique render states to glTF 2.0 materials properties and KHR_blend extension. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The updated glTF asset. * * @private */ function moveTechniqueRenderStates(gltf) { var blendingForTechnique = {}; var materialPropertiesForTechnique = {}; var techniquesLegacy = gltf.techniques; if (!defined(techniquesLegacy)) { return gltf; } ForEach.technique(gltf, function (techniqueLegacy, techniqueIndex) { var renderStates = techniqueLegacy.states; if (defined(renderStates)) { var materialProperties = materialPropertiesForTechnique[techniqueIndex] = {}; // If BLEND is enabled, the material should have alpha mode BLEND if (isStateEnabled(renderStates, WebGLConstants$1.BLEND)) { materialProperties.alphaMode = 'BLEND'; var blendFunctions = renderStates.functions; if (defined(blendFunctions) && (defined(blendFunctions.blendEquationSeparate) || defined(blendFunctions.blendFuncSeparate))) { blendingForTechnique[techniqueIndex] = { blendEquation: defaultValue(blendFunctions.blendEquationSeparate, defaultBlendEquation), blendFactors: getSupportedBlendFactors(blendFunctions.blendFuncSeparate, defaultBlendFactors) }; } } // If CULL_FACE is not enabled, the material should be doubleSided if (!isStateEnabled(renderStates, WebGLConstants$1.CULL_FACE)) { materialProperties.doubleSided = true; } delete techniqueLegacy.states; } }); if (Object.keys(blendingForTechnique).length > 0) { if (!defined(gltf.extensions)) { gltf.extensions = {}; } addExtensionsUsed(gltf, 'KHR_blend'); } ForEach.material(gltf, function (material) { if (defined(material.technique)) { var materialProperties = materialPropertiesForTechnique[material.technique]; ForEach.objectLegacy(materialProperties, function (value, property) { material[property] = value; }); var blending = blendingForTechnique[material.technique]; if (defined(blending)) { if (!defined(material.extensions)) { material.extensions = {}; } material.extensions.KHR_blend = blending; } } }); return gltf; } /** * Adds an extension to gltf.extensionsRequired if it does not already exist. * Initializes extensionsRequired if it is not defined. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String} extension The extension to add. * * @private */ function addExtensionsRequired(gltf, extension) { var extensionsRequired = gltf.extensionsRequired; if (!defined(extensionsRequired)) { extensionsRequired = []; gltf.extensionsRequired = extensionsRequired; } addToArray(extensionsRequired, extension, true); addExtensionsUsed(gltf, extension); } /** * Move glTF 1.0 material techniques to glTF 2.0 KHR_techniques_webgl extension. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The updated glTF asset. * * @private */ function moveTechniquesToExtension(gltf) { var techniquesLegacy = gltf.techniques; var mappedUniforms = {}; var updatedTechniqueIndices = {}; if (defined(techniquesLegacy)) { var extension = { programs: [], shaders: [], techniques: [] }; // Some 1.1 models have a glExtensionsUsed property that can be transferred to program.glExtensions var glExtensions = gltf.glExtensionsUsed; delete gltf.glExtensionsUsed; ForEach.technique(gltf, function (techniqueLegacy, techniqueIndex) { var technique = { name: techniqueLegacy.name, program: undefined, attributes: {}, uniforms: {} }; var parameterLegacy; ForEach.techniqueAttribute(techniqueLegacy, function (parameterName, attributeName) { parameterLegacy = techniqueLegacy.parameters[parameterName]; technique.attributes[attributeName] = { semantic: parameterLegacy.semantic }; }); ForEach.techniqueUniform(techniqueLegacy, function (parameterName, uniformName) { parameterLegacy = techniqueLegacy.parameters[parameterName]; technique.uniforms[uniformName] = { count: parameterLegacy.count, node: parameterLegacy.node, type: parameterLegacy.type, semantic: parameterLegacy.semantic, value: parameterLegacy.value }; // Store the name of the uniform to update material values. mappedUniforms[parameterName] = uniformName; }); var programLegacy = gltf.programs[techniqueLegacy.program]; var program = { name: programLegacy.name, fragmentShader: undefined, vertexShader: undefined, glExtensions: glExtensions }; var fs = gltf.shaders[programLegacy.fragmentShader]; program.fragmentShader = addToArray(extension.shaders, fs, true); var vs = gltf.shaders[programLegacy.vertexShader]; program.vertexShader = addToArray(extension.shaders, vs, true); technique.program = addToArray(extension.programs, program); // Store the index of the new technique to reference instead. updatedTechniqueIndices[techniqueIndex] = addToArray(extension.techniques, technique); }); if (extension.techniques.length > 0) { if (!defined(gltf.extensions)) { gltf.extensions = {}; } gltf.extensions.KHR_techniques_webgl = extension; addExtensionsUsed(gltf, 'KHR_techniques_webgl'); addExtensionsRequired(gltf, 'KHR_techniques_webgl'); } } ForEach.material(gltf, function (material) { if (defined(material.technique)) { var materialExtension = { technique: updatedTechniqueIndices[material.technique] }; ForEach.objectLegacy(material.values, function (value, parameterName) { if (!defined(materialExtension.values)) { materialExtension.values = {}; } var uniformName = mappedUniforms[parameterName]; materialExtension.values[uniformName] = value; }); if (!defined(material.extensions)) { material.extensions = {}; } material.extensions.KHR_techniques_webgl = materialExtension; } delete material.technique; delete material.values; }); delete gltf.techniques; delete gltf.programs; delete gltf.shaders; return gltf; } var allElementTypes = ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer']; /** * Removes unused elements from gltf. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {String[]} [elementTypes=['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer']] Element types to be removed. Needs to be a subset of ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'], other items will be ignored. * * @private */ function removeUnusedElements(gltf, elementTypes) { elementTypes = defaultValue(elementTypes, allElementTypes); allElementTypes.forEach(function(type) { if (elementTypes.indexOf(type) > -1) { removeUnusedElementsByType(gltf, type); } }); return gltf; } var TypeToGltfElementName = { accessor: 'accessors', buffer: 'buffers', bufferView: 'bufferViews', node: 'nodes', material: 'materials', mesh: 'meshes' }; function removeUnusedElementsByType(gltf, type) { var name = TypeToGltfElementName[type]; var arrayOfObjects = gltf[name]; if (defined(arrayOfObjects)) { var removed = 0; var usedIds = getListOfElementsIdsInUse[type](gltf); var length = arrayOfObjects.length; for (var i = 0; i < length; ++i) { if (!usedIds[i]) { Remove[type](gltf, i - removed); removed++; } } } } /** * Contains functions for removing elements from a glTF hierarchy. * Since top-level glTF elements are arrays, when something is removed, referring * indices need to be updated. * @constructor * * @private */ function Remove() {} Remove.accessor = function(gltf, accessorId) { var accessors = gltf.accessors; accessors.splice(accessorId, 1); ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { // Update accessor ids for the primitives. ForEach.meshPrimitiveAttribute(primitive, function(attributeAccessorId, semantic) { if (attributeAccessorId > accessorId) { primitive.attributes[semantic]--; } }); // Update accessor ids for the targets. ForEach.meshPrimitiveTarget(primitive, function(target) { ForEach.meshPrimitiveTargetAttribute(target, function(attributeAccessorId, semantic) { if (attributeAccessorId > accessorId) { target[semantic]--; } }); }); var indices = primitive.indices; if (defined(indices) && indices > accessorId) { primitive.indices--; } }); }); ForEach.skin(gltf, function(skin) { if (defined(skin.inverseBindMatrices) && skin.inverseBindMatrices > accessorId) { skin.inverseBindMatrices--; } }); ForEach.animation(gltf, function(animation) { ForEach.animationSampler(animation, function(sampler) { if (defined(sampler.input) && sampler.input > accessorId) { sampler.input--; } if (defined(sampler.output) && sampler.output > accessorId) { sampler.output--; } }); }); }; Remove.buffer = function(gltf, bufferId) { var buffers = gltf.buffers; buffers.splice(bufferId, 1); ForEach.bufferView(gltf, function(bufferView) { if (defined(bufferView.buffer) && bufferView.buffer > bufferId) { bufferView.buffer--; } }); }; Remove.bufferView = function(gltf, bufferViewId) { var bufferViews = gltf.bufferViews; bufferViews.splice(bufferViewId, 1); ForEach.accessor(gltf, function(accessor) { if (defined(accessor.bufferView) && accessor.bufferView > bufferViewId) { accessor.bufferView--; } }); ForEach.shader(gltf, function(shader) { if (defined(shader.bufferView) && shader.bufferView > bufferViewId) { shader.bufferView--; } }); ForEach.image(gltf, function(image) { if (defined(image.bufferView) && image.bufferView > bufferViewId) { image.bufferView--; } ForEach.compressedImage(image, function(compressedImage) { var compressedImageBufferView = compressedImage.bufferView; if (defined(compressedImageBufferView) && compressedImageBufferView > bufferViewId) { compressedImage.bufferView--; } }); }); if (hasExtension(gltf, 'KHR_draco_mesh_compression')) { ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.extensions) && defined(primitive.extensions.KHR_draco_mesh_compression)) { if (primitive.extensions.KHR_draco_mesh_compression.bufferView > bufferViewId) { primitive.extensions.KHR_draco_mesh_compression.bufferView--; } } }); }); } }; Remove.mesh = function(gltf, meshId) { var meshes = gltf.meshes; meshes.splice(meshId, 1); ForEach.node(gltf, function(node) { if (defined(node.mesh)) { if (node.mesh > meshId) { node.mesh--; } else if (node.mesh === meshId) { // Remove reference to deleted mesh delete node.mesh; } } }); }; Remove.node = function(gltf, nodeId) { var nodes = gltf.nodes; nodes.splice(nodeId, 1); // Shift all node references ForEach.skin(gltf, function(skin) { if (defined(skin.skeleton) && skin.skeleton > nodeId) { skin.skeleton--; } skin.joints = skin.joints.map(function(x) { return x > nodeId ? x - 1 : x; }); }); ForEach.animation(gltf, function(animation) { ForEach.animationChannel(animation, function(channel) { if (defined(channel.target) && defined(channel.target.node) && (channel.target.node > nodeId)) { channel.target.node--; } }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueUniform(technique, function(uniform) { if (defined(uniform.node) && uniform.node > nodeId) { uniform.node--; } }); }); ForEach.node(gltf, function(node) { if (!defined(node.children)) { return; } node.children = node.children .filter(function(x) { return x !== nodeId; // Remove }) .map(function(x) { return x > nodeId ? x - 1 : x; // Shift indices }); }); ForEach.scene(gltf, function(scene) { scene.nodes = scene.nodes .filter(function(x) { return x !== nodeId; // Remove }) .map(function(x) { return x > nodeId ? x - 1 : x; // Shift indices }); }); }; Remove.material = function(gltf, materialId) { var materials = gltf.materials; materials.splice(materialId, 1); // Shift other material ids ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.material) && primitive.material > materialId) { primitive.material--; } }); }); }; /** * Contains functions for getting a list of element ids in use by the glTF asset. * @constructor * * @private */ function getListOfElementsIdsInUse() {} getListOfElementsIdsInUse.accessor = function(gltf) { // Calculate accessor's that are currently in use. var usedAccessorIds = {}; ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { ForEach.meshPrimitiveAttribute(primitive, function(accessorId) { usedAccessorIds[accessorId] = true; }); ForEach.meshPrimitiveTarget(primitive, function(target) { ForEach.meshPrimitiveTargetAttribute(target, function(accessorId) { usedAccessorIds[accessorId] = true; }); }); var indices = primitive.indices; if (defined(indices)) { usedAccessorIds[indices] = true; } }); }); ForEach.skin(gltf, function(skin) { if (defined(skin.inverseBindMatrices)) { usedAccessorIds[skin.inverseBindMatrices] = true; } }); ForEach.animation(gltf, function(animation) { ForEach.animationSampler(animation, function(sampler) { if (defined(sampler.input)) { usedAccessorIds[sampler.input] = true; } if (defined(sampler.output)) { usedAccessorIds[sampler.output] = true; } }); }); return usedAccessorIds; }; getListOfElementsIdsInUse.buffer = function(gltf) { // Calculate buffer's that are currently in use. var usedBufferIds = {}; ForEach.bufferView(gltf, function(bufferView) { if (defined(bufferView.buffer)) { usedBufferIds[bufferView.buffer] = true; } }); return usedBufferIds; }; getListOfElementsIdsInUse.bufferView = function(gltf) { // Calculate bufferView's that are currently in use. var usedBufferViewIds = {}; ForEach.accessor(gltf, function(accessor) { if (defined(accessor.bufferView)) { usedBufferViewIds[accessor.bufferView] = true; } }); ForEach.shader(gltf, function(shader) { if (defined(shader.bufferView)) { usedBufferViewIds[shader.bufferView] = true; } }); ForEach.image(gltf, function(image) { if (defined(image.bufferView)) { usedBufferViewIds[image.bufferView] = true; } ForEach.compressedImage(image, function(compressedImage) { if (defined(compressedImage.bufferView)) { usedBufferViewIds[compressedImage.bufferView] = true; } }); }); if (hasExtension(gltf, 'KHR_draco_mesh_compression')) { ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.extensions) && defined(primitive.extensions.KHR_draco_mesh_compression)) { usedBufferViewIds[primitive.extensions.KHR_draco_mesh_compression.bufferView] = true; } }); }); } return usedBufferViewIds; }; getListOfElementsIdsInUse.mesh = function(gltf) { var usedMeshIds = {}; ForEach.node(gltf, function(node) { if (defined(node.mesh && defined(gltf.meshes))) { var mesh = gltf.meshes[node.mesh]; if (defined(mesh) && defined(mesh.primitives) && (mesh.primitives.length > 0)) { usedMeshIds[node.mesh] = true; } } }); return usedMeshIds; }; // Check if node is empty. It is considered empty if neither referencing // mesh, camera, extensions and has no children function nodeIsEmpty(gltf, node) { if (defined(node.mesh) || defined(node.camera) || defined(node.skin) || defined(node.weights) || defined(node.extras) || (defined(node.extensions) && node.extensions.length !== 0)) { return false; } // Empty if no children or children are all empty nodes return !defined(node.children) || node.children.filter(function(n) { return !nodeIsEmpty(gltf, gltf.nodes[n]); }).length === 0; } getListOfElementsIdsInUse.node = function(gltf) { var usedNodeIds = {}; ForEach.node(gltf, function(node, nodeId) { if (!nodeIsEmpty(gltf, node)) { usedNodeIds[nodeId] = true; } }); ForEach.skin(gltf, function(skin) { if (defined(skin.skeleton)) { usedNodeIds[skin.skeleton] = true; } ForEach.skinJoint(skin, function(joint) { usedNodeIds[joint] = true; }); }); ForEach.animation(gltf, function(animation) { ForEach.animationChannel(animation, function(channel) { if (defined(channel.target) && defined(channel.target.node)) { usedNodeIds[channel.target.node] = true; } }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueUniform(technique, function(uniform) { if (defined(uniform.node)) { usedNodeIds[uniform.node] = true; } }); }); return usedNodeIds; }; getListOfElementsIdsInUse.material = function(gltf) { var usedMaterialIds = {}; ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.material)) { usedMaterialIds[primitive.material] = true; } }); }); return usedMaterialIds; }; /** * Adds buffer to gltf. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {Buffer} buffer A Buffer object which will be added to gltf.buffers. * @returns {Number} The bufferView id of the newly added bufferView. * * @private */ function addBuffer(gltf, buffer) { var newBuffer = { byteLength: buffer.length, extras: { _pipeline: { source: buffer } } }; var bufferId = addToArray(gltf.buffers, newBuffer); var bufferView = { buffer: bufferId, byteOffset: 0, byteLength: buffer.length }; return addToArray(gltf.bufferViews, bufferView); } /** * Returns the accessor data in a contiguous array. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {Object} accessor The accessor. * @returns {Array} The accessor values in a contiguous array. * * @private */ function readAccessorPacked(gltf, accessor) { var byteStride = getAccessorByteStride(gltf, accessor); var componentTypeByteLength = ComponentDatatype$1.getSizeInBytes(accessor.componentType); var numberOfComponents = numberOfComponentsForType(accessor.type); var count = accessor.count; var values = new Array(numberOfComponents * count); if (!defined(accessor.bufferView)) { arrayFill(values, 0); return values; } var bufferView = gltf.bufferViews[accessor.bufferView]; var source = gltf.buffers[bufferView.buffer].extras._pipeline.source; var byteOffset = accessor.byteOffset + bufferView.byteOffset + source.byteOffset; var dataView = new DataView(source.buffer); var components = new Array(numberOfComponents); var componentReader = getComponentReader(accessor.componentType); for (var i = 0; i < count; ++i) { componentReader(dataView, byteOffset, numberOfComponents, componentTypeByteLength, components); for (var j = 0; j < numberOfComponents; ++j) { values[i * numberOfComponents + j] = components[j]; } byteOffset += byteStride; } return values; } /** * Update accessors referenced by JOINTS_0 and WEIGHTS_0 attributes to use correct component types. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The glTF asset with compressed meshes. * * @private */ function updateAccessorComponentTypes(gltf) { var componentType; ForEach.accessorWithSemantic(gltf, 'JOINTS_0', function(accessorId) { var accessor = gltf.accessors[accessorId]; componentType = accessor.componentType; if (componentType === WebGLConstants$1.BYTE) { convertType(gltf, accessor, ComponentDatatype$1.UNSIGNED_BYTE); } else if (componentType !== WebGLConstants$1.UNSIGNED_BYTE && componentType !== WebGLConstants$1.UNSIGNED_SHORT) { convertType(gltf, accessor, ComponentDatatype$1.UNSIGNED_SHORT); } }); ForEach.accessorWithSemantic(gltf, 'WEIGHTS_0', function(accessorId) { var accessor = gltf.accessors[accessorId]; componentType = accessor.componentType; if (componentType === WebGLConstants$1.BYTE) { convertType(gltf, accessor, ComponentDatatype$1.UNSIGNED_BYTE); } else if (componentType === WebGLConstants$1.SHORT) { convertType(gltf, accessor, ComponentDatatype$1.UNSIGNED_SHORT); } }); return gltf; } function convertType(gltf, accessor, updatedComponentType) { var typedArray = ComponentDatatype$1.createTypedArray(updatedComponentType, readAccessorPacked(gltf, accessor)); var newBuffer = new Uint8Array(typedArray.buffer); accessor.bufferView = addBuffer(gltf, newBuffer); accessor.componentType = updatedComponentType; accessor.byteOffset = 0; } var updateFunctions = { '0.8': glTF08to10, '1.0': glTF10to20, '2.0': undefined }; /** * Update the glTF version to the latest version (2.0), or targetVersion if specified. * Applies changes made to the glTF spec between revisions so that the core library * only has to handle the latest version. * * @param {Object} gltf A javascript object containing a glTF asset. * @param {Object} [options] Options for updating the glTF. * @param {String} [options.targetVersion] The glTF will be upgraded until it hits the specified version. * @returns {Object} The updated glTF asset. * * @private */ function updateVersion(gltf, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var targetVersion = options.targetVersion; var version = gltf.version; gltf.asset = defaultValue(gltf.asset, { version: '1.0' }); gltf.asset.version = defaultValue(gltf.asset.version, '1.0'); version = defaultValue(version, gltf.asset.version).toString(); // Invalid version if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) { // Try truncating trailing version numbers, could be a number as well if it is 0.8 if (defined(version)) { version = version.substring(0, 3); } // Default to 1.0 if it cannot be determined if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) { version = '1.0'; } } var updateFunction = updateFunctions[version]; while (defined(updateFunction)) { if (version === targetVersion) { break; } updateFunction(gltf, options); version = gltf.asset.version; updateFunction = updateFunctions[version]; } return gltf; } function updateInstanceTechniques(gltf) { var materials = gltf.materials; for (var materialId in materials) { if (Object.prototype.hasOwnProperty.call(materials, materialId)) { var material = materials[materialId]; var instanceTechnique = material.instanceTechnique; if (defined(instanceTechnique)) { material.technique = instanceTechnique.technique; material.values = instanceTechnique.values; delete material.instanceTechnique; } } } } function setPrimitiveModes(gltf) { var meshes = gltf.meshes; for (var meshId in meshes) { if (Object.prototype.hasOwnProperty.call(meshes, meshId)) { var mesh = meshes[meshId]; var primitives = mesh.primitives; if (defined(primitives)) { var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { var primitive = primitives[i]; var defaultMode = defaultValue(primitive.primitive, WebGLConstants$1.TRIANGLES); primitive.mode = defaultValue(primitive.mode, defaultMode); delete primitive.primitive; } } } } } function updateNodes(gltf) { var nodes = gltf.nodes; var axis = new Cartesian3(); var quat = new Quaternion(); for (var nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) { var node = nodes[nodeId]; if (defined(node.rotation)) { var rotation = node.rotation; Cartesian3.fromArray(rotation, 0, axis); Quaternion.fromAxisAngle(axis, rotation[3], quat); node.rotation = [quat.x, quat.y, quat.z, quat.w]; } var instanceSkin = node.instanceSkin; if (defined(instanceSkin)) { node.skeletons = instanceSkin.skeletons; node.skin = instanceSkin.skin; node.meshes = instanceSkin.meshes; delete node.instanceSkin; } } } } function updateAnimations(gltf) { var animations = gltf.animations; var accessors = gltf.accessors; var bufferViews = gltf.bufferViews; var buffers = gltf.buffers; var updatedAccessors = {}; var axis = new Cartesian3(); var quat = new Quaternion(); for (var animationId in animations) { if (Object.prototype.hasOwnProperty.call(animations, animationId)) { var animation = animations[animationId]; var channels = animation.channels; var parameters = animation.parameters; var samplers = animation.samplers; if (defined(channels)) { var channelsLength = channels.length; for (var i = 0; i < channelsLength; ++i) { var channel = channels[i]; if (channel.target.path === 'rotation') { var accessorId = parameters[samplers[channel.sampler].output]; if (defined(updatedAccessors[accessorId])) { continue; } updatedAccessors[accessorId] = true; var accessor = accessors[accessorId]; var bufferView = bufferViews[accessor.bufferView]; var buffer = buffers[bufferView.buffer]; var source = buffer.extras._pipeline.source; var byteOffset = source.byteOffset + bufferView.byteOffset + accessor.byteOffset; var componentType = accessor.componentType; var count = accessor.count; var componentsLength = numberOfComponentsForType(accessor.type); var length = accessor.count * componentsLength; var typedArray = ComponentDatatype$1.createArrayBufferView(componentType, source.buffer, byteOffset, length); for (var j = 0; j < count; j++) { var offset = j * componentsLength; Cartesian3.unpack(typedArray, offset, axis); var angle = typedArray[offset + 3]; Quaternion.fromAxisAngle(axis, angle, quat); Quaternion.pack(quat, typedArray, offset); } } } } } } } function removeTechniquePasses(gltf) { var techniques = gltf.techniques; for (var techniqueId in techniques) { if (Object.prototype.hasOwnProperty.call(techniques, techniqueId)) { var technique = techniques[techniqueId]; var passes = technique.passes; if (defined(passes)) { var passName = defaultValue(technique.pass, 'defaultPass'); if (Object.prototype.hasOwnProperty.call(passes, passName)) { var pass = passes[passName]; var instanceProgram = pass.instanceProgram; technique.attributes = defaultValue(technique.attributes, instanceProgram.attributes); technique.program = defaultValue(technique.program, instanceProgram.program); technique.uniforms = defaultValue(technique.uniforms, instanceProgram.uniforms); technique.states = defaultValue(technique.states, pass.states); } delete technique.passes; delete technique.pass; } } } } function glTF08to10(gltf) { if (!defined(gltf.asset)) { gltf.asset = {}; } var asset = gltf.asset; asset.version = '1.0'; // Profile should be an object, not a string if (typeof asset.profile === 'string') { var split = asset.profile.split(' '); asset.profile = { api: split[0], version: split[1] }; } else { asset.profile = {}; } // Version property should be in asset, not on the root element if (defined(gltf.version)) { delete gltf.version; } // material.instanceTechnique properties should be directly on the material updateInstanceTechniques(gltf); // primitive.primitive should be primitive.mode setPrimitiveModes(gltf); // Node rotation should be quaternion, not axis-angle // node.instanceSkin is deprecated updateNodes(gltf); // Animations that target rotations should be quaternion, not axis-angle updateAnimations(gltf); // technique.pass and techniques.passes are deprecated removeTechniquePasses(gltf); // gltf.allExtensions -> extensionsUsed if (defined(gltf.allExtensions)) { gltf.extensionsUsed = gltf.allExtensions; delete gltf.allExtensions; } // gltf.lights -> khrMaterialsCommon.lights if (defined(gltf.lights)) { var extensions = defaultValue(gltf.extensions, {}); gltf.extensions = extensions; var materialsCommon = defaultValue(extensions.KHR_materials_common, {}); extensions.KHR_materials_common = materialsCommon; materialsCommon.lights = gltf.lights; delete gltf.lights; addExtensionsUsed(gltf, 'KHR_materials_common'); } } function removeAnimationSamplersIndirection(gltf) { var animations = gltf.animations; for (var animationId in animations) { if (Object.prototype.hasOwnProperty.call(animations, animationId)) { var animation = animations[animationId]; var parameters = animation.parameters; if (defined(parameters)) { var samplers = animation.samplers; for (var samplerId in samplers) { if (Object.prototype.hasOwnProperty.call(samplers, samplerId)) { var sampler = samplers[samplerId]; sampler.input = parameters[sampler.input]; sampler.output = parameters[sampler.output]; } } delete animation.parameters; } } } } function objectToArray(object, mapping) { var array = []; for (var id in object) { if (Object.prototype.hasOwnProperty.call(object, id)) { var value = object[id]; mapping[id] = array.length; array.push(value); if (!defined(value.name)) { value.name = id; } } } return array; } function objectsToArrays(gltf) { var i; var globalMapping = { accessors: {}, animations: {}, buffers: {}, bufferViews: {}, cameras: {}, images: {}, materials: {}, meshes: {}, nodes: {}, programs: {}, samplers: {}, scenes: {}, shaders: {}, skins: {}, textures: {}, techniques: {} }; // Map joint names to id names var jointName; var jointNameToId = {}; var nodes = gltf.nodes; for (var id in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, id)) { jointName = nodes[id].jointName; if (defined(jointName)) { jointNameToId[jointName] = id; } } } // Convert top level objects to arrays for (var topLevelId in gltf) { if (Object.prototype.hasOwnProperty.call(gltf, topLevelId) && defined(globalMapping[topLevelId])) { var objectMapping = {}; var object = gltf[topLevelId]; gltf[topLevelId] = objectToArray(object, objectMapping); globalMapping[topLevelId] = objectMapping; } } // Remap joint names to array indexes for (jointName in jointNameToId) { if (Object.prototype.hasOwnProperty.call(jointNameToId, jointName)) { jointNameToId[jointName] = globalMapping.nodes[jointNameToId[jointName]]; } } // Fix references if (defined(gltf.scene)) { gltf.scene = globalMapping.scenes[gltf.scene]; } ForEach.bufferView(gltf, function(bufferView) { if (defined(bufferView.buffer)) { bufferView.buffer = globalMapping.buffers[bufferView.buffer]; } }); ForEach.accessor(gltf, function(accessor) { if (defined(accessor.bufferView)) { accessor.bufferView = globalMapping.bufferViews[accessor.bufferView]; } }); ForEach.shader(gltf, function(shader) { var extensions = shader.extensions; if (defined(extensions)) { var binaryGltf = extensions.KHR_binary_glTF; if (defined(binaryGltf)) { shader.bufferView = globalMapping.bufferViews[binaryGltf.bufferView]; delete extensions.KHR_binary_glTF; } if (Object.keys(extensions).length === 0) { delete shader.extensions; } } }); ForEach.program(gltf, function(program) { if (defined(program.vertexShader)) { program.vertexShader = globalMapping.shaders[program.vertexShader]; } if (defined(program.fragmentShader)) { program.fragmentShader = globalMapping.shaders[program.fragmentShader]; } }); ForEach.technique(gltf, function(technique) { if (defined(technique.program)) { technique.program = globalMapping.programs[technique.program]; } ForEach.techniqueParameter(technique, function(parameter) { if (defined(parameter.node)) { parameter.node = globalMapping.nodes[parameter.node]; } var value = parameter.value; if (typeof value === 'string') { parameter.value = { index: globalMapping.textures[value] }; } }); }); ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { if (defined(primitive.indices)) { primitive.indices = globalMapping.accessors[primitive.indices]; } ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) { primitive.attributes[semantic] = globalMapping.accessors[accessorId]; }); if (defined(primitive.material)) { primitive.material = globalMapping.materials[primitive.material]; } }); }); ForEach.node(gltf, function(node) { var children = node.children; if (defined(children)) { var childrenLength = children.length; for (i = 0; i < childrenLength; ++i) { children[i] = globalMapping.nodes[children[i]]; } } if (defined(node.meshes)) { // Split out meshes on nodes var meshes = node.meshes; var meshesLength = meshes.length; if (meshesLength > 0) { node.mesh = globalMapping.meshes[meshes[0]]; for (i = 1; i < meshesLength; ++i) { var meshNode = { mesh: globalMapping.meshes[meshes[i]] }; var meshNodeId = addToArray(gltf.nodes, meshNode); if (!defined(children)) { children = []; node.children = children; } children.push(meshNodeId); } } delete node.meshes; } if (defined(node.camera)) { node.camera = globalMapping.cameras[node.camera]; } if (defined(node.skin)) { node.skin = globalMapping.skins[node.skin]; } if (defined(node.skeletons)) { // Assign skeletons to skins var skeletons = node.skeletons; var skeletonsLength = skeletons.length; if ((skeletonsLength > 0) && defined(node.skin)) { var skin = gltf.skins[node.skin]; skin.skeleton = globalMapping.nodes[skeletons[0]]; } delete node.skeletons; } if (defined(node.jointName)) { delete node.jointName; } }); ForEach.skin(gltf, function(skin) { if (defined(skin.inverseBindMatrices)) { skin.inverseBindMatrices = globalMapping.accessors[skin.inverseBindMatrices]; } var jointNames = skin.jointNames; if (defined(jointNames)) { var joints = []; var jointNamesLength = jointNames.length; for (i = 0; i < jointNamesLength; ++i) { joints[i] = jointNameToId[jointNames[i]]; } skin.joints = joints; delete skin.jointNames; } }); ForEach.scene(gltf, function(scene) { var sceneNodes = scene.nodes; if (defined(sceneNodes)) { var sceneNodesLength = sceneNodes.length; for (i = 0; i < sceneNodesLength; ++i) { sceneNodes[i] = globalMapping.nodes[sceneNodes[i]]; } } }); ForEach.animation(gltf, function(animation) { var samplerMapping = {}; animation.samplers = objectToArray(animation.samplers, samplerMapping); ForEach.animationSampler(animation, function(sampler) { sampler.input = globalMapping.accessors[sampler.input]; sampler.output = globalMapping.accessors[sampler.output]; }); ForEach.animationChannel(animation, function(channel) { channel.sampler = samplerMapping[channel.sampler]; var target = channel.target; if (defined(target)) { target.node = globalMapping.nodes[target.id]; delete target.id; } }); }); ForEach.material(gltf, function(material) { if (defined(material.technique)) { material.technique = globalMapping.techniques[material.technique]; } ForEach.materialValue(material, function(value, name) { if (typeof value === 'string') { material.values[name] = { index: globalMapping.textures[value] }; } }); var extensions = material.extensions; if (defined(extensions)) { var materialsCommon = extensions.KHR_materials_common; if (defined(materialsCommon)) { ForEach.materialValue(materialsCommon, function(value, name) { if (typeof value === 'string') { materialsCommon.values[name] = { index: globalMapping.textures[value] }; } }); } } }); ForEach.image(gltf, function(image) { var extensions = image.extensions; if (defined(extensions)) { var binaryGltf = extensions.KHR_binary_glTF; if (defined(binaryGltf)) { image.bufferView = globalMapping.bufferViews[binaryGltf.bufferView]; image.mimeType = binaryGltf.mimeType; delete extensions.KHR_binary_glTF; } if (Object.keys(extensions).length === 0) { delete image.extensions; } } ForEach.compressedImage(image, function(compressedImage) { var compressedExtensions = compressedImage.extensions; if (defined(compressedExtensions)) { var compressedBinaryGltf = compressedExtensions.KHR_binary_glTF; if (defined(compressedBinaryGltf)) { compressedImage.bufferView = globalMapping.bufferViews[compressedBinaryGltf.bufferView]; compressedImage.mimeType = compressedBinaryGltf.mimeType; delete compressedExtensions.KHR_binary_glTF; } if (Object.keys(extensions).length === 0) { delete compressedImage.extensions; } } }); }); ForEach.texture(gltf, function(texture) { if (defined(texture.sampler)) { texture.sampler = globalMapping.samplers[texture.sampler]; } if (defined(texture.source)) { texture.source = globalMapping.images[texture.source]; } }); } function removeAnimationSamplerNames(gltf) { ForEach.animation(gltf, function(animation) { ForEach.animationSampler(animation, function(sampler) { delete sampler.name; }); }); } function removeEmptyArrays(gltf) { for (var topLevelId in gltf) { if (Object.prototype.hasOwnProperty.call(gltf, topLevelId)) { var array = gltf[topLevelId]; if (Array.isArray(array) && array.length === 0) { delete gltf[topLevelId]; } } } ForEach.node(gltf, function(node) { if (defined(node.children) && node.children.length === 0) { delete node.children; } }); } function stripAsset(gltf) { var asset = gltf.asset; delete asset.profile; delete asset.premultipliedAlpha; } var knownExtensions = { CESIUM_RTC: true, KHR_materials_common: true, WEB3D_quantized_attributes: true }; function requireKnownExtensions(gltf) { var extensionsUsed = gltf.extensionsUsed; gltf.extensionsRequired = defaultValue(gltf.extensionsRequired, []); if (defined(extensionsUsed)) { var extensionsUsedLength = extensionsUsed.length; for (var i = 0; i < extensionsUsedLength; ++i) { var extension = extensionsUsed[i]; if (defined(knownExtensions[extension])) { gltf.extensionsRequired.push(extension); } } } } function removeBufferType(gltf) { ForEach.buffer(gltf, function(buffer) { delete buffer.type; }); } function removeTextureProperties(gltf) { ForEach.texture(gltf, function(texture) { delete texture.format; delete texture.internalFormat; delete texture.target; delete texture.type; }); } function requireAttributeSetIndex(gltf) { ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) { if (semantic === 'TEXCOORD') { primitive.attributes.TEXCOORD_0 = accessorId; } else if (semantic === 'COLOR') { primitive.attributes.COLOR_0 = accessorId; } }); delete primitive.attributes.TEXCOORD; delete primitive.attributes.COLOR; }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueParameter(technique, function(parameter) { var semantic = parameter.semantic; if (defined(semantic)) { if (semantic === 'TEXCOORD') { parameter.semantic = 'TEXCOORD_0'; } else if (semantic === 'COLOR') { parameter.semantic = 'COLOR_0'; } } }); }); } var knownSemantics = { POSITION: true, NORMAL: true, TANGENT: true }; var indexedSemantics = { COLOR: 'COLOR', JOINT : 'JOINTS', JOINTS: 'JOINTS', TEXCOORD: 'TEXCOORD', WEIGHT: 'WEIGHTS', WEIGHTS: 'WEIGHTS' }; function underscoreApplicationSpecificSemantics(gltf) { var mappedSemantics = {}; ForEach.mesh(gltf, function(mesh) { ForEach.meshPrimitive(mesh, function(primitive) { /*eslint-disable no-unused-vars*/ ForEach.meshPrimitiveAttribute(primitive, function(accessorId, semantic) { if (semantic.charAt(0) !== '_') { var setIndex = semantic.search(/_[0-9]+/g); var strippedSemantic = semantic; var suffix = '_0'; if (setIndex >= 0) { strippedSemantic = semantic.substring(0, setIndex); suffix = semantic.substring(setIndex); } var newSemantic; var indexedSemantic = indexedSemantics[strippedSemantic]; if (defined(indexedSemantic)) { newSemantic = indexedSemantic + suffix; mappedSemantics[semantic] = newSemantic; } else if (!defined(knownSemantics[strippedSemantic])) { newSemantic = '_' + semantic; mappedSemantics[semantic] = newSemantic; } } }); for (var semantic in mappedSemantics) { if (Object.prototype.hasOwnProperty.call(mappedSemantics, semantic)) { var mappedSemantic = mappedSemantics[semantic]; var accessorId = primitive.attributes[semantic]; if (defined(accessorId)) { delete primitive.attributes[semantic]; primitive.attributes[mappedSemantic] = accessorId; } } } }); }); ForEach.technique(gltf, function(technique) { ForEach.techniqueParameter(technique, function(parameter) { var mappedSemantic = mappedSemantics[parameter.semantic]; if (defined(mappedSemantic)) { parameter.semantic = mappedSemantic; } }); }); } function clampCameraParameters(gltf) { ForEach.camera(gltf, function(camera) { var perspective = camera.perspective; if (defined(perspective)) { var aspectRatio = perspective.aspectRatio; if (defined(aspectRatio) && aspectRatio === 0.0) { delete perspective.aspectRatio; } var yfov = perspective.yfov; if (defined(yfov) && yfov === 0.0) { perspective.yfov = 1.0; } } }); } function computeAccessorByteStride(gltf, accessor) { return (defined(accessor.byteStride) && accessor.byteStride !== 0) ? accessor.byteStride : getAccessorByteStride(gltf, accessor); } function requireByteLength(gltf) { ForEach.buffer(gltf, function(buffer) { if (!defined(buffer.byteLength)) { buffer.byteLength = buffer.extras._pipeline.source.length; } }); ForEach.accessor(gltf, function(accessor) { var bufferViewId = accessor.bufferView; if (defined(bufferViewId)) { var bufferView = gltf.bufferViews[bufferViewId]; var accessorByteStride = computeAccessorByteStride(gltf, accessor); var accessorByteEnd = accessor.byteOffset + accessor.count * accessorByteStride; bufferView.byteLength = Math.max(defaultValue(bufferView.byteLength, 0), accessorByteEnd); } }); } function moveByteStrideToBufferView(gltf) { var i; var j; var bufferView; var bufferViews = gltf.bufferViews; var bufferViewHasVertexAttributes = {}; ForEach.accessorContainingVertexAttributeData(gltf, function(accessorId) { var accessor = gltf.accessors[accessorId]; if (defined(accessor.bufferView)) { bufferViewHasVertexAttributes[accessor.bufferView] = true; } }); // Map buffer views to a list of accessors var bufferViewMap = {}; ForEach.accessor(gltf, function(accessor) { if (defined(accessor.bufferView)) { bufferViewMap[accessor.bufferView] = defaultValue(bufferViewMap[accessor.bufferView], []); bufferViewMap[accessor.bufferView].push(accessor); } }); // Split accessors with different byte strides for (var bufferViewId in bufferViewMap) { if (Object.prototype.hasOwnProperty.call(bufferViewMap, bufferViewId)) { bufferView = bufferViews[bufferViewId]; var accessors = bufferViewMap[bufferViewId]; accessors.sort(function(a, b) { return a.byteOffset - b.byteOffset; }); var currentByteOffset = 0; var currentIndex = 0; var accessorsLength = accessors.length; for (i = 0; i < accessorsLength; ++i) { var accessor = accessors[i]; var accessorByteStride = computeAccessorByteStride(gltf, accessor); var accessorByteOffset = accessor.byteOffset; var accessorByteLength = accessor.count * accessorByteStride; delete accessor.byteStride; var hasNextAccessor = (i < accessorsLength - 1); var nextAccessorByteStride = hasNextAccessor ? computeAccessorByteStride(gltf, accessors[i + 1]) : undefined; if (accessorByteStride !== nextAccessorByteStride) { var newBufferView = clone$1(bufferView, true); if (bufferViewHasVertexAttributes[bufferViewId]) { newBufferView.byteStride = accessorByteStride; } newBufferView.byteOffset += currentByteOffset; newBufferView.byteLength = accessorByteOffset + accessorByteLength - currentByteOffset; var newBufferViewId = addToArray(bufferViews, newBufferView); for (j = currentIndex; j <= i; ++j) { accessor = accessors[j]; accessor.bufferView = newBufferViewId; accessor.byteOffset = accessor.byteOffset - currentByteOffset; } // Set current byte offset to next accessor's byte offset currentByteOffset = hasNextAccessor ? accessors[i + 1].byteOffset : undefined; currentIndex = i + 1; } } } } // Remove unused buffer views removeUnusedElements(gltf, ['accessor', 'bufferView', 'buffer']); } function requirePositionAccessorMinMax(gltf) { ForEach.accessorWithSemantic(gltf, 'POSITION', function(accessorId) { var accessor = gltf.accessors[accessorId]; if (!defined(accessor.min) || !defined(accessor.max)) { var minMax = findAccessorMinMax(gltf, accessor); accessor.min = minMax.min; accessor.max = minMax.max; } }); } function isNodeEmpty(node) { return (!defined(node.children) || node.children.length === 0) && (!defined(node.meshes) || node.meshes.length === 0) && !defined(node.camera) && !defined(node.skin) && !defined(node.skeletons) && !defined(node.jointName) && (!defined(node.translation) || Cartesian3.fromArray(node.translation).equals(Cartesian3.ZERO)) && (!defined(node.scale) || Cartesian3.fromArray(node.scale).equals(new Cartesian3(1.0, 1.0, 1.0))) && (!defined(node.rotation) || Cartesian4.fromArray(node.rotation).equals(new Cartesian4(0.0, 0.0, 0.0, 1.0))) && (!defined(node.matrix) || Matrix4.fromColumnMajorArray(node.matrix).equals(Matrix4.IDENTITY)) && !defined(node.extensions) && !defined(node.extras); } function deleteNode(gltf, nodeId) { // Remove from list of nodes in scene ForEach.scene(gltf, function(scene) { var sceneNodes = scene.nodes; if (defined(sceneNodes)) { var sceneNodesLength = sceneNodes.length; for (var i = sceneNodesLength; i >= 0; --i) { if (sceneNodes[i] === nodeId) { sceneNodes.splice(i, 1); return; } } } }); // Remove parent node's reference to this node, and delete the parent if also empty ForEach.node(gltf, function(parentNode, parentNodeId) { if (defined(parentNode.children)) { var index = parentNode.children.indexOf(nodeId); if (index > -1) { parentNode.children.splice(index, 1); if (isNodeEmpty(parentNode)) { deleteNode(gltf, parentNodeId); } } } }); delete gltf.nodes[nodeId]; } function removeEmptyNodes(gltf) { ForEach.node(gltf, function(node, nodeId) { if (isNodeEmpty(node)) { deleteNode(gltf, nodeId); } }); return gltf; } function requireAnimationAccessorMinMax(gltf) { ForEach.animation(gltf, function(animation) { ForEach.animationSampler(animation, function(sampler) { var accessor = gltf.accessors[sampler.input]; if (!defined(accessor.min) || !defined(accessor.max)) { var minMax = findAccessorMinMax(gltf, accessor); accessor.min = minMax.min; accessor.max = minMax.max; } }); }); } function glTF10to20(gltf) { gltf.asset = defaultValue(gltf.asset, {}); gltf.asset.version = '2.0'; // material.instanceTechnique properties should be directly on the material. instanceTechnique is a gltf 0.8 property but is seen in some 1.0 models. updateInstanceTechniques(gltf); // animation.samplers now refers directly to accessors and animation.parameters should be removed removeAnimationSamplersIndirection(gltf); // Remove empty nodes and re-assign referencing indices removeEmptyNodes(gltf); // Top-level objects are now arrays referenced by index instead of id objectsToArrays(gltf); // Animation.sampler objects cannot have names removeAnimationSamplerNames(gltf); // asset.profile no longer exists stripAsset(gltf); // Move known extensions from extensionsUsed to extensionsRequired requireKnownExtensions(gltf); // bufferView.byteLength and buffer.byteLength are required requireByteLength(gltf); // byteStride moved from accessor to bufferView moveByteStrideToBufferView(gltf); // accessor.min and accessor.max must be defined for accessors containing POSITION attributes requirePositionAccessorMinMax(gltf); // An animation sampler's input accessor must have min and max properties defined requireAnimationAccessorMinMax(gltf); // buffer.type is unnecessary and should be removed removeBufferType(gltf); // Remove format, internalFormat, target, and type removeTextureProperties(gltf); // TEXCOORD and COLOR attributes must be written with a set index (TEXCOORD_#) requireAttributeSetIndex(gltf); // Add underscores to application-specific parameters underscoreApplicationSpecificSemantics(gltf); // Accessors referenced by JOINTS_0 and WEIGHTS_0 attributes must have correct component types updateAccessorComponentTypes(gltf); // Clamp camera parameters clampCameraParameters(gltf); // Move legacy technique render states to material properties and add KHR_blend extension blending functions moveTechniqueRenderStates(gltf); // Add material techniques to KHR_techniques_webgl extension, removing shaders, programs, and techniques moveTechniquesToExtension(gltf); // Remove empty arrays removeEmptyArrays(gltf); } /** * @private */ function ModelLoadResources() { this.initialized = false; this.resourcesParsed = false; this.vertexBuffersToCreate = new Queue(); this.indexBuffersToCreate = new Queue(); this.buffers = {}; this.pendingBufferLoads = 0; this.programsToCreate = new Queue(); this.shaders = {}; this.pendingShaderLoads = 0; this.texturesToCreate = new Queue(); this.pendingTextureLoads = 0; this.texturesToCreateFromBufferView = new Queue(); this.pendingBufferViewToImage = 0; this.createSamplers = true; this.createSkins = true; this.createRuntimeAnimations = true; this.createVertexArrays = true; this.createRenderStates = true; this.createUniformMaps = true; this.createRuntimeNodes = true; this.createdBufferViews = {}; this.primitivesToDecode = new Queue(); this.activeDecodingTasks = 0; this.pendingDecodingCache = false; this.skinnedNodesIds = []; } /** * This function differs from the normal subarray function * because it takes offset and length, rather than begin and end. * @private */ function getSubarray(array, offset, length) { return array.subarray(offset, offset + length); } ModelLoadResources.prototype.getBuffer = function (bufferView) { return getSubarray( this.buffers[bufferView.buffer], bufferView.byteOffset, bufferView.byteLength ); }; ModelLoadResources.prototype.finishedPendingBufferLoads = function () { return this.pendingBufferLoads === 0; }; ModelLoadResources.prototype.finishedBuffersCreation = function () { return ( this.pendingBufferLoads === 0 && this.vertexBuffersToCreate.length === 0 && this.indexBuffersToCreate.length === 0 ); }; ModelLoadResources.prototype.finishedProgramCreation = function () { return this.pendingShaderLoads === 0 && this.programsToCreate.length === 0; }; ModelLoadResources.prototype.finishedTextureCreation = function () { var finishedPendingLoads = this.pendingTextureLoads === 0; var finishedResourceCreation = this.texturesToCreate.length === 0 && this.texturesToCreateFromBufferView.length === 0; return finishedPendingLoads && finishedResourceCreation; }; ModelLoadResources.prototype.finishedEverythingButTextureCreation = function () { var finishedPendingLoads = this.pendingBufferLoads === 0 && this.pendingShaderLoads === 0; var finishedResourceCreation = this.vertexBuffersToCreate.length === 0 && this.indexBuffersToCreate.length === 0 && this.programsToCreate.length === 0 && this.pendingBufferViewToImage === 0; return ( this.finishedDecoding() && finishedPendingLoads && finishedResourceCreation ); }; ModelLoadResources.prototype.finishedDecoding = function () { return ( this.primitivesToDecode.length === 0 && this.activeDecodingTasks === 0 && !this.pendingDecodingCache ); }; ModelLoadResources.prototype.finished = function () { return ( this.finishedDecoding() && this.finishedTextureCreation() && this.finishedEverythingButTextureCreation() ); }; /** * @private */ var ModelUtility = {}; /** * Updates the model's forward axis if the model is not a 2.0 model. * * @param {Object} model The model to update. */ ModelUtility.updateForwardAxis = function (model) { var cachedSourceVersion = model.gltf.extras.sourceVersion; if ( (defined(cachedSourceVersion) && cachedSourceVersion !== "2.0") || ModelUtility.getAssetVersion(model.gltf) !== "2.0" ) { model._gltfForwardAxis = Axis$1.X; } }; /** * Gets the string representing the glTF asset version. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {String} The glTF asset version string. */ ModelUtility.getAssetVersion = function (gltf) { // In glTF 1.0 it was valid to omit the version number. if (!defined(gltf.asset) || !defined(gltf.asset.version)) { return "1.0"; } return gltf.asset.version; }; /** * Splits primitive materials with values incompatible for generating techniques. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The glTF asset with modified materials. */ ModelUtility.splitIncompatibleMaterials = function (gltf) { var accessors = gltf.accessors; var materials = gltf.materials; var primitiveInfoByMaterial = {}; ForEach.mesh(gltf, function (mesh) { ForEach.meshPrimitive(mesh, function (primitive) { var materialIndex = primitive.material; var material = materials[materialIndex]; var jointAccessorId = primitive.attributes.JOINTS_0; var componentType; var accessorType; if (defined(jointAccessorId)) { var jointAccessor = accessors[jointAccessorId]; componentType = jointAccessor.componentType; accessorType = jointAccessor.type; } var isSkinned = defined(jointAccessorId) && accessorType === "VEC4"; var hasVertexColors = defined(primitive.attributes.COLOR_0); var hasMorphTargets = defined(primitive.targets); var hasNormals = defined(primitive.attributes.NORMAL); var hasTangents = defined(primitive.attributes.TANGENT); var hasTexCoords = defined(primitive.attributes.TEXCOORD_0); var hasTexCoord1 = hasTexCoords && defined(primitive.attributes.TEXCOORD_1); var hasOutline = defined(primitive.extensions) && defined(primitive.extensions.CESIUM_primitive_outline); var primitiveInfo = primitiveInfoByMaterial[materialIndex]; if (!defined(primitiveInfo)) { primitiveInfoByMaterial[materialIndex] = { skinning: { skinned: isSkinned, componentType: componentType, }, hasVertexColors: hasVertexColors, hasMorphTargets: hasMorphTargets, hasNormals: hasNormals, hasTangents: hasTangents, hasTexCoords: hasTexCoords, hasTexCoord1: hasTexCoord1, hasOutline: hasOutline, }; } else if ( primitiveInfo.skinning.skinned !== isSkinned || primitiveInfo.hasVertexColors !== hasVertexColors || primitiveInfo.hasMorphTargets !== hasMorphTargets || primitiveInfo.hasNormals !== hasNormals || primitiveInfo.hasTangents !== hasTangents || primitiveInfo.hasTexCoords !== hasTexCoords || primitiveInfo.hasTexCoord1 !== hasTexCoord1 || primitiveInfo.hasOutline !== hasOutline ) { // This primitive uses the same material as another one that either: // * Isn't skinned // * Uses a different type to store joints and weights // * Doesn't have vertex colors, morph targets, normals, tangents, or texCoords // * Doesn't have a CESIUM_primitive_outline extension. var clonedMaterial = clone$1(material, true); // Split this off as a separate material materialIndex = addToArray(materials, clonedMaterial); primitive.material = materialIndex; primitiveInfoByMaterial[materialIndex] = { skinning: { skinned: isSkinned, componentType: componentType, }, hasVertexColors: hasVertexColors, hasMorphTargets: hasMorphTargets, hasNormals: hasNormals, hasTangents: hasTangents, hasTexCoords: hasTexCoords, hasTexCoord1: hasTexCoord1, hasOutline: hasOutline, }; } }); }); return primitiveInfoByMaterial; }; ModelUtility.getShaderVariable = function (type) { if (type === "SCALAR") { return "float"; } return type.toLowerCase(); }; ModelUtility.ModelState = { NEEDS_LOAD: 0, LOADING: 1, LOADED: 2, // Renderable, but textures can still be pending when incrementallyLoadTextures is true. FAILED: 3, }; ModelUtility.getFailedLoadFunction = function (model, type, path) { return function (error) { model._state = ModelUtility.ModelState.FAILED; var message = "Failed to load " + type + ": " + path; if (defined(error)) { message += "\n" + error.message; } model._readyPromise.reject(new RuntimeError(message)); }; }; ModelUtility.parseBuffers = function (model, bufferLoad) { var loadResources = model._loadResources; ForEach.buffer(model.gltf, function (buffer, bufferViewId) { if (defined(buffer.extras._pipeline.source)) { loadResources.buffers[bufferViewId] = buffer.extras._pipeline.source; } else if (defined(bufferLoad)) { var bufferResource = model._resource.getDerivedResource({ url: buffer.uri, }); ++loadResources.pendingBufferLoads; bufferResource .fetchArrayBuffer() .then(bufferLoad(model, bufferViewId)) .otherwise( ModelUtility.getFailedLoadFunction( model, "buffer", bufferResource.url ) ); } }); }; var aMinScratch = new Cartesian3(); var aMaxScratch = new Cartesian3(); ModelUtility.computeBoundingSphere = function (model) { var gltf = model.gltf; var gltfNodes = gltf.nodes; var gltfMeshes = gltf.meshes; var rootNodes = gltf.scenes[gltf.scene].nodes; var rootNodesLength = rootNodes.length; var nodeStack = []; var min = new Cartesian3( Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE ); var max = new Cartesian3( -Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE ); for (var i = 0; i < rootNodesLength; ++i) { var n = gltfNodes[rootNodes[i]]; n._transformToRoot = ModelUtility.getTransform(n); nodeStack.push(n); while (nodeStack.length > 0) { n = nodeStack.pop(); var transformToRoot = n._transformToRoot; var meshId = n.mesh; if (defined(meshId)) { var mesh = gltfMeshes[meshId]; var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var m = 0; m < primitivesLength; ++m) { var positionAccessor = primitives[m].attributes.POSITION; if (defined(positionAccessor)) { var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor); if (defined(minMax.min) && defined(minMax.max)) { var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch); var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch); Matrix4.multiplyByPoint(transformToRoot, aMin, aMin); Matrix4.multiplyByPoint(transformToRoot, aMax, aMax); Cartesian3.minimumByComponent(min, aMin, min); Cartesian3.maximumByComponent(max, aMax, max); } } } } var children = n.children; if (defined(children)) { var childrenLength = children.length; for (var k = 0; k < childrenLength; ++k) { var child = gltfNodes[children[k]]; child._transformToRoot = ModelUtility.getTransform(child); Matrix4.multiplyTransformation( transformToRoot, child._transformToRoot, child._transformToRoot ); nodeStack.push(child); } } delete n._transformToRoot; } } var boundingSphere = BoundingSphere.fromCornerPoints(min, max); if (model._forwardAxis === Axis$1.Z) { // glTF 2.0 has a Z-forward convention that must be adapted here to X-forward. BoundingSphere.transformWithoutScale( boundingSphere, Axis$1.Z_UP_TO_X_UP, boundingSphere ); } if (model._upAxis === Axis$1.Y) { BoundingSphere.transformWithoutScale( boundingSphere, Axis$1.Y_UP_TO_Z_UP, boundingSphere ); } else if (model._upAxis === Axis$1.X) { BoundingSphere.transformWithoutScale( boundingSphere, Axis$1.X_UP_TO_Z_UP, boundingSphere ); } return boundingSphere; }; function techniqueAttributeForSemantic(technique, semantic) { return ForEach.techniqueAttribute(technique, function ( attribute, attributeName ) { if (attribute.semantic === semantic) { return attributeName; } }); } function ensureSemanticExistenceForPrimitive(gltf, primitive) { var accessors = gltf.accessors; var materials = gltf.materials; var techniquesWebgl = gltf.extensions.KHR_techniques_webgl; var techniques = techniquesWebgl.techniques; var programs = techniquesWebgl.programs; var shaders = techniquesWebgl.shaders; var targets = primitive.targets; var attributes = primitive.attributes; for (var target in targets) { if (targets.hasOwnProperty(target)) { var targetAttributes = targets[target]; for (var attribute in targetAttributes) { if (attribute !== "extras") { attributes[attribute + "_" + target] = targetAttributes[attribute]; } } } } var material = materials[primitive.material]; var technique = techniques[material.extensions.KHR_techniques_webgl.technique]; var program = programs[technique.program]; var vertexShader = shaders[program.vertexShader]; for (var semantic in attributes) { if (attributes.hasOwnProperty(semantic)) { if (!defined(techniqueAttributeForSemantic(technique, semantic))) { var accessorId = attributes[semantic]; var accessor = accessors[accessorId]; var lowerCase = semantic.toLowerCase(); if (lowerCase.charAt(0) === "_") { lowerCase = lowerCase.slice(1); } var attributeName = "a_" + lowerCase; technique.attributes[attributeName] = { semantic: semantic, type: accessor.componentType, }; var pipelineExtras = vertexShader.extras._pipeline; var shaderText = pipelineExtras.source; shaderText = "attribute " + ModelUtility.getShaderVariable(accessor.type) + " " + attributeName + ";\n" + shaderText; pipelineExtras.source = shaderText; } } } } /** * Ensures all attributes present on the primitive are present in the technique and * vertex shader. * * @param {Object} gltf A javascript object containing a glTF asset. * @returns {Object} The glTF asset, including any additional attributes. */ ModelUtility.ensureSemanticExistence = function (gltf) { ForEach.mesh(gltf, function (mesh) { ForEach.meshPrimitive(mesh, function (primitive) { ensureSemanticExistenceForPrimitive(gltf, primitive); }); }); return gltf; }; /** * Creates attribute location for all attributes required by a technique. * * @param {Object} technique A glTF KHR_techniques_webgl technique object. * @param {Object} precreatedAttributes A dictionary object of pre-created attributes for which to also create locations. * @returns {Object} A dictionary object containing attribute names and their locations. */ ModelUtility.createAttributeLocations = function ( technique, precreatedAttributes ) { var attributeLocations = {}; var hasIndex0 = false; var i = 1; ForEach.techniqueAttribute(technique, function (attribute, attributeName) { // Set the position attribute to the 0th index. In some WebGL implementations the shader // will not work correctly if the 0th attribute is not active. For example, some glTF models // list the normal attribute first but derived shaders like the cast-shadows shader do not use // the normal attribute. if (/pos/i.test(attributeName) && !hasIndex0) { attributeLocations[attributeName] = 0; hasIndex0 = true; } else { attributeLocations[attributeName] = i++; } }); if (defined(precreatedAttributes)) { for (var attributeName in precreatedAttributes) { if (precreatedAttributes.hasOwnProperty(attributeName)) { attributeLocations[attributeName] = i++; } } } return attributeLocations; }; ModelUtility.getAccessorMinMax = function (gltf, accessorId) { var accessor = gltf.accessors[accessorId]; var extensions = accessor.extensions; var accessorMin = accessor.min; var accessorMax = accessor.max; // If this accessor is quantized, we should use the decoded min and max if (defined(extensions)) { var quantizedAttributes = extensions.WEB3D_quantized_attributes; if (defined(quantizedAttributes)) { accessorMin = quantizedAttributes.decodedMin; accessorMax = quantizedAttributes.decodedMax; } } return { min: accessorMin, max: accessorMax, }; }; function getTechniqueAttributeOrUniformFunction( gltf, technique, semantic, ignoreNodes ) { if (hasExtension(gltf, "KHR_techniques_webgl")) { return function (attributeOrUniform, attributeOrUniformName) { if ( attributeOrUniform.semantic === semantic && (!ignoreNodes || !defined(attributeOrUniform.node)) ) { return attributeOrUniformName; } }; } return function (parameterName, attributeOrUniformName) { var attributeOrUniform = technique.parameters[parameterName]; if ( attributeOrUniform.semantic === semantic && (!ignoreNodes || !defined(attributeOrUniform.node)) ) { return attributeOrUniformName; } }; } ModelUtility.getAttributeOrUniformBySemantic = function ( gltf, semantic, programId, ignoreNodes ) { return ForEach.technique(gltf, function (technique) { if (defined(programId) && technique.program !== programId) { return; } var value = ForEach.techniqueAttribute( technique, getTechniqueAttributeOrUniformFunction( gltf, technique, semantic, ignoreNodes ) ); if (defined(value)) { return value; } return ForEach.techniqueUniform( technique, getTechniqueAttributeOrUniformFunction( gltf, technique, semantic, ignoreNodes ) ); }); }; ModelUtility.getDiffuseAttributeOrUniform = function (gltf, programId) { var diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "COLOR_0", programId ); if (!defined(diffuseUniformName)) { diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "_3DTILESDIFFUSE", programId ); } return diffuseUniformName; }; var nodeTranslationScratch = new Cartesian3(); var nodeQuaternionScratch = new Quaternion(); var nodeScaleScratch = new Cartesian3(); ModelUtility.getTransform = function (node, result) { if (defined(node.matrix)) { return Matrix4.fromColumnMajorArray(node.matrix, result); } return Matrix4.fromTranslationQuaternionRotationScale( Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch), Quaternion.unpack(node.rotation, 0, nodeQuaternionScratch), Cartesian3.fromArray(node.scale, 0, nodeScaleScratch), result ); }; ModelUtility.getUsedExtensions = function (gltf) { var extensionsUsed = gltf.extensionsUsed; var cachedExtensionsUsed = {}; if (defined(extensionsUsed)) { var extensionsUsedLength = extensionsUsed.length; for (var i = 0; i < extensionsUsedLength; i++) { var extension = extensionsUsed[i]; cachedExtensionsUsed[extension] = true; } } return cachedExtensionsUsed; }; ModelUtility.getRequiredExtensions = function (gltf) { var extensionsRequired = gltf.extensionsRequired; var cachedExtensionsRequired = {}; if (defined(extensionsRequired)) { var extensionsRequiredLength = extensionsRequired.length; for (var i = 0; i < extensionsRequiredLength; i++) { var extension = extensionsRequired[i]; cachedExtensionsRequired[extension] = true; } } return cachedExtensionsRequired; }; ModelUtility.supportedExtensions = { AGI_articulations: true, CESIUM_RTC: true, EXT_texture_webp: true, KHR_blend: true, KHR_binary_glTF: true, KHR_draco_mesh_compression: true, KHR_materials_common: true, KHR_techniques_webgl: true, KHR_materials_unlit: true, KHR_materials_pbrSpecularGlossiness: true, KHR_texture_transform: true, WEB3D_quantized_attributes: true, }; ModelUtility.checkSupportedExtensions = function ( extensionsRequired, browserSupportsWebp ) { for (var extension in extensionsRequired) { if (extensionsRequired.hasOwnProperty(extension)) { if (!ModelUtility.supportedExtensions[extension]) { throw new RuntimeError("Unsupported glTF Extension: " + extension); } if (extension === "EXT_texture_webp" && browserSupportsWebp === false) { throw new RuntimeError( "Loaded model requires WebP but browser does not support it." ); } } } }; ModelUtility.checkSupportedGlExtensions = function (extensionsUsed, context) { if (defined(extensionsUsed)) { var glExtensionsUsedLength = extensionsUsed.length; for (var i = 0; i < glExtensionsUsedLength; i++) { var extension = extensionsUsed[i]; if (extension !== "OES_element_index_uint") { throw new RuntimeError("Unsupported WebGL Extension: " + extension); } else if (!context.elementIndexUint) { throw new RuntimeError( "OES_element_index_uint WebGL extension is not enabled." ); } } } }; function replaceAllButFirstInString(string, find, replace) { // Limit search to strings that are not a subset of other tokens. find += "(?!\\w)"; find = new RegExp(find, "g"); var index = string.search(find); return string.replace(find, function (match, offset) { return index === offset ? match : replace; }); } function getQuantizedAttributes(gltf, accessorId) { var accessor = gltf.accessors[accessorId]; var extensions = accessor.extensions; if (defined(extensions)) { return extensions.WEB3D_quantized_attributes; } return undefined; } function getAttributeVariableName(gltf, primitive, attributeSemantic) { var materialId = primitive.material; var material = gltf.materials[materialId]; if ( !hasExtension(gltf, "KHR_techniques_webgl") || !defined(material.extensions) || !defined(material.extensions.KHR_techniques_webgl) ) { return; } var techniqueId = material.extensions.KHR_techniques_webgl.technique; var techniquesWebgl = gltf.extensions.KHR_techniques_webgl; var technique = techniquesWebgl.techniques[techniqueId]; return ForEach.techniqueAttribute(technique, function ( attribute, attributeName ) { var semantic = attribute.semantic; if (semantic === attributeSemantic) { return attributeName; } }); } ModelUtility.modifyShaderForDracoQuantizedAttributes = function ( gltf, primitive, shader, decodedAttributes ) { var quantizedUniforms = {}; for (var attributeSemantic in decodedAttributes) { if (decodedAttributes.hasOwnProperty(attributeSemantic)) { var attribute = decodedAttributes[attributeSemantic]; var quantization = attribute.quantization; if (!defined(quantization)) { continue; } var attributeVarName = getAttributeVariableName( gltf, primitive, attributeSemantic ); if (attributeSemantic.charAt(0) === "_") { attributeSemantic = attributeSemantic.substring(1); } var decodeUniformVarName = "gltf_u_dec_" + attributeSemantic.toLowerCase(); if (!defined(quantizedUniforms[decodeUniformVarName])) { var newMain = "gltf_decoded_" + attributeSemantic; var decodedAttributeVarName = attributeVarName.replace( "a_", "gltf_a_dec_" ); var size = attribute.componentsPerAttribute; // replace usages of the original attribute with the decoded version, but not the declaration shader = replaceAllButFirstInString( shader, attributeVarName, decodedAttributeVarName ); // declare decoded attribute var variableType; if (quantization.octEncoded) { variableType = "vec3"; } else if (size > 1) { variableType = "vec" + size; } else { variableType = "float"; } shader = variableType + " " + decodedAttributeVarName + ";\n" + shader; // The gltf 2.0 COLOR_0 vertex attribute can be VEC4 or VEC3 var vec3Color = size === 3 && attributeSemantic === "COLOR_0"; if (vec3Color) { shader = replaceAllButFirstInString( shader, decodedAttributeVarName, "vec4(" + decodedAttributeVarName + ", 1.0)" ); } // splice decode function into the shader var decode = ""; if (quantization.octEncoded) { var decodeUniformVarNameRangeConstant = decodeUniformVarName + "_rangeConstant"; shader = "uniform float " + decodeUniformVarNameRangeConstant + ";\n" + shader; decode = "\n" + "void main() {\n" + // Draco oct-encoding decodes to zxy order " " + decodedAttributeVarName + " = czm_octDecode(" + attributeVarName + ".xy, " + decodeUniformVarNameRangeConstant + ").zxy;\n" + " " + newMain + "();\n" + "}\n"; } else { var decodeUniformVarNameNormConstant = decodeUniformVarName + "_normConstant"; var decodeUniformVarNameMin = decodeUniformVarName + "_min"; shader = "uniform float " + decodeUniformVarNameNormConstant + ";\n" + "uniform " + variableType + " " + decodeUniformVarNameMin + ";\n" + shader; var attributeVarAccess = vec3Color ? ".xyz" : ""; decode = "\n" + "void main() {\n" + " " + decodedAttributeVarName + " = " + decodeUniformVarNameMin + " + " + attributeVarName + attributeVarAccess + " * " + decodeUniformVarNameNormConstant + ";\n" + " " + newMain + "();\n" + "}\n"; } shader = ShaderSource.replaceMain(shader, newMain); shader += decode; } } } return { shader: shader, }; }; ModelUtility.modifyShaderForQuantizedAttributes = function ( gltf, primitive, shader ) { var quantizedUniforms = {}; var attributes = primitive.attributes; for (var attributeSemantic in attributes) { if (attributes.hasOwnProperty(attributeSemantic)) { var attributeVarName = getAttributeVariableName( gltf, primitive, attributeSemantic ); var accessorId = primitive.attributes[attributeSemantic]; if (attributeSemantic.charAt(0) === "_") { attributeSemantic = attributeSemantic.substring(1); } var decodeUniformVarName = "gltf_u_dec_" + attributeSemantic.toLowerCase(); var decodeUniformVarNameScale = decodeUniformVarName + "_scale"; var decodeUniformVarNameTranslate = decodeUniformVarName + "_translate"; if ( !defined(quantizedUniforms[decodeUniformVarName]) && !defined(quantizedUniforms[decodeUniformVarNameScale]) ) { var quantizedAttributes = getQuantizedAttributes(gltf, accessorId); if (defined(quantizedAttributes)) { var decodeMatrix = quantizedAttributes.decodeMatrix; var newMain = "gltf_decoded_" + attributeSemantic; var decodedAttributeVarName = attributeVarName.replace( "a_", "gltf_a_dec_" ); var size = Math.floor(Math.sqrt(decodeMatrix.length)); // replace usages of the original attribute with the decoded version, but not the declaration shader = replaceAllButFirstInString( shader, attributeVarName, decodedAttributeVarName ); // declare decoded attribute var variableType; if (size > 2) { variableType = "vec" + (size - 1); } else { variableType = "float"; } shader = variableType + " " + decodedAttributeVarName + ";\n" + shader; // splice decode function into the shader - attributes are pre-multiplied with the decode matrix // uniform in the shader (32-bit floating point) var decode = ""; if (size === 5) { // separate scale and translate since glsl doesn't have mat5 shader = "uniform mat4 " + decodeUniformVarNameScale + ";\n" + shader; shader = "uniform vec4 " + decodeUniformVarNameTranslate + ";\n" + shader; decode = "\n" + "void main() {\n" + " " + decodedAttributeVarName + " = " + decodeUniformVarNameScale + " * " + attributeVarName + " + " + decodeUniformVarNameTranslate + ";\n" + " " + newMain + "();\n" + "}\n"; quantizedUniforms[decodeUniformVarNameScale] = { mat: 4 }; quantizedUniforms[decodeUniformVarNameTranslate] = { vec: 4 }; } else { shader = "uniform mat" + size + " " + decodeUniformVarName + ";\n" + shader; decode = "\n" + "void main() {\n" + " " + decodedAttributeVarName + " = " + variableType + "(" + decodeUniformVarName + " * vec" + size + "(" + attributeVarName + ",1.0));\n" + " " + newMain + "();\n" + "}\n"; quantizedUniforms[decodeUniformVarName] = { mat: size }; } shader = ShaderSource.replaceMain(shader, newMain); shader += decode; } } } } return { shader: shader, uniforms: quantizedUniforms, }; }; function getScalarUniformFunction(value) { var that = { value: value, clone: function (source, result) { return source; }, func: function () { return that.value; }, }; return that; } function getVec2UniformFunction(value) { var that = { value: Cartesian2.fromArray(value), clone: Cartesian2.clone, func: function () { return that.value; }, }; return that; } function getVec3UniformFunction(value) { var that = { value: Cartesian3.fromArray(value), clone: Cartesian3.clone, func: function () { return that.value; }, }; return that; } function getVec4UniformFunction(value) { var that = { value: Cartesian4.fromArray(value), clone: Cartesian4.clone, func: function () { return that.value; }, }; return that; } function getMat2UniformFunction(value) { var that = { value: Matrix2.fromColumnMajorArray(value), clone: Matrix2.clone, func: function () { return that.value; }, }; return that; } function getMat3UniformFunction(value) { var that = { value: Matrix3.fromColumnMajorArray(value), clone: Matrix3.clone, func: function () { return that.value; }, }; return that; } function getMat4UniformFunction(value) { var that = { value: Matrix4.fromColumnMajorArray(value), clone: Matrix4.clone, func: function () { return that.value; }, }; return that; } /////////////////////////////////////////////////////////////////////////// function DelayLoadedTextureUniform(value, textures, defaultTexture) { this._value = undefined; this._textureId = value.index; this._textures = textures; this._defaultTexture = defaultTexture; } Object.defineProperties(DelayLoadedTextureUniform.prototype, { value: { get: function () { // Use the default texture (1x1 white) until the model's texture is loaded if (!defined(this._value)) { var texture = this._textures[this._textureId]; if (defined(texture)) { this._value = texture; } else { return this._defaultTexture; } } return this._value; }, set: function (value) { this._value = value; }, }, }); DelayLoadedTextureUniform.prototype.clone = function (source) { return source; }; DelayLoadedTextureUniform.prototype.func = undefined; /////////////////////////////////////////////////////////////////////////// function getTextureUniformFunction(value, textures, defaultTexture) { var uniform = new DelayLoadedTextureUniform(value, textures, defaultTexture); // Define function here to access closure since 'this' can't be // used when the Renderer sets uniforms. uniform.func = function () { return uniform.value; }; return uniform; } var gltfUniformFunctions = {}; gltfUniformFunctions[WebGLConstants$1.FLOAT] = getScalarUniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_VEC2] = getVec2UniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_VEC3] = getVec3UniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_VEC4] = getVec4UniformFunction; gltfUniformFunctions[WebGLConstants$1.INT] = getScalarUniformFunction; gltfUniformFunctions[WebGLConstants$1.INT_VEC2] = getVec2UniformFunction; gltfUniformFunctions[WebGLConstants$1.INT_VEC3] = getVec3UniformFunction; gltfUniformFunctions[WebGLConstants$1.INT_VEC4] = getVec4UniformFunction; gltfUniformFunctions[WebGLConstants$1.BOOL] = getScalarUniformFunction; gltfUniformFunctions[WebGLConstants$1.BOOL_VEC2] = getVec2UniformFunction; gltfUniformFunctions[WebGLConstants$1.BOOL_VEC3] = getVec3UniformFunction; gltfUniformFunctions[WebGLConstants$1.BOOL_VEC4] = getVec4UniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_MAT2] = getMat2UniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_MAT3] = getMat3UniformFunction; gltfUniformFunctions[WebGLConstants$1.FLOAT_MAT4] = getMat4UniformFunction; gltfUniformFunctions[WebGLConstants$1.SAMPLER_2D] = getTextureUniformFunction; // GLTF_SPEC: Support SAMPLER_CUBE. https://github.com/KhronosGroup/glTF/issues/40 ModelUtility.createUniformFunction = function ( type, value, textures, defaultTexture ) { return gltfUniformFunctions[type](value, textures, defaultTexture); }; function scaleFromMatrix5Array(matrix) { return [ matrix[0], matrix[1], matrix[2], matrix[3], matrix[5], matrix[6], matrix[7], matrix[8], matrix[10], matrix[11], matrix[12], matrix[13], matrix[15], matrix[16], matrix[17], matrix[18], ]; } function translateFromMatrix5Array(matrix) { return [matrix[20], matrix[21], matrix[22], matrix[23]]; } ModelUtility.createUniformsForDracoQuantizedAttributes = function ( decodedAttributes ) { var uniformMap = {}; for (var attribute in decodedAttributes) { if (decodedAttributes.hasOwnProperty(attribute)) { var decodedData = decodedAttributes[attribute]; var quantization = decodedData.quantization; if (!defined(quantization)) { continue; } if (attribute.charAt(0) === "_") { attribute = attribute.substring(1); } var uniformVarName = "gltf_u_dec_" + attribute.toLowerCase(); if (quantization.octEncoded) { var uniformVarNameRangeConstant = uniformVarName + "_rangeConstant"; var rangeConstant = (1 << quantization.quantizationBits) - 1.0; uniformMap[uniformVarNameRangeConstant] = getScalarUniformFunction( rangeConstant ).func; continue; } var uniformVarNameNormConstant = uniformVarName + "_normConstant"; var normConstant = quantization.range / (1 << quantization.quantizationBits); uniformMap[uniformVarNameNormConstant] = getScalarUniformFunction( normConstant ).func; var uniformVarNameMin = uniformVarName + "_min"; switch (decodedData.componentsPerAttribute) { case 1: uniformMap[uniformVarNameMin] = getScalarUniformFunction( quantization.minValues ).func; break; case 2: uniformMap[uniformVarNameMin] = getVec2UniformFunction( quantization.minValues ).func; break; case 3: uniformMap[uniformVarNameMin] = getVec3UniformFunction( quantization.minValues ).func; break; case 4: uniformMap[uniformVarNameMin] = getVec4UniformFunction( quantization.minValues ).func; break; } } } return uniformMap; }; ModelUtility.createUniformsForQuantizedAttributes = function ( gltf, primitive, quantizedUniforms ) { var accessors = gltf.accessors; var setUniforms = {}; var uniformMap = {}; var attributes = primitive.attributes; for (var attribute in attributes) { if (attributes.hasOwnProperty(attribute)) { var accessorId = attributes[attribute]; var a = accessors[accessorId]; var extensions = a.extensions; if (attribute.charAt(0) === "_") { attribute = attribute.substring(1); } if (defined(extensions)) { var quantizedAttributes = extensions.WEB3D_quantized_attributes; if (defined(quantizedAttributes)) { var decodeMatrix = quantizedAttributes.decodeMatrix; var uniformVariable = "gltf_u_dec_" + attribute.toLowerCase(); switch (a.type) { case AttributeType$1.SCALAR: uniformMap[uniformVariable] = getMat2UniformFunction( decodeMatrix ).func; setUniforms[uniformVariable] = true; break; case AttributeType$1.VEC2: uniformMap[uniformVariable] = getMat3UniformFunction( decodeMatrix ).func; setUniforms[uniformVariable] = true; break; case AttributeType$1.VEC3: uniformMap[uniformVariable] = getMat4UniformFunction( decodeMatrix ).func; setUniforms[uniformVariable] = true; break; case AttributeType$1.VEC4: // VEC4 attributes are split into scale and translate because there is no mat5 in GLSL var uniformVariableScale = uniformVariable + "_scale"; var uniformVariableTranslate = uniformVariable + "_translate"; uniformMap[uniformVariableScale] = getMat4UniformFunction( scaleFromMatrix5Array(decodeMatrix) ).func; uniformMap[uniformVariableTranslate] = getVec4UniformFunction( translateFromMatrix5Array(decodeMatrix) ).func; setUniforms[uniformVariableScale] = true; setUniforms[uniformVariableTranslate] = true; break; } } } } } // If there are any unset quantized uniforms in this program, they should be set to the identity for (var quantizedUniform in quantizedUniforms) { if (quantizedUniforms.hasOwnProperty(quantizedUniform)) { if (!setUniforms[quantizedUniform]) { var properties = quantizedUniforms[quantizedUniform]; if (defined(properties.mat)) { if (properties.mat === 2) { uniformMap[quantizedUniform] = getMat2UniformFunction( Matrix2.IDENTITY ).func; } else if (properties.mat === 3) { uniformMap[quantizedUniform] = getMat3UniformFunction( Matrix3.IDENTITY ).func; } else if (properties.mat === 4) { uniformMap[quantizedUniform] = getMat4UniformFunction( Matrix4.IDENTITY ).func; } } if (defined(properties.vec)) { if (properties.vec === 4) { uniformMap[quantizedUniform] = getVec4UniformFunction([ 0, 0, 0, 0, ]).func; } } } } } return uniformMap; }; // This doesn't support LOCAL, which we could add if it is ever used. var scratchTranslationRtc = new Cartesian3(); var gltfSemanticUniforms$1 = { MODEL: function (uniformState, model) { return function () { return uniformState.model; }; }, VIEW: function (uniformState, model) { return function () { return uniformState.view; }; }, PROJECTION: function (uniformState, model) { return function () { return uniformState.projection; }; }, MODELVIEW: function (uniformState, model) { return function () { return uniformState.modelView; }; }, CESIUM_RTC_MODELVIEW: function (uniformState, model) { // CESIUM_RTC extension var mvRtc = new Matrix4(); return function () { if (defined(model._rtcCenter)) { Matrix4.getTranslation(uniformState.model, scratchTranslationRtc); Cartesian3.add( scratchTranslationRtc, model._rtcCenter, scratchTranslationRtc ); Matrix4.multiplyByPoint( uniformState.view, scratchTranslationRtc, scratchTranslationRtc ); return Matrix4.setTranslation( uniformState.modelView, scratchTranslationRtc, mvRtc ); } return uniformState.modelView; }; }, MODELVIEWPROJECTION: function (uniformState, model) { return function () { return uniformState.modelViewProjection; }; }, MODELINVERSE: function (uniformState, model) { return function () { return uniformState.inverseModel; }; }, VIEWINVERSE: function (uniformState, model) { return function () { return uniformState.inverseView; }; }, PROJECTIONINVERSE: function (uniformState, model) { return function () { return uniformState.inverseProjection; }; }, MODELVIEWINVERSE: function (uniformState, model) { return function () { return uniformState.inverseModelView; }; }, MODELVIEWPROJECTIONINVERSE: function (uniformState, model) { return function () { return uniformState.inverseModelViewProjection; }; }, MODELINVERSETRANSPOSE: function (uniformState, model) { return function () { return uniformState.inverseTransposeModel; }; }, MODELVIEWINVERSETRANSPOSE: function (uniformState, model) { return function () { return uniformState.normal; }; }, VIEWPORT: function (uniformState, model) { return function () { return uniformState.viewportCartesian4; }; }, // JOINTMATRIX created in createCommand() }; ModelUtility.getGltfSemanticUniforms = function () { return gltfSemanticUniforms$1; }; /** * @private */ function processModelMaterialsCommon(gltf, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); if (!defined(gltf)) { return; } if (!hasExtension(gltf, "KHR_materials_common")) { return; } if (!hasExtension(gltf, "KHR_techniques_webgl")) { if (!defined(gltf.extensions)) { gltf.extensions = {}; } gltf.extensions.KHR_techniques_webgl = { programs: [], shaders: [], techniques: [], }; gltf.extensionsUsed.push("KHR_techniques_webgl"); gltf.extensionsRequired.push("KHR_techniques_webgl"); } var techniquesWebgl = gltf.extensions.KHR_techniques_webgl; lightDefaults(gltf); var lightParameters = generateLightParameters(gltf); var primitiveByMaterial = ModelUtility.splitIncompatibleMaterials(gltf); var techniques = {}; var generatedTechniques = false; ForEach.material(gltf, function (material, materialIndex) { if ( defined(material.extensions) && defined(material.extensions.KHR_materials_common) ) { var khrMaterialsCommon = material.extensions.KHR_materials_common; var primitiveInfo = primitiveByMaterial[materialIndex]; var techniqueKey = getTechniqueKey(khrMaterialsCommon, primitiveInfo); var technique = techniques[techniqueKey]; if (!defined(technique)) { technique = generateTechnique$1( gltf, techniquesWebgl, primitiveInfo, khrMaterialsCommon, lightParameters, options.addBatchIdToGeneratedShaders ); techniques[techniqueKey] = technique; generatedTechniques = true; } var materialValues = {}; var values = khrMaterialsCommon.values; var uniformName; for (var valueName in values) { if ( values.hasOwnProperty(valueName) && valueName !== "transparent" && valueName !== "doubleSided" ) { uniformName = "u_" + valueName.toLowerCase(); materialValues[uniformName] = values[valueName]; } } material.extensions.KHR_techniques_webgl = { technique: technique, values: materialValues, }; material.alphaMode = "OPAQUE"; if (khrMaterialsCommon.transparent) { material.alphaMode = "BLEND"; } if (khrMaterialsCommon.doubleSided) { material.doubleSided = true; } } }); if (!generatedTechniques) { return gltf; } // If any primitives have semantics that aren't declared in the generated // shaders, we want to preserve them. ModelUtility.ensureSemanticExistence(gltf); return gltf; } function generateLightParameters(gltf) { var result = {}; var lights; if ( defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common) ) { lights = gltf.extensions.KHR_materials_common.lights; } if (defined(lights)) { // Figure out which node references the light var nodes = gltf.nodes; for (var nodeName in nodes) { if (nodes.hasOwnProperty(nodeName)) { var node = nodes[nodeName]; if ( defined(node.extensions) && defined(node.extensions.KHR_materials_common) ) { var nodeLightId = node.extensions.KHR_materials_common.light; if (defined(nodeLightId) && defined(lights[nodeLightId])) { lights[nodeLightId].node = nodeName; } delete node.extensions.KHR_materials_common; } } } // Add light parameters to result var lightCount = 0; for (var lightName in lights) { if (lights.hasOwnProperty(lightName)) { var light = lights[lightName]; var lightType = light.type; if (lightType !== "ambient" && !defined(light.node)) { delete lights[lightName]; continue; } var lightBaseName = "light" + lightCount.toString(); light.baseName = lightBaseName; switch (lightType) { case "ambient": var ambient = light.ambient; result[lightBaseName + "Color"] = { type: WebGLConstants$1.FLOAT_VEC3, value: ambient.color, }; break; case "directional": var directional = light.directional; result[lightBaseName + "Color"] = { type: WebGLConstants$1.FLOAT_VEC3, value: directional.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants$1.FLOAT_MAT4, }; } break; case "point": var point = light.point; result[lightBaseName + "Color"] = { type: WebGLConstants$1.FLOAT_VEC3, value: point.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants$1.FLOAT_MAT4, }; } result[lightBaseName + "Attenuation"] = { type: WebGLConstants$1.FLOAT_VEC3, value: [ point.constantAttenuation, point.linearAttenuation, point.quadraticAttenuation, ], }; break; case "spot": var spot = light.spot; result[lightBaseName + "Color"] = { type: WebGLConstants$1.FLOAT_VEC3, value: spot.color, }; if (defined(light.node)) { result[lightBaseName + "Transform"] = { node: light.node, semantic: "MODELVIEW", type: WebGLConstants$1.FLOAT_MAT4, }; result[lightBaseName + "InverseTransform"] = { node: light.node, semantic: "MODELVIEWINVERSE", type: WebGLConstants$1.FLOAT_MAT4, useInFragment: true, }; } result[lightBaseName + "Attenuation"] = { type: WebGLConstants$1.FLOAT_VEC3, value: [ spot.constantAttenuation, spot.linearAttenuation, spot.quadraticAttenuation, ], }; result[lightBaseName + "FallOff"] = { type: WebGLConstants$1.FLOAT_VEC2, value: [spot.fallOffAngle, spot.fallOffExponent], }; break; } ++lightCount; } } } return result; } function generateTechnique$1( gltf, techniquesWebgl, primitiveInfo, khrMaterialsCommon, lightParameters, addBatchIdToGeneratedShaders ) { if (!defined(khrMaterialsCommon)) { khrMaterialsCommon = {}; } addBatchIdToGeneratedShaders = defaultValue( addBatchIdToGeneratedShaders, false ); var techniques = techniquesWebgl.techniques; var shaders = techniquesWebgl.shaders; var programs = techniquesWebgl.programs; var lightingModel = khrMaterialsCommon.technique.toUpperCase(); var lights; if ( defined(gltf.extensions) && defined(gltf.extensions.KHR_materials_common) ) { lights = gltf.extensions.KHR_materials_common.lights; } var parameterValues = khrMaterialsCommon.values; var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); var skinningInfo; var hasSkinning = false; var hasVertexColors = false; if (defined(primitiveInfo)) { skinningInfo = primitiveInfo.skinning; hasSkinning = skinningInfo.skinned; hasVertexColors = primitiveInfo.hasVertexColors; } var vertexShader = "precision highp float;\n"; var fragmentShader = "precision highp float;\n"; var hasNormals = lightingModel !== "CONSTANT"; // Add techniques var techniqueUniforms = { u_modelViewMatrix: { semantic: hasExtension(gltf, "CESIUM_RTC") ? "CESIUM_RTC_MODELVIEW" : "MODELVIEW", type: WebGLConstants$1.FLOAT_MAT4, }, u_projectionMatrix: { semantic: "PROJECTION", type: WebGLConstants$1.FLOAT_MAT4, }, }; if (hasNormals) { techniqueUniforms.u_normalMatrix = { semantic: "MODELVIEWINVERSETRANSPOSE", type: WebGLConstants$1.FLOAT_MAT3, }; } if (hasSkinning) { techniqueUniforms.u_jointMatrix = { count: jointCount, semantic: "JOINTMATRIX", type: WebGLConstants$1.FLOAT_MAT4, }; } // Add material values var uniformName; var hasTexCoords = false; for (var name in parameterValues) { //generate shader parameters for KHR_materials_common attributes //(including a check, because some boolean flags should not be used as shader parameters) if ( parameterValues.hasOwnProperty(name) && name !== "transparent" && name !== "doubleSided" ) { var uniformType = getKHRMaterialsCommonValueType( name, parameterValues[name] ); uniformName = "u_" + name.toLowerCase(); if (!hasTexCoords && uniformType === WebGLConstants$1.SAMPLER_2D) { hasTexCoords = true; } techniqueUniforms[uniformName] = { type: uniformType, }; } } // Give the diffuse uniform a semantic to support color replacement in 3D Tiles if (defined(techniqueUniforms.u_diffuse)) { techniqueUniforms.u_diffuse.semantic = "_3DTILESDIFFUSE"; } // Copy light parameters into technique parameters if (defined(lightParameters)) { for (var lightParamName in lightParameters) { if (lightParameters.hasOwnProperty(lightParamName)) { uniformName = "u_" + lightParamName; techniqueUniforms[uniformName] = lightParameters[lightParamName]; } } } // Add uniforms to shaders for (uniformName in techniqueUniforms) { if (techniqueUniforms.hasOwnProperty(uniformName)) { var uniform = techniqueUniforms[uniformName]; var arraySize = defined(uniform.count) ? "[" + uniform.count + "]" : ""; if ( (uniform.type !== WebGLConstants$1.FLOAT_MAT3 && uniform.type !== WebGLConstants$1.FLOAT_MAT4) || uniform.useInFragment ) { fragmentShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; delete uniform.useInFragment; } else { vertexShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; } } } // Add attributes with semantics var vertexShaderMain = ""; if (hasSkinning) { vertexShaderMain += " mat4 skinMatrix =\n" + " a_weight.x * u_jointMatrix[int(a_joint.x)] +\n" + " a_weight.y * u_jointMatrix[int(a_joint.y)] +\n" + " a_weight.z * u_jointMatrix[int(a_joint.z)] +\n" + " a_weight.w * u_jointMatrix[int(a_joint.w)];\n"; } // Add position always var techniqueAttributes = { a_position: { semantic: "POSITION", }, }; vertexShader += "attribute vec3 a_position;\n"; vertexShader += "varying vec3 v_positionEC;\n"; if (hasSkinning) { vertexShaderMain += " vec4 pos = u_modelViewMatrix * skinMatrix * vec4(a_position,1.0);\n"; } else { vertexShaderMain += " vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);\n"; } vertexShaderMain += " v_positionEC = pos.xyz;\n"; vertexShaderMain += " gl_Position = u_projectionMatrix * pos;\n"; fragmentShader += "varying vec3 v_positionEC;\n"; // Add normal if we don't have constant lighting if (hasNormals) { techniqueAttributes.a_normal = { semantic: "NORMAL", }; vertexShader += "attribute vec3 a_normal;\n"; vertexShader += "varying vec3 v_normal;\n"; if (hasSkinning) { vertexShaderMain += " v_normal = u_normalMatrix * mat3(skinMatrix) * a_normal;\n"; } else { vertexShaderMain += " v_normal = u_normalMatrix * a_normal;\n"; } fragmentShader += "varying vec3 v_normal;\n"; } // Add texture coordinates if the material uses them var v_texcoord; if (hasTexCoords) { techniqueAttributes.a_texcoord_0 = { semantic: "TEXCOORD_0", }; v_texcoord = "v_texcoord_0"; vertexShader += "attribute vec2 a_texcoord_0;\n"; vertexShader += "varying vec2 " + v_texcoord + ";\n"; vertexShaderMain += " " + v_texcoord + " = a_texcoord_0;\n"; fragmentShader += "varying vec2 " + v_texcoord + ";\n"; } if (hasSkinning) { techniqueAttributes.a_joint = { semantic: "JOINTS_0", }; techniqueAttributes.a_weight = { semantic: "WEIGHTS_0", }; vertexShader += "attribute vec4 a_joint;\n"; vertexShader += "attribute vec4 a_weight;\n"; } if (hasVertexColors) { techniqueAttributes.a_vertexColor = { semantic: "COLOR_0", }; vertexShader += "attribute vec4 a_vertexColor;\n"; vertexShader += "varying vec4 v_vertexColor;\n"; vertexShaderMain += " v_vertexColor = a_vertexColor;\n"; fragmentShader += "varying vec4 v_vertexColor;\n"; } if (addBatchIdToGeneratedShaders) { techniqueAttributes.a_batchId = { semantic: "_BATCHID", }; vertexShader += "attribute float a_batchId;\n"; } var hasSpecular = hasNormals && (lightingModel === "BLINN" || lightingModel === "PHONG") && defined(techniqueUniforms.u_specular) && defined(techniqueUniforms.u_shininess) && techniqueUniforms.u_shininess > 0.0; // Generate lighting code blocks var hasNonAmbientLights = false; var hasAmbientLights = false; var fragmentLightingBlock = ""; for (var lightName in lights) { if (lights.hasOwnProperty(lightName)) { var light = lights[lightName]; var lightType = light.type.toLowerCase(); var lightBaseName = light.baseName; fragmentLightingBlock += " {\n"; var lightColorName = "u_" + lightBaseName + "Color"; var varyingDirectionName; var varyingPositionName; if (lightType === "ambient") { hasAmbientLights = true; fragmentLightingBlock += " ambientLight += " + lightColorName + ";\n"; } else if (hasNormals) { hasNonAmbientLights = true; varyingDirectionName = "v_" + lightBaseName + "Direction"; varyingPositionName = "v_" + lightBaseName + "Position"; if (lightType !== "point") { vertexShader += "varying vec3 " + varyingDirectionName + ";\n"; fragmentShader += "varying vec3 " + varyingDirectionName + ";\n"; vertexShaderMain += " " + varyingDirectionName + " = mat3(u_" + lightBaseName + "Transform) * vec3(0.,0.,1.);\n"; if (lightType === "directional") { fragmentLightingBlock += " vec3 l = normalize(" + varyingDirectionName + ");\n"; } } if (lightType !== "directional") { vertexShader += "varying vec3 " + varyingPositionName + ";\n"; fragmentShader += "varying vec3 " + varyingPositionName + ";\n"; vertexShaderMain += " " + varyingPositionName + " = u_" + lightBaseName + "Transform[3].xyz;\n"; fragmentLightingBlock += " vec3 VP = " + varyingPositionName + " - v_positionEC;\n"; fragmentLightingBlock += " vec3 l = normalize(VP);\n"; fragmentLightingBlock += " float range = length(VP);\n"; fragmentLightingBlock += " float attenuation = 1.0 / (u_" + lightBaseName + "Attenuation.x + "; fragmentLightingBlock += "(u_" + lightBaseName + "Attenuation.y * range) + "; fragmentLightingBlock += "(u_" + lightBaseName + "Attenuation.z * range * range));\n"; } else { fragmentLightingBlock += " float attenuation = 1.0;\n"; } if (lightType === "spot") { fragmentLightingBlock += " float spotDot = dot(l, normalize(" + varyingDirectionName + "));\n"; fragmentLightingBlock += " if (spotDot < cos(u_" + lightBaseName + "FallOff.x * 0.5))\n"; fragmentLightingBlock += " {\n"; fragmentLightingBlock += " attenuation = 0.0;\n"; fragmentLightingBlock += " }\n"; fragmentLightingBlock += " else\n"; fragmentLightingBlock += " {\n"; fragmentLightingBlock += " attenuation *= max(0.0, pow(spotDot, u_" + lightBaseName + "FallOff.y));\n"; fragmentLightingBlock += " }\n"; } fragmentLightingBlock += " diffuseLight += " + lightColorName + "* max(dot(normal,l), 0.) * attenuation;\n"; if (hasSpecular) { if (lightingModel === "BLINN") { fragmentLightingBlock += " vec3 h = normalize(l + viewDir);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess)) * attenuation;\n"; } else { // PHONG fragmentLightingBlock += " vec3 reflectDir = reflect(-l, normal);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)) * attenuation;\n"; } fragmentLightingBlock += " specularLight += " + lightColorName + " * specularIntensity;\n"; } } fragmentLightingBlock += " }\n"; } } if (!hasAmbientLights) { // Add an ambient light if we don't have one fragmentLightingBlock += " ambientLight += vec3(0.2, 0.2, 0.2);\n"; } if (!hasNonAmbientLights && lightingModel !== "CONSTANT") { fragmentShader += "#ifdef USE_CUSTOM_LIGHT_COLOR \n"; fragmentShader += "uniform vec3 gltf_lightColor; \n"; fragmentShader += "#endif \n"; fragmentLightingBlock += "#ifndef USE_CUSTOM_LIGHT_COLOR \n"; fragmentLightingBlock += " vec3 lightColor = czm_lightColor;\n"; fragmentLightingBlock += "#else \n"; fragmentLightingBlock += " vec3 lightColor = gltf_lightColor;\n"; fragmentLightingBlock += "#endif \n"; fragmentLightingBlock += " vec3 l = normalize(czm_lightDirectionEC);\n"; var minimumLighting = "0.2"; // Use strings instead of values as 0.0 -> 0 when stringified fragmentLightingBlock += " diffuseLight += lightColor * max(dot(normal,l), " + minimumLighting + ");\n"; if (hasSpecular) { if (lightingModel === "BLINN") { fragmentLightingBlock += " vec3 h = normalize(l + viewDir);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess));\n"; } else { // PHONG fragmentLightingBlock += " vec3 reflectDir = reflect(-l, normal);\n"; fragmentLightingBlock += " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));\n"; } fragmentLightingBlock += " specularLight += lightColor * specularIntensity;\n"; } } vertexShader += "void main(void) {\n"; vertexShader += vertexShaderMain; vertexShader += "}\n"; fragmentShader += "void main(void) {\n"; var colorCreationBlock = " vec3 color = vec3(0.0, 0.0, 0.0);\n"; if (hasNormals) { fragmentShader += " vec3 normal = normalize(v_normal);\n"; if (khrMaterialsCommon.doubleSided) { fragmentShader += " if (czm_backFacing())\n"; fragmentShader += " {\n"; fragmentShader += " normal = -normal;\n"; fragmentShader += " }\n"; } } var finalColorComputation; if (lightingModel !== "CONSTANT") { if (defined(techniqueUniforms.u_diffuse)) { if (techniqueUniforms.u_diffuse.type === WebGLConstants$1.SAMPLER_2D) { fragmentShader += " vec4 diffuse = texture2D(u_diffuse, " + v_texcoord + ");\n"; } else { fragmentShader += " vec4 diffuse = u_diffuse;\n"; } fragmentShader += " vec3 diffuseLight = vec3(0.0, 0.0, 0.0);\n"; colorCreationBlock += " color += diffuse.rgb * diffuseLight;\n"; } if (hasSpecular) { if (techniqueUniforms.u_specular.type === WebGLConstants$1.SAMPLER_2D) { fragmentShader += " vec3 specular = texture2D(u_specular, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 specular = u_specular.rgb;\n"; } fragmentShader += " vec3 specularLight = vec3(0.0, 0.0, 0.0);\n"; colorCreationBlock += " color += specular * specularLight;\n"; } if (defined(techniqueUniforms.u_transparency)) { finalColorComputation = " gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency);\n"; } else { finalColorComputation = " gl_FragColor = vec4(color * diffuse.a, diffuse.a);\n"; } } else if (defined(techniqueUniforms.u_transparency)) { finalColorComputation = " gl_FragColor = vec4(color * u_transparency, u_transparency);\n"; } else { finalColorComputation = " gl_FragColor = vec4(color, 1.0);\n"; } if (hasVertexColors) { colorCreationBlock += " color *= v_vertexColor.rgb;\n"; } if (defined(techniqueUniforms.u_emission)) { if (techniqueUniforms.u_emission.type === WebGLConstants$1.SAMPLER_2D) { fragmentShader += " vec3 emission = texture2D(u_emission, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 emission = u_emission.rgb;\n"; } colorCreationBlock += " color += emission;\n"; } if (defined(techniqueUniforms.u_ambient) || lightingModel !== "CONSTANT") { if (defined(techniqueUniforms.u_ambient)) { if (techniqueUniforms.u_ambient.type === WebGLConstants$1.SAMPLER_2D) { fragmentShader += " vec3 ambient = texture2D(u_ambient, " + v_texcoord + ").rgb;\n"; } else { fragmentShader += " vec3 ambient = u_ambient.rgb;\n"; } } else { fragmentShader += " vec3 ambient = diffuse.rgb;\n"; } colorCreationBlock += " color += ambient * ambientLight;\n"; } fragmentShader += " vec3 viewDir = -normalize(v_positionEC);\n"; fragmentShader += " vec3 ambientLight = vec3(0.0, 0.0, 0.0);\n"; // Add in light computations fragmentShader += fragmentLightingBlock; fragmentShader += colorCreationBlock; fragmentShader += finalColorComputation; fragmentShader += "}\n"; // Add shaders var vertexShaderId = addToArray(shaders, { type: WebGLConstants$1.VERTEX_SHADER, extras: { _pipeline: { source: vertexShader, extension: ".glsl", }, }, }); var fragmentShaderId = addToArray(shaders, { type: WebGLConstants$1.FRAGMENT_SHADER, extras: { _pipeline: { source: fragmentShader, extension: ".glsl", }, }, }); // Add program var programId = addToArray(programs, { fragmentShader: fragmentShaderId, vertexShader: vertexShaderId, }); var techniqueId = addToArray(techniques, { attributes: techniqueAttributes, program: programId, uniforms: techniqueUniforms, }); return techniqueId; } function getKHRMaterialsCommonValueType(paramName, paramValue) { var value; // Backwards compatibility for COLLADA2GLTF v1.0-draft when it encoding // materials using KHR_materials_common with explicit type/value members if (defined(paramValue.value)) { value = paramValue.value; } else if (defined(paramValue.index)) { value = [paramValue.index]; } else { value = paramValue; } switch (paramName) { case "ambient": return value.length === 1 ? WebGLConstants$1.SAMPLER_2D : WebGLConstants$1.FLOAT_VEC4; case "diffuse": return value.length === 1 ? WebGLConstants$1.SAMPLER_2D : WebGLConstants$1.FLOAT_VEC4; case "emission": return value.length === 1 ? WebGLConstants$1.SAMPLER_2D : WebGLConstants$1.FLOAT_VEC4; case "specular": return value.length === 1 ? WebGLConstants$1.SAMPLER_2D : WebGLConstants$1.FLOAT_VEC4; case "shininess": return WebGLConstants$1.FLOAT; case "transparency": return WebGLConstants$1.FLOAT; // these two are usually not used directly within shaders, // they are just added here for completeness case "transparent": return WebGLConstants$1.BOOL; case "doubleSided": return WebGLConstants$1.BOOL; } } function getTechniqueKey(khrMaterialsCommon, primitiveInfo) { var techniqueKey = ""; techniqueKey += "technique:" + khrMaterialsCommon.technique + ";"; var values = khrMaterialsCommon.values; var keys = Object.keys(values).sort(); var keysCount = keys.length; for (var i = 0; i < keysCount; ++i) { var name = keys[i]; if (values.hasOwnProperty(name)) { techniqueKey += name + ":" + getKHRMaterialsCommonValueType(name, values[name]); techniqueKey += ";"; } } var jointCount = defaultValue(khrMaterialsCommon.jointCount, 0); techniqueKey += jointCount.toString() + ";"; if (defined(primitiveInfo)) { var skinningInfo = primitiveInfo.skinning; if (jointCount > 0) { techniqueKey += skinningInfo.type + ";"; } techniqueKey += primitiveInfo.hasVertexColors; } return techniqueKey; } function lightDefaults(gltf) { var khrMaterialsCommon = gltf.extensions.KHR_materials_common; if (!defined(khrMaterialsCommon) || !defined(khrMaterialsCommon.lights)) { return; } var lights = khrMaterialsCommon.lights; var lightsLength = lights.length; for (var lightId = 0; lightId < lightsLength; lightId++) { var light = lights[lightId]; if (light.type === "ambient") { if (!defined(light.ambient)) { light.ambient = {}; } var ambientLight = light.ambient; if (!defined(ambientLight.color)) { ambientLight.color = [1.0, 1.0, 1.0]; } } else if (light.type === "directional") { if (!defined(light.directional)) { light.directional = {}; } var directionalLight = light.directional; if (!defined(directionalLight.color)) { directionalLight.color = [1.0, 1.0, 1.0]; } } else if (light.type === "point") { if (!defined(light.point)) { light.point = {}; } var pointLight = light.point; if (!defined(pointLight.color)) { pointLight.color = [1.0, 1.0, 1.0]; } pointLight.constantAttenuation = defaultValue( pointLight.constantAttenuation, 1.0 ); pointLight.linearAttenuation = defaultValue( pointLight.linearAttenuation, 0.0 ); pointLight.quadraticAttenuation = defaultValue( pointLight.quadraticAttenuation, 0.0 ); } else if (light.type === "spot") { if (!defined(light.spot)) { light.spot = {}; } var spotLight = light.spot; if (!defined(spotLight.color)) { spotLight.color = [1.0, 1.0, 1.0]; } spotLight.constantAttenuation = defaultValue( spotLight.constantAttenuation, 1.0 ); spotLight.fallOffAngle = defaultValue(spotLight.fallOffAngle, 3.14159265); spotLight.fallOffExponent = defaultValue(spotLight.fallOffExponent, 0.0); spotLight.linearAttenuation = defaultValue( spotLight.linearAttenuation, 0.0 ); spotLight.quadraticAttenuation = defaultValue( spotLight.quadraticAttenuation, 0.0 ); } } } /** * @private */ function processPbrMaterials(gltf, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); // No need to create new techniques if they already exist, // the shader should handle these values if (hasExtension(gltf, "KHR_techniques_webgl")) { return gltf; } // All materials in glTF are PBR by default, // so we should apply PBR unless no materials are found. if (!defined(gltf.materials) || gltf.materials.length === 0) { return gltf; } if (!defined(gltf.extensions)) { gltf.extensions = {}; } if (!defined(gltf.extensionsUsed)) { gltf.extensionsUsed = []; } if (!defined(gltf.extensionsRequired)) { gltf.extensionsRequired = []; } gltf.extensions.KHR_techniques_webgl = { programs: [], shaders: [], techniques: [], }; gltf.extensionsUsed.push("KHR_techniques_webgl"); gltf.extensionsRequired.push("KHR_techniques_webgl"); var primitiveByMaterial = ModelUtility.splitIncompatibleMaterials(gltf); ForEach.material(gltf, function (material, materialIndex) { var generatedMaterialValues = {}; var technique = generateTechnique( gltf, material, materialIndex, generatedMaterialValues, primitiveByMaterial, options ); if (!defined(material.extensions)) { material.extensions = {}; } material.extensions.KHR_techniques_webgl = { values: generatedMaterialValues, technique: technique, }; }); // If any primitives have semantics that aren't declared in the generated // shaders, we want to preserve them. ModelUtility.ensureSemanticExistence(gltf); return gltf; } function isSpecularGlossinessMaterial(material) { return ( defined(material.extensions) && defined(material.extensions.KHR_materials_pbrSpecularGlossiness) ); } function addTextureCoordinates( gltf, textureName, generatedMaterialValues, defaultTexCoord, result ) { var texCoord; var texInfo = generatedMaterialValues[textureName]; if (defined(texInfo) && defined(texInfo.texCoord) && texInfo.texCoord === 1) { defaultTexCoord = defaultTexCoord.replace("0", "1"); } if (defined(generatedMaterialValues[textureName + "Offset"])) { texCoord = textureName + "Coord"; result.fragmentShaderMain += " vec2 " + texCoord + " = computeTexCoord(" + defaultTexCoord + ", " + textureName + "Offset, " + textureName + "Rotation, " + textureName + "Scale);\n"; } else { texCoord = defaultTexCoord; } return texCoord; } var DEFAULT_TEXTURE_OFFSET = [0.0, 0.0]; var DEFAULT_TEXTURE_ROTATION = [0.0]; var DEFAULT_TEXTURE_SCALE = [1.0, 1.0]; function handleKHRTextureTransform( parameterName, value, generatedMaterialValues ) { if ( parameterName.indexOf("Texture") === -1 || !defined(value.extensions) || !defined(value.extensions.KHR_texture_transform) ) { return; } var uniformName = "u_" + parameterName; var extension = value.extensions.KHR_texture_transform; generatedMaterialValues[uniformName + "Offset"] = defaultValue( extension.offset, DEFAULT_TEXTURE_OFFSET ); generatedMaterialValues[uniformName + "Rotation"] = defaultValue( extension.rotation, DEFAULT_TEXTURE_ROTATION ); generatedMaterialValues[uniformName + "Scale"] = defaultValue( extension.scale, DEFAULT_TEXTURE_SCALE ); if (defined(value.texCoord) && defined(extension.texCoord)) { generatedMaterialValues[uniformName].texCoord = extension.texCoord; } } function generateTechnique( gltf, material, materialIndex, generatedMaterialValues, primitiveByMaterial, options ) { var addBatchIdToGeneratedShaders = defaultValue( options.addBatchIdToGeneratedShaders, false ); var techniquesWebgl = gltf.extensions.KHR_techniques_webgl; var techniques = techniquesWebgl.techniques; var shaders = techniquesWebgl.shaders; var programs = techniquesWebgl.programs; var useSpecGloss = isSpecularGlossinessMaterial(material); var uniformName; var parameterName; var value; var pbrMetallicRoughness = material.pbrMetallicRoughness; if (defined(pbrMetallicRoughness) && !useSpecGloss) { for (parameterName in pbrMetallicRoughness) { if (pbrMetallicRoughness.hasOwnProperty(parameterName)) { value = pbrMetallicRoughness[parameterName]; uniformName = "u_" + parameterName; generatedMaterialValues[uniformName] = value; handleKHRTextureTransform( parameterName, value, generatedMaterialValues ); } } } if (useSpecGloss) { var pbrSpecularGlossiness = material.extensions.KHR_materials_pbrSpecularGlossiness; for (parameterName in pbrSpecularGlossiness) { if (pbrSpecularGlossiness.hasOwnProperty(parameterName)) { value = pbrSpecularGlossiness[parameterName]; uniformName = "u_" + parameterName; generatedMaterialValues[uniformName] = value; handleKHRTextureTransform( parameterName, value, generatedMaterialValues ); } } } for (var additional in material) { if ( material.hasOwnProperty(additional) && (additional.indexOf("Texture") >= 0 || additional.indexOf("Factor") >= 0) ) { value = material[additional]; uniformName = "u_" + additional; generatedMaterialValues[uniformName] = value; handleKHRTextureTransform(additional, value, generatedMaterialValues); } } var vertexShader = "precision highp float;\n"; var fragmentShader = "precision highp float;\n"; var skin; if (defined(gltf.skins)) { skin = gltf.skins[0]; } var joints = defined(skin) ? skin.joints : []; var jointCount = joints.length; var primitiveInfo = primitiveByMaterial[materialIndex]; var skinningInfo; var hasSkinning = false; var hasVertexColors = false; var hasMorphTargets = false; var hasNormals = false; var hasTangents = false; var hasTexCoords = false; var hasTexCoord1 = false; var hasOutline = false; var isUnlit = false; //【hongguo】 田洪果 倾斜模型编辑 var isTrustGltf = options.modelEditor && options.modelEditor.enable; if (defined(primitiveInfo)) { skinningInfo = primitiveInfo.skinning; hasSkinning = skinningInfo.skinned && joints.length > 0; hasVertexColors = primitiveInfo.hasVertexColors; hasMorphTargets = primitiveInfo.hasMorphTargets; hasNormals = primitiveInfo.hasNormals; hasTangents = primitiveInfo.hasTangents; hasTexCoords = primitiveInfo.hasTexCoords; hasTexCoord1 = primitiveInfo.hasTexCoord1; hasOutline = primitiveInfo.hasOutline; } var morphTargets; if (hasMorphTargets) { ForEach.mesh(gltf, function (mesh) { ForEach.meshPrimitive(mesh, function (primitive) { if (primitive.material === materialIndex) { var targets = primitive.targets; if (defined(targets)) { morphTargets = targets; } } }); }); } // Add techniques var techniqueUniforms = { // Add matrices u_modelViewMatrix: { semantic: hasExtension(gltf, "CESIUM_RTC") ? "CESIUM_RTC_MODELVIEW" : "MODELVIEW", type: WebGLConstants$1.FLOAT_MAT4, }, u_projectionMatrix: { semantic: "PROJECTION", type: WebGLConstants$1.FLOAT_MAT4, }, }; if ( defined(material.extensions) && defined(material.extensions.KHR_materials_unlit) ) { isUnlit = true; } if (hasNormals) { techniqueUniforms.u_normalMatrix = { semantic: "MODELVIEWINVERSETRANSPOSE", type: WebGLConstants$1.FLOAT_MAT3, }; } if (hasSkinning) { techniqueUniforms.u_jointMatrix = { count: jointCount, semantic: "JOINTMATRIX", type: WebGLConstants$1.FLOAT_MAT4, }; } if (hasMorphTargets) { techniqueUniforms.u_morphWeights = { count: morphTargets.length, semantic: "MORPHWEIGHTS", type: WebGLConstants$1.FLOAT, }; } var alphaMode = material.alphaMode; if (defined(alphaMode) && alphaMode === "MASK") { techniqueUniforms.u_alphaCutoff = { semantic: "ALPHACUTOFF", type: WebGLConstants$1.FLOAT, }; } // Add material values for (uniformName in generatedMaterialValues) { if (generatedMaterialValues.hasOwnProperty(uniformName)) { techniqueUniforms[uniformName] = { type: getPBRValueType(uniformName), }; } } var baseColorUniform = defaultValue( techniqueUniforms.u_baseColorTexture, techniqueUniforms.u_baseColorFactor ); if (defined(baseColorUniform)) { baseColorUniform.semantic = "_3DTILESDIFFUSE"; } // Add uniforms to shaders for (uniformName in techniqueUniforms) { if (techniqueUniforms.hasOwnProperty(uniformName)) { var uniform = techniqueUniforms[uniformName]; var arraySize = defined(uniform.count) ? "[" + uniform.count + "]" : ""; if ( (uniform.type !== WebGLConstants$1.FLOAT_MAT3 && uniform.type !== WebGLConstants$1.FLOAT_MAT4 && uniformName !== "u_morphWeights") || uniform.useInFragment ) { fragmentShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; delete uniform.useInFragment; } else { vertexShader += "uniform " + webGLConstantToGlslType(uniform.type) + " " + uniformName + arraySize + ";\n"; } } } //【hongguo】 田洪果 倾斜模型编辑 if(isTrustGltf){ fragmentShader += 'uniform vec4 floodVar;\n'; fragmentShader += 'uniform vec4 floodColor;\n'; } //【hongguo】 田洪果 倾斜模型编辑 if (hasOutline) { fragmentShader += "uniform sampler2D u_outlineTexture;\n"; } // Add attributes with semantics var vertexShaderMain = ""; if (hasSkinning) { vertexShaderMain += " mat4 skinMatrix =\n" + " a_weight.x * u_jointMatrix[int(a_joint.x)] +\n" + " a_weight.y * u_jointMatrix[int(a_joint.y)] +\n" + " a_weight.z * u_jointMatrix[int(a_joint.z)] +\n" + " a_weight.w * u_jointMatrix[int(a_joint.w)];\n"; } // Add position always var techniqueAttributes = { a_position: { semantic: "POSITION", }, }; if (hasOutline) { techniqueAttributes.a_outlineCoordinates = { semantic: "_OUTLINE_COORDINATES", }; } vertexShader += "attribute vec3 a_position;\n"; //【hongguo】 田洪果 建筑物特效 vertexShader += 'varying vec3 v_stcVertex;\n'; vertexShaderMain += 'v_stcVertex = a_position;\n'; fragmentShader += 'varying vec3 v_stcVertex;\n'; //【hongguo】 田洪果 建筑物特效 if (hasNormals) { vertexShader += "varying vec3 v_positionEC;\n"; } if (hasOutline) { vertexShader += "attribute vec3 a_outlineCoordinates;\n"; vertexShader += "varying vec3 v_outlineCoordinates;\n"; } // Morph Target Weighting vertexShaderMain += " vec3 weightedPosition = a_position;\n"; //【hongguo】 田洪果 添加偏移调整着色器代码 倾斜模型编辑 vertexShaderMain += ' vec3 tempaPos = a_position;\n'; if(isTrustGltf){ vertexShaderMain += ' tempaPos = tempaPos - b3dmOffset;\n'; } vertexShader += 'bool isPointInBound(vec2 point, vec4 bounds)\n'+ '{\n'+ ' return (point.x>bounds.x&&point.xbounds.y);\n'+ '}\n'+ 'float unpackDepth(const in vec4 rgba_depth)\n'+ '{\n'+ ' const vec4 bitShifts = vec4(1.0, 1.0 / 255.0, 1.0 / (255.0 * 255.0), 1.0 / (255.0 * 255.0 * 255.0));\n'+ ' float depth=dot(rgba_depth, bitShifts);\n'+ ' return depth;\n'+ '}\n'; //【hongguo】 田洪果 倾斜模型编辑 if (hasNormals) { vertexShaderMain += " vec3 weightedNormal = a_normal;\n"; } if (hasTangents) { vertexShaderMain += " vec4 weightedTangent = a_tangent;\n"; } if (hasMorphTargets) { for (var k = 0; k < morphTargets.length; k++) { var targetAttributes = morphTargets[k]; for (var targetAttribute in targetAttributes) { if ( targetAttributes.hasOwnProperty(targetAttribute) && targetAttribute !== "extras" ) { var attributeName = "a_" + targetAttribute + "_" + k; techniqueAttributes[attributeName] = { semantic: targetAttribute + "_" + k, }; vertexShader += "attribute vec3 " + attributeName + ";\n"; if (targetAttribute === "POSITION") { vertexShaderMain += " weightedPosition += u_morphWeights[" + k + "] * " + attributeName + ";\n"; } else if (targetAttribute === "NORMAL") { vertexShaderMain += " weightedNormal += u_morphWeights[" + k + "] * " + attributeName + ";\n"; } else if (hasTangents && targetAttribute === "TANGENT") { vertexShaderMain += " weightedTangent.xyz += u_morphWeights[" + k + "] * " + attributeName + ";\n"; } } } } } // Final position computation if (hasSkinning) { vertexShaderMain += " vec4 position = skinMatrix * vec4(weightedPosition, 1.0);\n"; } else { vertexShaderMain += " vec4 position = vec4(weightedPosition, 1.0);\n"; } vertexShaderMain += " position = u_modelViewMatrix * position;\n"; if (hasNormals) { vertexShaderMain += " v_positionEC = position.xyz;\n"; } vertexShaderMain += " gl_Position = u_projectionMatrix * position;\n"; if (hasOutline) { vertexShaderMain += " v_outlineCoordinates = a_outlineCoordinates;\n"; } //【hongguo】 田洪果 倾斜模型编辑 if(isTrustGltf){ fragmentShader += 'varying float v_isFlat;\n'; fragmentShader += 'uniform bvec4 IsYaPing;\n'; fragmentShader += 'uniform bvec4 editVar;\n'; } //【hongguo】 田洪果 倾斜模型编辑 // Final normal computation if (hasNormals) { techniqueAttributes.a_normal = { semantic: "NORMAL", }; vertexShader += "attribute vec3 a_normal;\n"; if (!isUnlit) { vertexShader += "varying vec3 v_normal;\n"; if (hasSkinning) { vertexShaderMain += " v_normal = u_normalMatrix * mat3(skinMatrix) * weightedNormal;\n"; } else { vertexShaderMain += " v_normal = u_normalMatrix * weightedNormal;\n"; } fragmentShader += "varying vec3 v_normal;\n"; } fragmentShader += "varying vec3 v_positionEC;\n"; } // Read tangents if available if (hasTangents) { techniqueAttributes.a_tangent = { semantic: "TANGENT", }; vertexShader += "attribute vec4 a_tangent;\n"; vertexShader += "varying vec4 v_tangent;\n"; vertexShaderMain += " v_tangent.xyz = u_normalMatrix * weightedTangent.xyz;\n"; vertexShaderMain += " v_tangent.w = weightedTangent.w;\n"; fragmentShader += "varying vec4 v_tangent;\n"; } if (hasOutline) { fragmentShader += "varying vec3 v_outlineCoordinates;\n"; } var fragmentShaderMain = ""; // Add texture coordinates if the material uses them var v_texCoord; var normalTexCoord; var baseColorTexCoord; var specularGlossinessTexCoord; var diffuseTexCoord; var metallicRoughnessTexCoord; var occlusionTexCoord; var emissiveTexCoord; if (hasTexCoords) { techniqueAttributes.a_texcoord_0 = { semantic: "TEXCOORD_0", }; v_texCoord = "v_texcoord_0"; vertexShader += "attribute vec2 a_texcoord_0;\n"; vertexShader += "varying vec2 " + v_texCoord + ";\n"; vertexShaderMain += " " + v_texCoord + " = a_texcoord_0;\n"; fragmentShader += "varying vec2 " + v_texCoord + ";\n"; if (hasTexCoord1) { techniqueAttributes.a_texcoord_1 = { semantic: "TEXCOORD_1", }; var v_texCoord1 = v_texCoord.replace("0", "1"); vertexShader += "attribute vec2 a_texcoord_1;\n"; vertexShader += "varying vec2 " + v_texCoord1 + ";\n"; vertexShaderMain += " " + v_texCoord1 + " = a_texcoord_1;\n"; fragmentShader += "varying vec2 " + v_texCoord1 + ";\n"; } var result = { fragmentShaderMain: fragmentShaderMain, }; normalTexCoord = addTextureCoordinates( gltf, "u_normalTexture", generatedMaterialValues, v_texCoord, result ); baseColorTexCoord = addTextureCoordinates( gltf, "u_baseColorTexture", generatedMaterialValues, v_texCoord, result ); specularGlossinessTexCoord = addTextureCoordinates( gltf, "u_specularGlossinessTexture", generatedMaterialValues, v_texCoord, result ); diffuseTexCoord = addTextureCoordinates( gltf, "u_diffuseTexture", generatedMaterialValues, v_texCoord, result ); metallicRoughnessTexCoord = addTextureCoordinates( gltf, "u_metallicRoughnessTexture", generatedMaterialValues, v_texCoord, result ); occlusionTexCoord = addTextureCoordinates( gltf, "u_occlusionTexture", generatedMaterialValues, v_texCoord, result ); emissiveTexCoord = addTextureCoordinates( gltf, "u_emissiveTexture", generatedMaterialValues, v_texCoord, result ); fragmentShaderMain = result.fragmentShaderMain; } // Add skinning information if available if (hasSkinning) { techniqueAttributes.a_joint = { semantic: "JOINTS_0", }; techniqueAttributes.a_weight = { semantic: "WEIGHTS_0", }; vertexShader += "attribute vec4 a_joint;\n"; vertexShader += "attribute vec4 a_weight;\n"; } if (hasVertexColors) { techniqueAttributes.a_vertexColor = { semantic: "COLOR_0", }; vertexShader += "attribute vec4 a_vertexColor;\n"; vertexShader += "varying vec4 v_vertexColor;\n"; vertexShaderMain += " v_vertexColor = a_vertexColor;\n"; fragmentShader += "varying vec4 v_vertexColor;\n"; } if (addBatchIdToGeneratedShaders) { techniqueAttributes.a_batchId = { semantic: "_BATCHID", }; vertexShader += "attribute float a_batchId;\n"; } //【hongguo】 田洪果 倾斜模型编辑 if(isTrustGltf){ vertexShader += 'uniform bvec4 IsYaPing;\n'; vertexShader += 'uniform bvec4 editVar;\n'; vertexShader += 'uniform vec3 b3dmOffset;\n'; vertexShader += 'uniform sampler2D u_polygonTexture;\n'; vertexShader += 'uniform vec4 u_polygonBounds;\n'; vertexShader += 'uniform vec2 u_heightVar;\n'; vertexShader += 'varying float v_isFlat;\n'; } //【hongguo】 田洪果 倾斜模型编辑 vertexShader += "void main(void) \n{\n"; //【hongguo】 田洪果 倾斜模型编辑 //仿dataEarth的着色器,实现压平代码,大致意思是通过解压深度平均采样优化压平的边界 if(isTrustGltf){ vertexShaderMain += 'if(IsYaPing[0]){\n'; vertexShaderMain += 'if (isPointInBound(tempaPos.xy, u_polygonBounds)){\n'; vertexShaderMain += ' vec2 texCoord;\n'; vertexShaderMain += ' texCoord.x = (tempaPos.x-u_polygonBounds.x)/(u_polygonBounds.z-u_polygonBounds.x);\n'; vertexShaderMain += ' texCoord.y = (tempaPos.y-u_polygonBounds.y)/(u_polygonBounds.w-u_polygonBounds.y);\n'; vertexShaderMain += ' float texelSize = 1.0/4096.0;\n'; vertexShaderMain += ' float depth0 = unpackDepth(texture2D(u_polygonTexture, texCoord.xy));\n'; vertexShaderMain += ' float depth1 = unpackDepth(texture2D(u_polygonTexture, texCoord.xy + vec2(-texelSize,0.0)));\n'; vertexShaderMain += ' float depth2 = unpackDepth(texture2D(u_polygonTexture, texCoord.xy + vec2(texelSize,0.0)));\n'; vertexShaderMain += ' float depth3 = unpackDepth(texture2D(u_polygonTexture, texCoord.xy + vec2(0.0,-texelSize)));\n'; vertexShaderMain += ' float depth4 = unpackDepth(texture2D(u_polygonTexture, texCoord.xy + vec2(0.0,texelSize)));\n'; vertexShaderMain += ' float depth = max( max( max( max( depth0, depth1),depth2), depth3), depth4);\n'; vertexShaderMain += ' float z = (depth - 0.5)*2.0*5000.0;\n'; vertexShaderMain += ' if(abs(depth) > 0.00001){\n'; vertexShaderMain += ' v_isFlat = 10.0;\n'; vertexShaderMain += ' if(IsYaPing[2]||IsYaPing[3]){return;}\n'; vertexShaderMain += ' if(IsYaPing[1]){\n'; vertexShaderMain += ' weightedPosition.z = u_heightVar[0] + u_heightVar[1];\n'; vertexShaderMain += ' weightedPosition.z = 0.00000;\n'; vertexShaderMain += ' gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(weightedPosition,1.0);\n'; vertexShaderMain += ' }\n'; vertexShaderMain += ' }else{v_isFlat = -10.0;}\n'; vertexShaderMain += '}else{v_isFlat = -10.0;}\n'; vertexShaderMain += '};\n'; } //【hongguo】 田洪果 倾斜模型编辑 vertexShader += vertexShaderMain; vertexShader += "}\n"; // Fragment shader lighting if (hasNormals && !isUnlit) { fragmentShader += "const float M_PI = 3.141592653589793;\n"; fragmentShader += "vec3 lambertianDiffuse(vec3 diffuseColor) \n" + "{\n" + " return diffuseColor / M_PI;\n" + "}\n\n"; fragmentShader += "vec3 fresnelSchlick2(vec3 f0, vec3 f90, float VdotH) \n" + "{\n" + " return f0 + (f90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);\n" + "}\n\n"; fragmentShader += "vec3 fresnelSchlick(float metalness, float VdotH) \n" + "{\n" + " return metalness + (vec3(1.0) - metalness) * pow(1.0 - VdotH, 5.0);\n" + "}\n\n"; fragmentShader += "float smithVisibilityG1(float NdotV, float roughness) \n" + "{\n" + " float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;\n" + " return NdotV / (NdotV * (1.0 - k) + k);\n" + "}\n\n"; fragmentShader += "float smithVisibilityGGX(float roughness, float NdotL, float NdotV) \n" + "{\n" + " return smithVisibilityG1(NdotL, roughness) * smithVisibilityG1(NdotV, roughness);\n" + "}\n\n"; fragmentShader += "float GGX(float roughness, float NdotH) \n" + "{\n" + " float roughnessSquared = roughness * roughness;\n" + " float f = (NdotH * roughnessSquared - NdotH) * NdotH + 1.0;\n" + " return roughnessSquared / (M_PI * f * f);\n" + "}\n\n"; } fragmentShader += "vec3 SRGBtoLINEAR3(vec3 srgbIn) \n" + "{\n" + " return pow(srgbIn, vec3(2.2));\n" + "}\n\n"; fragmentShader += "vec4 SRGBtoLINEAR4(vec4 srgbIn) \n" + "{\n" + " vec3 linearOut = pow(srgbIn.rgb, vec3(2.2));\n" + " return vec4(linearOut, srgbIn.a);\n" + "}\n\n"; fragmentShader += "vec3 applyTonemapping(vec3 linearIn) \n" + "{\n" + "#ifndef HDR \n" + " return czm_acesTonemapping(linearIn);\n" + "#else \n" + " return linearIn;\n" + "#endif \n" + "}\n\n"; fragmentShader += "vec3 LINEARtoSRGB(vec3 linearIn) \n" + "{\n" + "#ifndef HDR \n" + " return pow(linearIn, vec3(1.0/2.2));\n" + "#else \n" + " return linearIn;\n" + "#endif \n" + "}\n\n"; fragmentShader += "vec2 computeTexCoord(vec2 texCoords, vec2 offset, float rotation, vec2 scale) \n" + "{\n" + " rotation = -rotation; \n" + " mat3 transform = mat3(\n" + " cos(rotation) * scale.x, sin(rotation) * scale.x, 0.0, \n" + " -sin(rotation) * scale.y, cos(rotation) * scale.y, 0.0, \n" + " offset.x, offset.y, 1.0); \n" + " vec2 transformedTexCoords = (transform * vec3(fract(texCoords), 1.0)).xy; \n" + " return transformedTexCoords; \n" + "}\n\n"; fragmentShader += "#ifdef USE_IBL_LIGHTING \n"; fragmentShader += "uniform vec2 gltf_iblFactor; \n"; fragmentShader += "#endif \n"; fragmentShader += "#ifdef USE_CUSTOM_LIGHT_COLOR \n"; fragmentShader += "uniform vec3 gltf_lightColor; \n"; fragmentShader += "#endif \n"; fragmentShader += "void main(void) \n{\n"; fragmentShader += fragmentShaderMain; // Add normal mapping to fragment shader if (hasNormals && !isUnlit) { fragmentShader += " vec3 ng = normalize(v_normal);\n"; fragmentShader += " vec3 positionWC = vec3(czm_inverseView * vec4(v_positionEC, 1.0));\n"; if (defined(generatedMaterialValues.u_normalTexture)) { if (hasTangents) { // Read tangents from varying fragmentShader += " vec3 t = normalize(v_tangent.xyz);\n"; fragmentShader += " vec3 b = normalize(cross(ng, t) * v_tangent.w);\n"; fragmentShader += " mat3 tbn = mat3(t, b, ng);\n"; fragmentShader += " vec3 n = texture2D(u_normalTexture, " + normalTexCoord + ").rgb;\n"; fragmentShader += " n = normalize(tbn * (2.0 * n - 1.0));\n"; } else { // Add standard derivatives extension fragmentShader = "#ifdef GL_OES_standard_derivatives\n" + "#extension GL_OES_standard_derivatives : enable\n" + "#endif\n" + fragmentShader; // Compute tangents fragmentShader += "#ifdef GL_OES_standard_derivatives\n"; fragmentShader += " vec3 pos_dx = dFdx(v_positionEC);\n"; fragmentShader += " vec3 pos_dy = dFdy(v_positionEC);\n"; fragmentShader += " vec3 tex_dx = dFdx(vec3(" + normalTexCoord + ",0.0));\n"; fragmentShader += " vec3 tex_dy = dFdy(vec3(" + normalTexCoord + ",0.0));\n"; fragmentShader += " vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);\n"; fragmentShader += " t = normalize(t - ng * dot(ng, t));\n"; fragmentShader += " vec3 b = normalize(cross(ng, t));\n"; fragmentShader += " mat3 tbn = mat3(t, b, ng);\n"; fragmentShader += " vec3 n = texture2D(u_normalTexture, " + normalTexCoord + ").rgb;\n"; fragmentShader += " n = normalize(tbn * (2.0 * n - 1.0));\n"; fragmentShader += "#else\n"; fragmentShader += " vec3 n = ng;\n"; fragmentShader += "#endif\n"; } } else { fragmentShader += " vec3 n = ng;\n"; } if (material.doubleSided) { fragmentShader += " if (czm_backFacing())\n"; fragmentShader += " {\n"; fragmentShader += " n = -n;\n"; fragmentShader += " }\n"; } } // Add base color to fragment shader if (defined(generatedMaterialValues.u_baseColorTexture)) { fragmentShader += " vec4 baseColorWithAlpha = SRGBtoLINEAR4(texture2D(u_baseColorTexture, " + baseColorTexCoord + "));\n"; //【hongguo】 田洪果 倾斜模型编辑 //裁剪内部或者外部 if(isTrustGltf){ fragmentShader += 'if(IsYaPing[0]&&IsYaPing[2]){'+ ' if(!editVar[0]&&v_isFlat>1.0){'+ ' discard;'+ ' }'+ ' if(editVar[0]&&v_isFlat<-1.0){'+ ' discard;'+ ' }'+ '}'; } //【hongguo】 田洪果 倾斜模型编辑 if (defined(generatedMaterialValues.u_baseColorFactor)) { fragmentShader += " baseColorWithAlpha *= u_baseColorFactor;\n"; } } else if (defined(generatedMaterialValues.u_baseColorFactor)) { fragmentShader += " vec4 baseColorWithAlpha = u_baseColorFactor;\n"; } else { fragmentShader += " vec4 baseColorWithAlpha = vec4(1.0);\n"; } if (hasVertexColors) { fragmentShader += " baseColorWithAlpha *= v_vertexColor;\n"; } fragmentShader += " vec3 baseColor = baseColorWithAlpha.rgb;\n"; if (hasNormals && !isUnlit) { if (useSpecGloss) { if (defined(generatedMaterialValues.u_specularGlossinessTexture)) { fragmentShader += " vec4 specularGlossiness = SRGBtoLINEAR4(texture2D(u_specularGlossinessTexture, " + specularGlossinessTexCoord + "));\n"; fragmentShader += " vec3 specular = specularGlossiness.rgb;\n"; fragmentShader += " float glossiness = specularGlossiness.a;\n"; if (defined(generatedMaterialValues.u_specularFactor)) { fragmentShader += " specular *= u_specularFactor;\n"; } if (defined(generatedMaterialValues.u_glossinessFactor)) { fragmentShader += " glossiness *= u_glossinessFactor;\n"; } } else { if (defined(generatedMaterialValues.u_specularFactor)) { fragmentShader += " vec3 specular = clamp(u_specularFactor, vec3(0.0), vec3(1.0));\n"; } else { fragmentShader += " vec3 specular = vec3(1.0);\n"; } if (defined(generatedMaterialValues.u_glossinessFactor)) { fragmentShader += " float glossiness = clamp(u_glossinessFactor, 0.0, 1.0);\n"; } else { fragmentShader += " float glossiness = 1.0;\n"; } } if (defined(generatedMaterialValues.u_diffuseTexture)) { fragmentShader += " vec4 diffuse = SRGBtoLINEAR4(texture2D(u_diffuseTexture, " + diffuseTexCoord + "));\n"; if (defined(generatedMaterialValues.u_diffuseFactor)) { fragmentShader += " diffuse *= u_diffuseFactor;\n"; } } else if (defined(generatedMaterialValues.u_diffuseFactor)) { fragmentShader += " vec4 diffuse = clamp(u_diffuseFactor, vec4(0.0), vec4(1.0));\n"; } else { fragmentShader += " vec4 diffuse = vec4(1.0);\n"; } } else if (defined(generatedMaterialValues.u_metallicRoughnessTexture)) { fragmentShader += " vec3 metallicRoughness = texture2D(u_metallicRoughnessTexture, " + metallicRoughnessTexCoord + ").rgb;\n"; fragmentShader += " float metalness = clamp(metallicRoughness.b, 0.0, 1.0);\n"; fragmentShader += " float roughness = clamp(metallicRoughness.g, 0.04, 1.0);\n"; if (defined(generatedMaterialValues.u_metallicFactor)) { fragmentShader += " metalness *= u_metallicFactor;\n"; } if (defined(generatedMaterialValues.u_roughnessFactor)) { fragmentShader += " roughness *= u_roughnessFactor;\n"; } } else { if (defined(generatedMaterialValues.u_metallicFactor)) { fragmentShader += " float metalness = clamp(u_metallicFactor, 0.0, 1.0);\n"; } else { fragmentShader += " float metalness = 1.0;\n"; } if (defined(generatedMaterialValues.u_roughnessFactor)) { fragmentShader += " float roughness = clamp(u_roughnessFactor, 0.04, 1.0);\n"; } else { fragmentShader += " float roughness = 1.0;\n"; } } fragmentShader += " vec3 v = -normalize(v_positionEC);\n"; // Generate fragment shader's lighting block fragmentShader += "#ifndef USE_CUSTOM_LIGHT_COLOR \n"; fragmentShader += " vec3 lightColorHdr = czm_lightColorHdr;\n"; fragmentShader += "#else \n"; fragmentShader += " vec3 lightColorHdr = gltf_lightColor;\n"; fragmentShader += "#endif \n"; fragmentShader += " vec3 l = normalize(czm_lightDirectionEC);\n"; fragmentShader += " vec3 h = normalize(v + l);\n"; fragmentShader += " float NdotL = clamp(dot(n, l), 0.001, 1.0);\n"; fragmentShader += " float NdotV = abs(dot(n, v)) + 0.001;\n"; fragmentShader += " float NdotH = clamp(dot(n, h), 0.0, 1.0);\n"; fragmentShader += " float LdotH = clamp(dot(l, h), 0.0, 1.0);\n"; fragmentShader += " float VdotH = clamp(dot(v, h), 0.0, 1.0);\n"; fragmentShader += " vec3 f0 = vec3(0.04);\n"; // Whether the material uses metallic-roughness or specular-glossiness changes how the BRDF inputs are computed. // It does not change the implementation of the BRDF itself. if (useSpecGloss) { fragmentShader += " float roughness = 1.0 - glossiness;\n"; fragmentShader += " vec3 diffuseColor = diffuse.rgb * (1.0 - max(max(specular.r, specular.g), specular.b));\n"; fragmentShader += " vec3 specularColor = specular;\n"; } else { fragmentShader += " vec3 diffuseColor = baseColor * (1.0 - metalness) * (1.0 - f0);\n"; fragmentShader += " vec3 specularColor = mix(f0, baseColor, metalness);\n"; } fragmentShader += " float alpha = roughness * roughness;\n"; fragmentShader += " float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n"; fragmentShader += " vec3 r90 = vec3(clamp(reflectance * 25.0, 0.0, 1.0));\n"; fragmentShader += " vec3 r0 = specularColor.rgb;\n"; fragmentShader += " vec3 F = fresnelSchlick2(r0, r90, VdotH);\n"; fragmentShader += " float G = smithVisibilityGGX(alpha, NdotL, NdotV);\n"; fragmentShader += " float D = GGX(alpha, NdotH);\n"; fragmentShader += " vec3 diffuseContribution = (1.0 - F) * lambertianDiffuse(diffuseColor);\n"; fragmentShader += " vec3 specularContribution = F * G * D / (4.0 * NdotL * NdotV);\n"; fragmentShader += " vec3 color = NdotL * lightColorHdr * (diffuseContribution + specularContribution);\n"; // Use the procedural IBL if there are no environment maps fragmentShader += "#if defined(USE_IBL_LIGHTING) && !defined(DIFFUSE_IBL) && !defined(SPECULAR_IBL) \n"; fragmentShader += " vec3 r = normalize(czm_inverseViewRotation * normalize(reflect(v, n)));\n"; // Figure out if the reflection vector hits the ellipsoid fragmentShader += " float vertexRadius = length(positionWC);\n"; fragmentShader += " float horizonDotNadir = 1.0 - min(1.0, czm_ellipsoidRadii.x / vertexRadius);\n"; fragmentShader += " float reflectionDotNadir = dot(r, normalize(positionWC));\n"; // Flipping the X vector is a cheap way to get the inverse of czm_temeToPseudoFixed, since that's a rotation about Z. fragmentShader += " r.x = -r.x;\n"; fragmentShader += " r = -normalize(czm_temeToPseudoFixed * r);\n"; fragmentShader += " r.x = -r.x;\n"; fragmentShader += " float inverseRoughness = 1.04 - roughness;\n"; fragmentShader += " inverseRoughness *= inverseRoughness;\n"; fragmentShader += " vec3 sceneSkyBox = textureCube(czm_environmentMap, r).rgb * inverseRoughness;\n"; fragmentShader += " float atmosphereHeight = 0.05;\n"; fragmentShader += " float blendRegionSize = 0.1 * ((1.0 - inverseRoughness) * 8.0 + 1.1 - horizonDotNadir);\n"; fragmentShader += " float blendRegionOffset = roughness * -1.0;\n"; fragmentShader += " float farAboveHorizon = clamp(horizonDotNadir - blendRegionSize * 0.5 + blendRegionOffset, 1.0e-10 - blendRegionSize, 0.99999);\n"; fragmentShader += " float aroundHorizon = clamp(horizonDotNadir + blendRegionSize * 0.5, 1.0e-10 - blendRegionSize, 0.99999);\n"; fragmentShader += " float farBelowHorizon = clamp(horizonDotNadir + blendRegionSize * 1.5, 1.0e-10 - blendRegionSize, 0.99999);\n"; fragmentShader += " float smoothstepHeight = smoothstep(0.0, atmosphereHeight, horizonDotNadir);\n"; fragmentShader += " vec3 belowHorizonColor = mix(vec3(0.1, 0.15, 0.25), vec3(0.4, 0.7, 0.9), smoothstepHeight);\n"; fragmentShader += " vec3 nadirColor = belowHorizonColor * 0.5;\n"; fragmentShader += " vec3 aboveHorizonColor = mix(vec3(0.9, 1.0, 1.2), belowHorizonColor, roughness * 0.5);\n"; fragmentShader += " vec3 blueSkyColor = mix(vec3(0.18, 0.26, 0.48), aboveHorizonColor, reflectionDotNadir * inverseRoughness * 0.5 + 0.75);\n"; fragmentShader += " vec3 zenithColor = mix(blueSkyColor, sceneSkyBox, smoothstepHeight);\n"; fragmentShader += " vec3 blueSkyDiffuseColor = vec3(0.7, 0.85, 0.9);\n"; fragmentShader += " float diffuseIrradianceFromEarth = (1.0 - horizonDotNadir) * (reflectionDotNadir * 0.25 + 0.75) * smoothstepHeight;\n"; fragmentShader += " float diffuseIrradianceFromSky = (1.0 - smoothstepHeight) * (1.0 - (reflectionDotNadir * 0.25 + 0.25));\n"; fragmentShader += " vec3 diffuseIrradiance = blueSkyDiffuseColor * clamp(diffuseIrradianceFromEarth + diffuseIrradianceFromSky, 0.0, 1.0);\n"; fragmentShader += " float notDistantRough = (1.0 - horizonDotNadir * roughness * 0.8);\n"; fragmentShader += " vec3 specularIrradiance = mix(zenithColor, aboveHorizonColor, smoothstep(farAboveHorizon, aroundHorizon, reflectionDotNadir) * notDistantRough);\n"; fragmentShader += " specularIrradiance = mix(specularIrradiance, belowHorizonColor, smoothstep(aroundHorizon, farBelowHorizon, reflectionDotNadir) * inverseRoughness);\n"; fragmentShader += " specularIrradiance = mix(specularIrradiance, nadirColor, smoothstep(farBelowHorizon, 1.0, reflectionDotNadir) * inverseRoughness);\n"; // Luminance model from page 40 of http://silviojemma.com/public/papers/lighting/spherical-harmonic-lighting.pdf fragmentShader += "#ifdef USE_SUN_LUMINANCE \n"; // Angle between sun and zenith fragmentShader += " float LdotZenith = clamp(dot(normalize(czm_inverseViewRotation * l), normalize(positionWC * -1.0)), 0.001, 1.0);\n"; fragmentShader += " float S = acos(LdotZenith);\n"; // Angle between zenith and current pixel fragmentShader += " float NdotZenith = clamp(dot(normalize(czm_inverseViewRotation * n), normalize(positionWC * -1.0)), 0.001, 1.0);\n"; // Angle between sun and current pixel fragmentShader += " float gamma = acos(NdotL);\n"; fragmentShader += " float numerator = ((0.91 + 10.0 * exp(-3.0 * gamma) + 0.45 * pow(NdotL, 2.0)) * (1.0 - exp(-0.32 / NdotZenith)));\n"; fragmentShader += " float denominator = (0.91 + 10.0 * exp(-3.0 * S) + 0.45 * pow(LdotZenith,2.0)) * (1.0 - exp(-0.32));\n"; fragmentShader += " float luminance = gltf_luminanceAtZenith * (numerator / denominator);\n"; fragmentShader += "#endif \n"; fragmentShader += " vec2 brdfLut = texture2D(czm_brdfLut, vec2(NdotV, roughness)).rg;\n"; fragmentShader += " vec3 IBLColor = (diffuseIrradiance * diffuseColor * gltf_iblFactor.x) + (specularIrradiance * SRGBtoLINEAR3(specularColor * brdfLut.x + brdfLut.y) * gltf_iblFactor.y);\n"; fragmentShader += " float maximumComponent = max(max(lightColorHdr.x, lightColorHdr.y), lightColorHdr.z);\n"; fragmentShader += " vec3 lightColor = lightColorHdr / max(maximumComponent, 1.0);\n"; fragmentShader += " IBLColor *= lightColor;\n"; fragmentShader += "#ifdef USE_SUN_LUMINANCE \n"; fragmentShader += " color += IBLColor * luminance;\n"; fragmentShader += "#else \n"; fragmentShader += " color += IBLColor; \n"; fragmentShader += "#endif \n"; // Environment maps were provided, use them for IBL fragmentShader += "#elif defined(DIFFUSE_IBL) || defined(SPECULAR_IBL) \n"; fragmentShader += " const mat3 yUpToZUp = mat3(-1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0); \n"; fragmentShader += " vec3 cubeDir = normalize(yUpToZUp * gltf_iblReferenceFrameMatrix * normalize(reflect(-v, n))); \n"; fragmentShader += "#ifdef DIFFUSE_IBL \n"; fragmentShader += "#ifdef CUSTOM_SPHERICAL_HARMONICS \n"; fragmentShader += " vec3 diffuseIrradiance = czm_sphericalHarmonics(cubeDir, gltf_sphericalHarmonicCoefficients); \n"; fragmentShader += "#else \n"; fragmentShader += " vec3 diffuseIrradiance = czm_sphericalHarmonics(cubeDir, czm_sphericalHarmonicCoefficients); \n"; fragmentShader += "#endif \n"; fragmentShader += "#else \n"; fragmentShader += " vec3 diffuseIrradiance = vec3(0.0); \n"; fragmentShader += "#endif \n"; fragmentShader += "#ifdef SPECULAR_IBL \n"; fragmentShader += " vec2 brdfLut = texture2D(czm_brdfLut, vec2(NdotV, roughness)).rg;\n"; fragmentShader += "#ifdef CUSTOM_SPECULAR_IBL \n"; fragmentShader += " vec3 specularIBL = czm_sampleOctahedralProjection(gltf_specularMap, gltf_specularMapSize, cubeDir, roughness * gltf_maxSpecularLOD, gltf_maxSpecularLOD);\n"; fragmentShader += "#else \n"; fragmentShader += " vec3 specularIBL = czm_sampleOctahedralProjection(czm_specularEnvironmentMaps, czm_specularEnvironmentMapSize, cubeDir, roughness * czm_specularEnvironmentMapsMaximumLOD, czm_specularEnvironmentMapsMaximumLOD);\n"; fragmentShader += "#endif \n"; fragmentShader += " specularIBL *= F * brdfLut.x + brdfLut.y;\n"; fragmentShader += "#else \n"; fragmentShader += " vec3 specularIBL = vec3(0.0); \n"; fragmentShader += "#endif \n"; fragmentShader += " color += diffuseIrradiance * diffuseColor + specularColor * specularIBL;\n"; fragmentShader += "#endif \n"; } else { fragmentShader += " vec3 color = baseColor;\n"; } // Ignore occlusion and emissive when unlit if (!isUnlit) { if (defined(generatedMaterialValues.u_occlusionTexture)) { fragmentShader += " color *= texture2D(u_occlusionTexture, " + occlusionTexCoord + ").r;\n"; } if (defined(generatedMaterialValues.u_emissiveTexture)) { fragmentShader += " vec3 emissive = SRGBtoLINEAR3(texture2D(u_emissiveTexture, " + emissiveTexCoord + ").rgb);\n"; if (defined(generatedMaterialValues.u_emissiveFactor)) { fragmentShader += " emissive *= u_emissiveFactor;\n"; } fragmentShader += " color += emissive;\n"; } else if (defined(generatedMaterialValues.u_emissiveFactor)) { fragmentShader += " color += u_emissiveFactor;\n"; } } if (!isUnlit) { fragmentShader += " color = applyTonemapping(color);\n"; } fragmentShader += " color = LINEARtoSRGB(color);\n"; if (hasOutline) { fragmentShader += " float outlineness = max(\n"; fragmentShader += " texture2D(u_outlineTexture, vec2(v_outlineCoordinates.x, 0.5)).r,\n"; fragmentShader += " max(\n"; fragmentShader += " texture2D(u_outlineTexture, vec2(v_outlineCoordinates.y, 0.5)).r,\n"; fragmentShader += " texture2D(u_outlineTexture, vec2(v_outlineCoordinates.z, 0.5)).r));\n"; fragmentShader += " color = mix(color, vec3(0.0, 0.0, 0.0), outlineness);\n"; } if (defined(alphaMode)) { if (alphaMode === "MASK") { fragmentShader += " if (baseColorWithAlpha.a < u_alphaCutoff) {\n"; fragmentShader += " discard;\n"; fragmentShader += " }\n"; fragmentShader += " gl_FragColor = vec4(color, 1.0);\n"; } else if (alphaMode === "BLEND") { fragmentShader += " gl_FragColor = vec4(color, baseColorWithAlpha.a);\n"; } else { fragmentShader += " gl_FragColor = vec4(color, 1.0);\n"; } } else { fragmentShader += " gl_FragColor = vec4(color, 1.0);\n"; } //【hongguo】 田洪果 建筑物特效 if (options.buildStyle) { fragmentShader += 'float stc_pl = fract(czm_frameNumber / 120.0) * 3.14159265 * 2.0;\n'+ // 每120帧刷新一次顶部光照 'float stc_sd = v_stcVertex.z / 50.0 + sin(stc_pl) * 0.1;\n'+ // 顶部光照从上到下照到距地面50米(原200) 'gl_FragColor *= vec4(stc_sd, stc_sd, stc_sd, 1.0);\n'+ 'float stc_h = clamp(v_stcVertex.z / 100.0, 0.0, 1.0);\n'+ //光线条起始位置100米高度(原200) 'float stc_a13 = fract(czm_frameNumber / 360.0);\n'+ // 每360帧刷新一次光线条(原360) 'stc_a13 = abs(stc_a13 - 0.5) * 2.0;\n'+ 'float stc_diff = step(0.02, abs(stc_h - stc_a13));\n'+ // 光线条宽度0.01(原0.005) // 'float stc_diff1 = step(0.005, abs(stc_h - stc_a13));\n'+ 'gl_FragColor.rgb += gl_FragColor.rgb * (1.0 - stc_diff);\n'; } //【hongguo】 田洪果 建筑物特效 fragmentShader += "}\n"; // Add shaders var vertexShaderId = addToArray(shaders, { type: WebGLConstants$1.VERTEX_SHADER, extras: { _pipeline: { source: vertexShader, extension: ".glsl", }, }, }); var fragmentShaderId = addToArray(shaders, { type: WebGLConstants$1.FRAGMENT_SHADER, extras: { _pipeline: { source: fragmentShader, extension: ".glsl", }, }, }); // Add program var programId = addToArray(programs, { fragmentShader: fragmentShaderId, vertexShader: vertexShaderId, }); var techniqueId = addToArray(techniques, { attributes: techniqueAttributes, program: programId, uniforms: techniqueUniforms, }); return techniqueId; } function getPBRValueType(paramName) { if (paramName.indexOf("Offset") !== -1) { return WebGLConstants$1.FLOAT_VEC2; } else if (paramName.indexOf("Rotation") !== -1) { return WebGLConstants$1.FLOAT; } else if (paramName.indexOf("Scale") !== -1) { return WebGLConstants$1.FLOAT_VEC2; } else if (paramName.indexOf("Texture") !== -1) { return WebGLConstants$1.SAMPLER_2D; } switch (paramName) { case "u_baseColorFactor": return WebGLConstants$1.FLOAT_VEC4; case "u_metallicFactor": return WebGLConstants$1.FLOAT; case "u_roughnessFactor": return WebGLConstants$1.FLOAT; case "u_emissiveFactor": return WebGLConstants$1.FLOAT_VEC3; // Specular Glossiness Types case "u_diffuseFactor": return WebGLConstants$1.FLOAT_VEC4; case "u_specularFactor": return WebGLConstants$1.FLOAT_VEC3; case "u_glossinessFactor": return WebGLConstants$1.FLOAT; } } /** * Describes a renderable batch of geometry. * * @alias Vector3DTileBatch * @constructor * * @param {Object} options An object with the following properties: * @param {Number} options.offset The offset of the batch into the indices buffer. * @param {Number} options.count The number of indices in the batch. * @param {Color} options.color The color of the geometry in the batch. * @param {Number[]} options.batchIds An array where each element is the batch id of the geometry in the batch. * * @private */ function Vector3DTileBatch(options) { /** * The offset of the batch into the indices buffer. * @type {Number} */ this.offset = options.offset; /** * The number of indices in the batch. * @type {Number} */ this.count = options.count; /** * The color of the geometry in the batch. * @type {Color} */ this.color = options.color; /** * An array where each element is the batch id of the geometry in the batch. * @type {Number[]} */ this.batchIds = options.batchIds; } //This file is automatically rebuilt by the Cesium build process. var VectorTileVS = "attribute vec3 position;\n\ attribute float a_batchId;\n\ \n\ uniform mat4 u_modifiedModelViewProjection;\n\ \n\ void main()\n\ {\n\ gl_Position = czm_depthClamp(u_modifiedModelViewProjection * vec4(position, 1.0));\n\ }\n\ "; // JavaScript Expression Parser (JSEP) 0.3.1 // JSEP may be freely distributed under the MIT License // http://jsep.from.so/ var tmp$5 = {}; /*global module: true, exports: true, console: true */ (function (root) { // Node Types // ---------- // This is the full set of types that any JSEP node can be. // Store them here to save space when minified var COMPOUND = 'Compound', IDENTIFIER = 'Identifier', MEMBER_EXP = 'MemberExpression', LITERAL = 'Literal', THIS_EXP = 'ThisExpression', CALL_EXP = 'CallExpression', UNARY_EXP = 'UnaryExpression', BINARY_EXP = 'BinaryExpression', LOGICAL_EXP = 'LogicalExpression', CONDITIONAL_EXP = 'ConditionalExpression', ARRAY_EXP = 'ArrayExpression', PERIOD_CODE = 46, // '.' COMMA_CODE = 44, // ',' SQUOTE_CODE = 39, // single quote DQUOTE_CODE = 34, // double quotes OPAREN_CODE = 40, // ( CPAREN_CODE = 41, // ) OBRACK_CODE = 91, // [ CBRACK_CODE = 93, // ] QUMARK_CODE = 63, // ? SEMCOL_CODE = 59, // ; COLON_CODE = 58, // : throwError = function(message, index) { var error = new Error(message + ' at character ' + index); error.index = index; error.description = message; throw error; }, // Operations // ---------- // Set `t` to `true` to save space (when minified, not gzipped) t = true, // Use a quickly-accessible map to store all of the unary operators // Values are set to `true` (it really doesn't matter) unary_ops = {'-': t, '!': t, '~': t, '+': t}, // Also use a map for the binary operations but set their values to their // binary precedence for quick reference: // see [Order of operations](http://en.wikipedia.org/wiki/Order_of_operations#Programming_language) binary_ops = { '||': 1, '&&': 2, '|': 3, '^': 4, '&': 5, '==': 6, '!=': 6, '===': 6, '!==': 6, '<': 7, '>': 7, '<=': 7, '>=': 7, '<<':8, '>>': 8, '>>>': 8, '+': 9, '-': 9, '*': 10, '/': 10, '%': 10 }, // Get return the longest key length of any object getMaxKeyLen = function(obj) { var max_len = 0, len; for(var key in obj) { if((len = key.length) > max_len && obj.hasOwnProperty(key)) { max_len = len; } } return max_len; }, max_unop_len = getMaxKeyLen(unary_ops), max_binop_len = getMaxKeyLen(binary_ops), // Literals // ---------- // Store the values to return for the various literals we may encounter literals = { 'true': true, 'false': false, 'null': null }, // Except for `this`, which is special. This could be changed to something like `'self'` as well this_str = 'this', // Returns the precedence of a binary operator or `0` if it isn't a binary operator binaryPrecedence = function(op_val) { return binary_ops[op_val] || 0; }, // Utility function (gets called from multiple places) // Also note that `a && b` and `a || b` are *logical* expressions, not binary expressions createBinaryExpression = function (operator, left, right) { var type = (operator === '||' || operator === '&&') ? LOGICAL_EXP : BINARY_EXP; return { type: type, operator: operator, left: left, right: right }; }, // `ch` is a character code in the next three functions isDecimalDigit = function(ch) { return (ch >= 48 && ch <= 57); // 0...9 }, isIdentifierStart = function(ch) { return (ch === 36) || (ch === 95) || // `$` and `_` (ch >= 65 && ch <= 90) || // A...Z (ch >= 97 && ch <= 122) || // a...z (ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator }, isIdentifierPart = function(ch) { return (ch === 36) || (ch === 95) || // `$` and `_` (ch >= 65 && ch <= 90) || // A...Z (ch >= 97 && ch <= 122) || // a...z (ch >= 48 && ch <= 57) || // 0...9 (ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator }, // Parsing // ------- // `expr` is a string with the passed in expression jsep = function(expr) { // `index` stores the character number we are currently at while `length` is a constant // All of the gobbles below will modify `index` as we move along var index = 0, charAtFunc = expr.charAt, charCodeAtFunc = expr.charCodeAt, exprI = function(i) { return charAtFunc.call(expr, i); }, exprICode = function(i) { return charCodeAtFunc.call(expr, i); }, length = expr.length, // Push `index` up to the next non-space character gobbleSpaces = function() { var ch = exprICode(index); // space or tab while(ch === 32 || ch === 9) { ch = exprICode(++index); } }, // The main parsing function. Much of this code is dedicated to ternary expressions gobbleExpression = function() { var test = gobbleBinaryExpression(), consequent, alternate; gobbleSpaces(); if(exprICode(index) === QUMARK_CODE) { // Ternary expression: test ? consequent : alternate index++; consequent = gobbleExpression(); if(!consequent) { throwError('Expected expression', index); } gobbleSpaces(); if(exprICode(index) === COLON_CODE) { index++; alternate = gobbleExpression(); if(!alternate) { throwError('Expected expression', index); } return { type: CONDITIONAL_EXP, test: test, consequent: consequent, alternate: alternate }; } else { throwError('Expected :', index); } } else { return test; } }, // Search for the operation portion of the string (e.g. `+`, `===`) // Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`) // and move down from 3 to 2 to 1 character until a matching binary operation is found // then, return that binary operation gobbleBinaryOp = function() { gobbleSpaces(); var to_check = expr.substr(index, max_binop_len), tc_len = to_check.length; while(tc_len > 0) { if(binary_ops.hasOwnProperty(to_check)) { index += tc_len; return to_check; } to_check = to_check.substr(0, --tc_len); } return false; }, // This function is responsible for gobbling an individual expression, // e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)` gobbleBinaryExpression = function() { var node, biop, prec, stack, biop_info, left, right, i; // First, try to get the leftmost thing // Then, check to see if there's a binary operator operating on that leftmost thing left = gobbleToken(); biop = gobbleBinaryOp(); // If there wasn't a binary operator, just return the leftmost node if(!biop) { return left; } // Otherwise, we need to start a stack to properly place the binary operations in their // precedence structure biop_info = { value: biop, prec: binaryPrecedence(biop)}; right = gobbleToken(); if(!right) { throwError("Expected expression after " + biop, index); } stack = [left, biop_info, right]; // Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm) while((biop = gobbleBinaryOp())) { prec = binaryPrecedence(biop); if(prec === 0) { break; } biop_info = { value: biop, prec: prec }; // Reduce: make a binary expression from the three topmost entries. while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { right = stack.pop(); biop = stack.pop().value; left = stack.pop(); node = createBinaryExpression(biop, left, right); stack.push(node); } node = gobbleToken(); if(!node) { throwError("Expected expression after " + biop, index); } stack.push(biop_info, node); } i = stack.length - 1; node = stack[i]; while(i > 1) { node = createBinaryExpression(stack[i - 1].value, stack[i - 2], node); i -= 2; } return node; }, // An individual part of a binary expression: // e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis) gobbleToken = function() { var ch, to_check, tc_len; gobbleSpaces(); ch = exprICode(index); if(isDecimalDigit(ch) || ch === PERIOD_CODE) { // Char code 46 is a dot `.` which can start off a numeric literal return gobbleNumericLiteral(); } else if(ch === SQUOTE_CODE || ch === DQUOTE_CODE) { // Single or double quotes return gobbleStringLiteral(); } else if(isIdentifierStart(ch) || ch === OPAREN_CODE) { // open parenthesis // `foo`, `bar.baz` return gobbleVariable(); } else if (ch === OBRACK_CODE) { return gobbleArray(); } else { to_check = expr.substr(index, max_unop_len); tc_len = to_check.length; while(tc_len > 0) { if(unary_ops.hasOwnProperty(to_check)) { index += tc_len; return { type: UNARY_EXP, operator: to_check, argument: gobbleToken(), prefix: true }; } to_check = to_check.substr(0, --tc_len); } return false; } }, // Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to // keep track of everything in the numeric literal and then calling `parseFloat` on that string gobbleNumericLiteral = function() { var number = '', ch, chCode; while(isDecimalDigit(exprICode(index))) { number += exprI(index++); } if(exprICode(index) === PERIOD_CODE) { // can start with a decimal marker number += exprI(index++); while(isDecimalDigit(exprICode(index))) { number += exprI(index++); } } ch = exprI(index); if(ch === 'e' || ch === 'E') { // exponent marker number += exprI(index++); ch = exprI(index); if(ch === '+' || ch === '-') { // exponent sign number += exprI(index++); } while(isDecimalDigit(exprICode(index))) { //exponent itself number += exprI(index++); } if(!isDecimalDigit(exprICode(index-1)) ) { throwError('Expected exponent (' + number + exprI(index) + ')', index); } } chCode = exprICode(index); // Check to make sure this isn't a variable name that start with a number (123abc) if(isIdentifierStart(chCode)) { throwError('Variable names cannot start with a number (' + number + exprI(index) + ')', index); } else if(chCode === PERIOD_CODE) { throwError('Unexpected period', index); } return { type: LITERAL, value: parseFloat(number), raw: number }; }, // Parses a string literal, staring with single or double quotes with basic support for escape codes // e.g. `"hello world"`, `'this is\nJSEP'` gobbleStringLiteral = function() { var str = '', quote = exprI(index++), closed = false, ch; while(index < length) { ch = exprI(index++); if(ch === quote) { closed = true; break; } else if(ch === '\\') { // Check for all of the common escape codes ch = exprI(index++); switch(ch) { case 'n': str += '\n'; break; case 'r': str += '\r'; break; case 't': str += '\t'; break; case 'b': str += '\b'; break; case 'f': str += '\f'; break; case 'v': str += '\x0B'; break; default : str += '\\' + ch; } } else { str += ch; } } if(!closed) { throwError('Unclosed quote after "'+str+'"', index); } return { type: LITERAL, value: str, raw: quote + str + quote }; }, // Gobbles only identifiers // e.g.: `foo`, `_value`, `$x1` // Also, this function checks if that identifier is a literal: // (e.g. `true`, `false`, `null`) or `this` gobbleIdentifier = function() { var ch = exprICode(index), start = index, identifier; if(isIdentifierStart(ch)) { index++; } else { throwError('Unexpected ' + exprI(index), index); } while(index < length) { ch = exprICode(index); if(isIdentifierPart(ch)) { index++; } else { break; } } identifier = expr.slice(start, index); if(literals.hasOwnProperty(identifier)) { return { type: LITERAL, value: literals[identifier], raw: identifier }; } else if(identifier === this_str) { return { type: THIS_EXP }; } else { return { type: IDENTIFIER, name: identifier }; } }, // Gobbles a list of arguments within the context of a function call // or array literal. This function also assumes that the opening character // `(` or `[` has already been gobbled, and gobbles expressions and commas // until the terminator character `)` or `]` is encountered. // e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]` gobbleArguments = function(termination) { var ch_i, args = [], node, closed = false; while(index < length) { gobbleSpaces(); ch_i = exprICode(index); if(ch_i === termination) { // done parsing closed = true; index++; break; } else if (ch_i === COMMA_CODE) { // between expressions index++; } else { node = gobbleExpression(); if(!node || node.type === COMPOUND) { throwError('Expected comma', index); } args.push(node); } } if (!closed) { throwError('Expected ' + String.fromCharCode(termination), index); } return args; }, // Gobble a non-literal variable name. This variable name may include properties // e.g. `foo`, `bar.baz`, `foo['bar'].baz` // It also gobbles function calls: // e.g. `Math.acos(obj.angle)` gobbleVariable = function() { var ch_i, node; ch_i = exprICode(index); if(ch_i === OPAREN_CODE) { node = gobbleGroup(); } else { node = gobbleIdentifier(); } gobbleSpaces(); ch_i = exprICode(index); while(ch_i === PERIOD_CODE || ch_i === OBRACK_CODE || ch_i === OPAREN_CODE) { index++; if(ch_i === PERIOD_CODE) { gobbleSpaces(); node = { type: MEMBER_EXP, computed: false, object: node, property: gobbleIdentifier() }; } else if(ch_i === OBRACK_CODE) { node = { type: MEMBER_EXP, computed: true, object: node, property: gobbleExpression() }; gobbleSpaces(); ch_i = exprICode(index); if(ch_i !== CBRACK_CODE) { throwError('Unclosed [', index); } index++; } else if(ch_i === OPAREN_CODE) { // A function call is being made; gobble all the arguments node = { type: CALL_EXP, 'arguments': gobbleArguments(CPAREN_CODE), callee: node }; } gobbleSpaces(); ch_i = exprICode(index); } return node; }, // Responsible for parsing a group of things within parentheses `()` // This function assumes that it needs to gobble the opening parenthesis // and then tries to gobble everything within that parenthesis, assuming // that the next thing it should see is the close parenthesis. If not, // then the expression probably doesn't have a `)` gobbleGroup = function() { index++; var node = gobbleExpression(); gobbleSpaces(); if(exprICode(index) === CPAREN_CODE) { index++; return node; } else { throwError('Unclosed (', index); } }, // Responsible for parsing Array literals `[1, 2, 3]` // This function assumes that it needs to gobble the opening bracket // and then tries to gobble the expressions as arguments. gobbleArray = function() { index++; return { type: ARRAY_EXP, elements: gobbleArguments(CBRACK_CODE) }; }, nodes = [], ch_i, node; while(index < length) { ch_i = exprICode(index); // Expressions can be separated by semicolons, commas, or just inferred without any // separators if(ch_i === SEMCOL_CODE || ch_i === COMMA_CODE) { index++; // ignore separators } else { // Try to gobble each expression individually if((node = gobbleExpression())) { nodes.push(node); // If we weren't able to find a binary expression and are out of room, then // the expression passed in probably has too much } else if(index < length) { throwError('Unexpected "' + exprI(index) + '"', index); } } } // If there's only one expression just try returning the expression if(nodes.length === 1) { return nodes[0]; } else { return { type: COMPOUND, body: nodes }; } }; // To be filled in by the template jsep.version = '0.3.1'; jsep.toString = function() { return 'JavaScript Expression Parser (JSEP) v' + jsep.version; }; /** * @method jsep.addUnaryOp * @param {string} op_name The name of the unary op to add * @return jsep */ jsep.addUnaryOp = function(op_name) { max_unop_len = Math.max(op_name.length, max_unop_len); unary_ops[op_name] = t; return this; }; /** * @method jsep.addBinaryOp * @param {string} op_name The name of the binary op to add * @param {number} precedence The precedence of the binary op (can be a float) * @return jsep */ jsep.addBinaryOp = function(op_name, precedence) { max_binop_len = Math.max(op_name.length, max_binop_len); binary_ops[op_name] = precedence; return this; }; /** * @method jsep.addLiteral * @param {string} literal_name The name of the literal to add * @param {*} literal_value The value of the literal * @return jsep */ jsep.addLiteral = function(literal_name, literal_value) { literals[literal_name] = literal_value; return this; }; /** * @method jsep.removeUnaryOp * @param {string} op_name The name of the unary op to remove * @return jsep */ jsep.removeUnaryOp = function(op_name) { delete unary_ops[op_name]; if(op_name.length === max_unop_len) { max_unop_len = getMaxKeyLen(unary_ops); } return this; }; /** * @method jsep.removeAllUnaryOps * @return jsep */ jsep.removeAllUnaryOps = function() { unary_ops = {}; max_unop_len = 0; return this; }; /** * @method jsep.removeBinaryOp * @param {string} op_name The name of the binary op to remove * @return jsep */ jsep.removeBinaryOp = function(op_name) { delete binary_ops[op_name]; if(op_name.length === max_binop_len) { max_binop_len = getMaxKeyLen(binary_ops); } return this; }; /** * @method jsep.removeAllBinaryOps * @return jsep */ jsep.removeAllBinaryOps = function() { binary_ops = {}; max_binop_len = 0; return this; }; /** * @method jsep.removeLiteral * @param {string} literal_name The name of the literal to remove * @return jsep */ jsep.removeLiteral = function(literal_name) { delete literals[literal_name]; return this; }; /** * @method jsep.removeAllLiterals * @return jsep */ jsep.removeAllLiterals = function() { literals = {}; return this; }; root.jsep = jsep; }(tmp$5)); var jsep = tmp$5.jsep; /** * @private */ var ExpressionNodeType = { VARIABLE: 0, UNARY: 1, BINARY: 2, TERNARY: 3, CONDITIONAL: 4, MEMBER: 5, FUNCTION_CALL: 6, ARRAY: 7, REGEX: 8, VARIABLE_IN_STRING: 9, LITERAL_NULL: 10, LITERAL_BOOLEAN: 11, LITERAL_NUMBER: 12, LITERAL_STRING: 13, LITERAL_COLOR: 14, LITERAL_VECTOR: 15, LITERAL_REGEX: 16, LITERAL_UNDEFINED: 17, BUILTIN_VARIABLE: 18, }; var ExpressionNodeType$1 = Object.freeze(ExpressionNodeType); /** * An expression for a style applied to a {@link Cesium3DTileset}. *

* Evaluates an expression defined using the * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}. *

*

* Implements the {@link StyleExpression} interface. *

* * @alias Expression * @constructor * * @param {String} [expression] The expression defined using the 3D Tiles Styling language. * @param {Object} [defines] Defines in the style. * * @example * var expression = new Cesium.Expression('(regExp("^Chest").test(${County})) && (${YearBuilt} >= 1970)'); * expression.evaluate(feature); // returns true or false depending on the feature's properties * * @example * var expression = new Cesium.Expression('(${Temperature} > 90) ? color("red") : color("white")'); * expression.evaluateColor(feature, result); // returns a Cesium.Color object */ function Expression(expression, defines) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("expression", expression); //>>includeEnd('debug'); this._expression = expression; expression = replaceDefines(expression, defines); expression = replaceVariables(removeBackslashes(expression)); // customize jsep operators jsep.addBinaryOp("=~", 0); jsep.addBinaryOp("!~", 0); var ast; try { ast = jsep(expression); } catch (e) { throw new RuntimeError(e); } this._runtimeAst = createRuntimeAst(this, ast); } Object.defineProperties(Expression.prototype, { /** * Gets the expression defined in the 3D Tiles Styling language. * * @memberof Expression.prototype * * @type {String} * @readonly * * @default undefined */ expression: { get: function () { return this._expression; }, }, }); // Scratch storage manager while evaluating deep expressions. // For example, an expression like dot(vec4(${red}), vec4(${green}) * vec4(${blue}) requires 3 scratch Cartesian4's var scratchStorage = { arrayIndex: 0, arrayArray: [[]], cartesian2Index: 0, cartesian3Index: 0, cartesian4Index: 0, cartesian2Array: [new Cartesian2()], cartesian3Array: [new Cartesian3()], cartesian4Array: [new Cartesian4()], reset: function () { this.arrayIndex = 0; this.cartesian2Index = 0; this.cartesian3Index = 0; this.cartesian4Index = 0; }, getArray: function () { if (this.arrayIndex >= this.arrayArray.length) { this.arrayArray.push([]); } var array = this.arrayArray[this.arrayIndex++]; array.length = 0; return array; }, getCartesian2: function () { if (this.cartesian2Index >= this.cartesian2Array.length) { this.cartesian2Array.push(new Cartesian2()); } return this.cartesian2Array[this.cartesian2Index++]; }, getCartesian3: function () { if (this.cartesian3Index >= this.cartesian3Array.length) { this.cartesian3Array.push(new Cartesian3()); } return this.cartesian3Array[this.cartesian3Index++]; }, getCartesian4: function () { if (this.cartesian4Index >= this.cartesian4Array.length) { this.cartesian4Array.push(new Cartesian4()); } return this.cartesian4Array[this.cartesian4Index++]; }, }; /** * Evaluates the result of an expression, optionally using the provided feature's properties. If the result of * the expression in the * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language} * is of type Boolean, Number, or String, the corresponding JavaScript * primitive type will be returned. If the result is a RegExp, a Javascript RegExp * object will be returned. If the result is a Cartesian2, Cartesian3, or Cartesian4, * a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} object will be returned. If the result argument is * a {@link Color}, the {@link Cartesian4} value is converted to a {@link Color} and then returned. * * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. * @param {Object} [result] The object onto which to store the result. * @returns {Boolean|Number|String|RegExp|Cartesian2|Cartesian3|Cartesian4|Color} The result of evaluating the expression. */ Expression.prototype.evaluate = function (feature, result) { scratchStorage.reset(); var value = this._runtimeAst.evaluate(feature); if (result instanceof Color && value instanceof Cartesian4) { return Color.fromCartesian4(value, result); } if ( value instanceof Cartesian2 || value instanceof Cartesian3 || value instanceof Cartesian4 ) { return value.clone(result); } return value; }; /** * Evaluates the result of a Color expression, optionally using the provided feature's properties. *

* This is equivalent to {@link Expression#evaluate} but always returns a {@link Color} object. *

* * @param {Cesium3DTileFeature} feature The feature whose properties may be used as variables in the expression. * @param {Color} [result] The object in which to store the result * @returns {Color} The modified result parameter or a new Color instance if one was not provided. */ Expression.prototype.evaluateColor = function (feature, result) { scratchStorage.reset(); var color = this._runtimeAst.evaluate(feature); return Color.fromCartesian4(color, result); }; /** * Gets the shader function for this expression. * Returns undefined if the shader function can't be generated from this expression. * * @param {String} functionName Name to give to the generated function. * @param {String} propertyNameMap Maps property variable names to shader attribute names. * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. * @param {String} returnType The return type of the generated function. * * @returns {String} The shader function. * * @private */ Expression.prototype.getShaderFunction = function ( functionName, propertyNameMap, shaderState, returnType ) { var shaderExpression = this.getShaderExpression(propertyNameMap, shaderState); shaderExpression = returnType + " " + functionName + "() \n" + "{ \n" + " return " + shaderExpression + "; \n" + "} \n"; return shaderExpression; }; /** * Gets the shader expression for this expression. * Returns undefined if the shader expression can't be generated from this expression. * * @param {String} propertyNameMap Maps property variable names to shader attribute names. * @param {Object} shaderState Stores information about the generated shader function, including whether it is translucent. * * @returns {String} The shader expression. * * @private */ Expression.prototype.getShaderExpression = function ( propertyNameMap, shaderState ) { return this._runtimeAst.getShaderExpression(propertyNameMap, shaderState); }; var unaryOperators = ["!", "-", "+"]; var binaryOperators = [ "+", "-", "*", "/", "%", "===", "!==", ">", ">=", "<", "<=", "&&", "||", "!~", "=~", ]; var variableRegex = /\${(.*?)}/g; // Matches ${variable_name} var backslashRegex = /\\/g; var backslashReplacement = "@#%"; var replacementRegex = /@#%/g; var scratchColor$k = new Color(); var unaryFunctions = { abs: getEvaluateUnaryComponentwise(Math.abs), sqrt: getEvaluateUnaryComponentwise(Math.sqrt), cos: getEvaluateUnaryComponentwise(Math.cos), sin: getEvaluateUnaryComponentwise(Math.sin), tan: getEvaluateUnaryComponentwise(Math.tan), acos: getEvaluateUnaryComponentwise(Math.acos), asin: getEvaluateUnaryComponentwise(Math.asin), atan: getEvaluateUnaryComponentwise(Math.atan), radians: getEvaluateUnaryComponentwise(CesiumMath.toRadians), degrees: getEvaluateUnaryComponentwise(CesiumMath.toDegrees), sign: getEvaluateUnaryComponentwise(CesiumMath.sign), floor: getEvaluateUnaryComponentwise(Math.floor), ceil: getEvaluateUnaryComponentwise(Math.ceil), round: getEvaluateUnaryComponentwise(Math.round), exp: getEvaluateUnaryComponentwise(Math.exp), exp2: getEvaluateUnaryComponentwise(exp2), log: getEvaluateUnaryComponentwise(Math.log), log2: getEvaluateUnaryComponentwise(log2), fract: getEvaluateUnaryComponentwise(fract), length: length$1, normalize: normalize, }; var binaryFunctions = { atan2: getEvaluateBinaryComponentwise(Math.atan2, false), pow: getEvaluateBinaryComponentwise(Math.pow, false), min: getEvaluateBinaryComponentwise(Math.min, true), max: getEvaluateBinaryComponentwise(Math.max, true), distance: distance, dot: dot, cross: cross, }; var ternaryFunctions = { clamp: getEvaluateTernaryComponentwise(CesiumMath.clamp, true), mix: getEvaluateTernaryComponentwise(CesiumMath.lerp, true), }; function fract(number) { return number - Math.floor(number); } function exp2(exponent) { return Math.pow(2.0, exponent); } function log2(number) { return CesiumMath.log2(number); } function getEvaluateUnaryComponentwise(operation) { return function (call, left) { if (typeof left === "number") { return operation(left); } else if (left instanceof Cartesian2) { return Cartesian2.fromElements( operation(left.x), operation(left.y), scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian3) { return Cartesian3.fromElements( operation(left.x), operation(left.y), operation(left.z), scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian4) { return Cartesian4.fromElements( operation(left.x), operation(left.y), operation(left.z), operation(left.w), scratchStorage.getCartesian4() ); } throw new RuntimeError( 'Function "' + call + '" requires a vector or number argument. Argument is ' + left + "." ); }; } function getEvaluateBinaryComponentwise(operation, allowScalar) { return function (call, left, right) { if (allowScalar && typeof right === "number") { if (typeof left === "number") { return operation(left, right); } else if (left instanceof Cartesian2) { return Cartesian2.fromElements( operation(left.x, right), operation(left.y, right), scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian3) { return Cartesian3.fromElements( operation(left.x, right), operation(left.y, right), operation(left.z, right), scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian4) { return Cartesian4.fromElements( operation(left.x, right), operation(left.y, right), operation(left.z, right), operation(left.w, right), scratchStorage.getCartesian4() ); } } if (typeof left === "number" && typeof right === "number") { return operation(left, right); } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { return Cartesian2.fromElements( operation(left.x, right.x), operation(left.y, right.y), scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { return Cartesian3.fromElements( operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { return Cartesian4.fromElements( operation(left.x, right.x), operation(left.y, right.y), operation(left.z, right.z), operation(left.w, right.w), scratchStorage.getCartesian4() ); } throw new RuntimeError( 'Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + " and " + right + "." ); }; } function getEvaluateTernaryComponentwise(operation, allowScalar) { return function (call, left, right, test) { if (allowScalar && typeof test === "number") { if (typeof left === "number" && typeof right === "number") { return operation(left, right, test); } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { return Cartesian2.fromElements( operation(left.x, right.x, test), operation(left.y, right.y, test), scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { return Cartesian3.fromElements( operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { return Cartesian4.fromElements( operation(left.x, right.x, test), operation(left.y, right.y, test), operation(left.z, right.z, test), operation(left.w, right.w, test), scratchStorage.getCartesian4() ); } } if ( typeof left === "number" && typeof right === "number" && typeof test === "number" ) { return operation(left, right, test); } else if ( left instanceof Cartesian2 && right instanceof Cartesian2 && test instanceof Cartesian2 ) { return Cartesian2.fromElements( operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), scratchStorage.getCartesian2() ); } else if ( left instanceof Cartesian3 && right instanceof Cartesian3 && test instanceof Cartesian3 ) { return Cartesian3.fromElements( operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), scratchStorage.getCartesian3() ); } else if ( left instanceof Cartesian4 && right instanceof Cartesian4 && test instanceof Cartesian4 ) { return Cartesian4.fromElements( operation(left.x, right.x, test.x), operation(left.y, right.y, test.y), operation(left.z, right.z, test.z), operation(left.w, right.w, test.w), scratchStorage.getCartesian4() ); } throw new RuntimeError( 'Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + ", " + right + ", and " + test + "." ); }; } function length$1(call, left) { if (typeof left === "number") { return Math.abs(left); } else if (left instanceof Cartesian2) { return Cartesian2.magnitude(left); } else if (left instanceof Cartesian3) { return Cartesian3.magnitude(left); } else if (left instanceof Cartesian4) { return Cartesian4.magnitude(left); } throw new RuntimeError( 'Function "' + call + '" requires a vector or number argument. Argument is ' + left + "." ); } function normalize(call, left) { if (typeof left === "number") { return 1.0; } else if (left instanceof Cartesian2) { return Cartesian2.normalize(left, scratchStorage.getCartesian2()); } else if (left instanceof Cartesian3) { return Cartesian3.normalize(left, scratchStorage.getCartesian3()); } else if (left instanceof Cartesian4) { return Cartesian4.normalize(left, scratchStorage.getCartesian4()); } throw new RuntimeError( 'Function "' + call + '" requires a vector or number argument. Argument is ' + left + "." ); } function distance(call, left, right) { if (typeof left === "number" && typeof right === "number") { return Math.abs(left - right); } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { return Cartesian2.distance(left, right); } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { return Cartesian3.distance(left, right); } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { return Cartesian4.distance(left, right); } throw new RuntimeError( 'Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + " and " + right + "." ); } function dot(call, left, right) { if (typeof left === "number" && typeof right === "number") { return left * right; } else if (left instanceof Cartesian2 && right instanceof Cartesian2) { return Cartesian2.dot(left, right); } else if (left instanceof Cartesian3 && right instanceof Cartesian3) { return Cartesian3.dot(left, right); } else if (left instanceof Cartesian4 && right instanceof Cartesian4) { return Cartesian4.dot(left, right); } throw new RuntimeError( 'Function "' + call + '" requires vector or number arguments of matching types. Arguments are ' + left + " and " + right + "." ); } function cross(call, left, right) { if (left instanceof Cartesian3 && right instanceof Cartesian3) { return Cartesian3.cross(left, right, scratchStorage.getCartesian3()); } throw new RuntimeError( 'Function "' + call + '" requires vec3 arguments. Arguments are ' + left + " and " + right + "." ); } function Node$1(type, value, left, right, test) { this._type = type; this._value = value; this._left = left; this._right = right; this._test = test; this.evaluate = undefined; setEvaluateFunction(this); } function replaceDefines(expression, defines) { if (!defined(defines)) { return expression; } for (var key in defines) { if (defines.hasOwnProperty(key)) { var definePlaceholder = new RegExp("\\$\\{" + key + "\\}", "g"); var defineReplace = "(" + defines[key] + ")"; if (defined(defineReplace)) { expression = expression.replace(definePlaceholder, defineReplace); } } } return expression; } function removeBackslashes(expression) { return expression.replace(backslashRegex, backslashReplacement); } function replaceBackslashes(expression) { return expression.replace(replacementRegex, "\\"); } function replaceVariables(expression) { var exp = expression; var result = ""; var i = exp.indexOf("${"); while (i >= 0) { // Check if string is inside quotes var openSingleQuote = exp.indexOf("'"); var openDoubleQuote = exp.indexOf('"'); var closeQuote; if (openSingleQuote >= 0 && openSingleQuote < i) { closeQuote = exp.indexOf("'", openSingleQuote + 1); result += exp.substr(0, closeQuote + 1); exp = exp.substr(closeQuote + 1); i = exp.indexOf("${"); } else if (openDoubleQuote >= 0 && openDoubleQuote < i) { closeQuote = exp.indexOf('"', openDoubleQuote + 1); result += exp.substr(0, closeQuote + 1); exp = exp.substr(closeQuote + 1); i = exp.indexOf("${"); } else { result += exp.substr(0, i); var j = exp.indexOf("}"); if (j < 0) { throw new RuntimeError("Unmatched {."); } result += "czm_" + exp.substr(i + 2, j - (i + 2)); exp = exp.substr(j + 1); i = exp.indexOf("${"); } } result += exp; return result; } function parseLiteral(ast) { var type = typeof ast.value; if (ast.value === null) { return new Node$1(ExpressionNodeType$1.LITERAL_NULL, null); } else if (type === "boolean") { return new Node$1(ExpressionNodeType$1.LITERAL_BOOLEAN, ast.value); } else if (type === "number") { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, ast.value); } else if (type === "string") { if (ast.value.indexOf("${") >= 0) { return new Node$1(ExpressionNodeType$1.VARIABLE_IN_STRING, ast.value); } return new Node$1( ExpressionNodeType$1.LITERAL_STRING, replaceBackslashes(ast.value) ); } } function parseCall(expression, ast) { var args = ast.arguments; var argsLength = args.length; var call; var val, left, right; // Member function calls if (ast.callee.type === "MemberExpression") { call = ast.callee.property.name; var object = ast.callee.object; if (call === "test" || call === "exec") { // Make sure this is called on a valid type if (object.callee.name !== "regExp") { throw new RuntimeError(call + " is not a function."); } if (argsLength === 0) { if (call === "test") { return new Node$1(ExpressionNodeType$1.LITERAL_BOOLEAN, false); } return new Node$1(ExpressionNodeType$1.LITERAL_NULL, null); } left = createRuntimeAst(expression, object); right = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.FUNCTION_CALL, call, left, right); } else if (call === "toString") { val = createRuntimeAst(expression, object); return new Node$1(ExpressionNodeType$1.FUNCTION_CALL, call, val); } throw new RuntimeError('Unexpected function call "' + call + '".'); } // Non-member function calls call = ast.callee.name; if (call === "color") { if (argsLength === 0) { return new Node$1(ExpressionNodeType$1.LITERAL_COLOR, call); } val = createRuntimeAst(expression, args[0]); if (defined(args[1])) { var alpha = createRuntimeAst(expression, args[1]); return new Node$1(ExpressionNodeType$1.LITERAL_COLOR, call, [val, alpha]); } return new Node$1(ExpressionNodeType$1.LITERAL_COLOR, call, [val]); } else if (call === "rgb" || call === "hsl") { if (argsLength < 3) { throw new RuntimeError(call + " requires three arguments."); } val = [ createRuntimeAst(expression, args[0]), createRuntimeAst(expression, args[1]), createRuntimeAst(expression, args[2]), ]; return new Node$1(ExpressionNodeType$1.LITERAL_COLOR, call, val); } else if (call === "rgba" || call === "hsla") { if (argsLength < 4) { throw new RuntimeError(call + " requires four arguments."); } val = [ createRuntimeAst(expression, args[0]), createRuntimeAst(expression, args[1]), createRuntimeAst(expression, args[2]), createRuntimeAst(expression, args[3]), ]; return new Node$1(ExpressionNodeType$1.LITERAL_COLOR, call, val); } else if (call === "vec2" || call === "vec3" || call === "vec4") { // Check for invalid constructors at evaluation time val = new Array(argsLength); for (var i = 0; i < argsLength; ++i) { val[i] = createRuntimeAst(expression, args[i]); } return new Node$1(ExpressionNodeType$1.LITERAL_VECTOR, call, val); } else if (call === "isNaN" || call === "isFinite") { if (argsLength === 0) { if (call === "isNaN") { return new Node$1(ExpressionNodeType$1.LITERAL_BOOLEAN, true); } return new Node$1(ExpressionNodeType$1.LITERAL_BOOLEAN, false); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (call === "isExactClass" || call === "isClass") { if (argsLength < 1 || argsLength > 1) { throw new RuntimeError(call + " requires exactly one argument."); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (call === "getExactClassName") { if (argsLength > 0) { throw new RuntimeError(call + " does not take any argument."); } return new Node$1(ExpressionNodeType$1.UNARY, call); } else if (defined(unaryFunctions[call])) { if (argsLength !== 1) { throw new RuntimeError(call + " requires exactly one argument."); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (defined(binaryFunctions[call])) { if (argsLength !== 2) { throw new RuntimeError(call + " requires exactly two arguments."); } left = createRuntimeAst(expression, args[0]); right = createRuntimeAst(expression, args[1]); return new Node$1(ExpressionNodeType$1.BINARY, call, left, right); } else if (defined(ternaryFunctions[call])) { if (argsLength !== 3) { throw new RuntimeError(call + " requires exactly three arguments."); } left = createRuntimeAst(expression, args[0]); right = createRuntimeAst(expression, args[1]); var test = createRuntimeAst(expression, args[2]); return new Node$1(ExpressionNodeType$1.TERNARY, call, left, right, test); } else if (call === "Boolean") { if (argsLength === 0) { return new Node$1(ExpressionNodeType$1.LITERAL_BOOLEAN, false); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (call === "Number") { if (argsLength === 0) { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, 0); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (call === "String") { if (argsLength === 0) { return new Node$1(ExpressionNodeType$1.LITERAL_STRING, ""); } val = createRuntimeAst(expression, args[0]); return new Node$1(ExpressionNodeType$1.UNARY, call, val); } else if (call === "regExp") { return parseRegex(expression, ast); } throw new RuntimeError('Unexpected function call "' + call + '".'); } function parseRegex(expression, ast) { var args = ast.arguments; // no arguments, return default regex if (args.length === 0) { return new Node$1(ExpressionNodeType$1.LITERAL_REGEX, new RegExp()); } var pattern = createRuntimeAst(expression, args[0]); var exp; // optional flag argument supplied if (args.length > 1) { var flags = createRuntimeAst(expression, args[1]); if (isLiteralType(pattern) && isLiteralType(flags)) { try { exp = new RegExp( replaceBackslashes(String(pattern._value)), flags._value ); } catch (e) { throw new RuntimeError(e); } return new Node$1(ExpressionNodeType$1.LITERAL_REGEX, exp); } return new Node$1(ExpressionNodeType$1.REGEX, pattern, flags); } // only pattern argument supplied if (isLiteralType(pattern)) { try { exp = new RegExp(replaceBackslashes(String(pattern._value))); } catch (e) { throw new RuntimeError(e); } return new Node$1(ExpressionNodeType$1.LITERAL_REGEX, exp); } return new Node$1(ExpressionNodeType$1.REGEX, pattern); } function parseKeywordsAndVariables(ast) { if (isVariable(ast.name)) { var name = getPropertyName(ast.name); if (name.substr(0, 8) === "tiles3d_") { return new Node$1(ExpressionNodeType$1.BUILTIN_VARIABLE, name); } return new Node$1(ExpressionNodeType$1.VARIABLE, name); } else if (ast.name === "NaN") { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, NaN); } else if (ast.name === "Infinity") { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, Infinity); } else if (ast.name === "undefined") { return new Node$1(ExpressionNodeType$1.LITERAL_UNDEFINED, undefined); } throw new RuntimeError(ast.name + " is not defined."); } function parseMathConstant(ast) { var name = ast.property.name; if (name === "PI") { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, Math.PI); } else if (name === "E") { return new Node$1(ExpressionNodeType$1.LITERAL_NUMBER, Math.E); } } function parseNumberConstant(ast) { var name = ast.property.name; if (name === "POSITIVE_INFINITY") { return new Node$1( ExpressionNodeType$1.LITERAL_NUMBER, Number.POSITIVE_INFINITY ); } } function parseMemberExpression(expression, ast) { if (ast.object.name === "Math") { return parseMathConstant(ast); } else if (ast.object.name === "Number") { return parseNumberConstant(ast); } var val; var obj = createRuntimeAst(expression, ast.object); if (ast.computed) { val = createRuntimeAst(expression, ast.property); return new Node$1(ExpressionNodeType$1.MEMBER, "brackets", obj, val); } val = new Node$1(ExpressionNodeType$1.LITERAL_STRING, ast.property.name); return new Node$1(ExpressionNodeType$1.MEMBER, "dot", obj, val); } function isLiteralType(node) { return node._type >= ExpressionNodeType$1.LITERAL_NULL; } function isVariable(name) { return name.substr(0, 4) === "czm_"; } function getPropertyName(variable) { return variable.substr(4); } function createRuntimeAst(expression, ast) { var node; var op; var left; var right; if (ast.type === "Literal") { node = parseLiteral(ast); } else if (ast.type === "CallExpression") { node = parseCall(expression, ast); } else if (ast.type === "Identifier") { node = parseKeywordsAndVariables(ast); } else if (ast.type === "UnaryExpression") { op = ast.operator; var child = createRuntimeAst(expression, ast.argument); if (unaryOperators.indexOf(op) > -1) { node = new Node$1(ExpressionNodeType$1.UNARY, op, child); } else { throw new RuntimeError('Unexpected operator "' + op + '".'); } } else if (ast.type === "BinaryExpression") { op = ast.operator; left = createRuntimeAst(expression, ast.left); right = createRuntimeAst(expression, ast.right); if (binaryOperators.indexOf(op) > -1) { node = new Node$1(ExpressionNodeType$1.BINARY, op, left, right); } else { throw new RuntimeError('Unexpected operator "' + op + '".'); } } else if (ast.type === "LogicalExpression") { op = ast.operator; left = createRuntimeAst(expression, ast.left); right = createRuntimeAst(expression, ast.right); if (binaryOperators.indexOf(op) > -1) { node = new Node$1(ExpressionNodeType$1.BINARY, op, left, right); } } else if (ast.type === "ConditionalExpression") { var test = createRuntimeAst(expression, ast.test); left = createRuntimeAst(expression, ast.consequent); right = createRuntimeAst(expression, ast.alternate); node = new Node$1(ExpressionNodeType$1.CONDITIONAL, "?", left, right, test); } else if (ast.type === "MemberExpression") { node = parseMemberExpression(expression, ast); } else if (ast.type === "ArrayExpression") { var val = []; for (var i = 0; i < ast.elements.length; i++) { val[i] = createRuntimeAst(expression, ast.elements[i]); } node = new Node$1(ExpressionNodeType$1.ARRAY, val); } else if (ast.type === "Compound") { // empty expression or multiple expressions throw new RuntimeError("Provide exactly one expression."); } else { throw new RuntimeError("Cannot parse expression."); } return node; } function setEvaluateFunction(node) { if (node._type === ExpressionNodeType$1.CONDITIONAL) { node.evaluate = node._evaluateConditional; } else if (node._type === ExpressionNodeType$1.FUNCTION_CALL) { if (node._value === "test") { node.evaluate = node._evaluateRegExpTest; } else if (node._value === "exec") { node.evaluate = node._evaluateRegExpExec; } else if (node._value === "toString") { node.evaluate = node._evaluateToString; } } else if (node._type === ExpressionNodeType$1.UNARY) { if (node._value === "!") { node.evaluate = node._evaluateNot; } else if (node._value === "-") { node.evaluate = node._evaluateNegative; } else if (node._value === "+") { node.evaluate = node._evaluatePositive; } else if (node._value === "isNaN") { node.evaluate = node._evaluateNaN; } else if (node._value === "isFinite") { node.evaluate = node._evaluateIsFinite; } else if (node._value === "isExactClass") { node.evaluate = node._evaluateIsExactClass; } else if (node._value === "isClass") { node.evaluate = node._evaluateIsClass; } else if (node._value === "getExactClassName") { node.evaluate = node._evaluateGetExactClassName; } else if (node._value === "Boolean") { node.evaluate = node._evaluateBooleanConversion; } else if (node._value === "Number") { node.evaluate = node._evaluateNumberConversion; } else if (node._value === "String") { node.evaluate = node._evaluateStringConversion; } else if (defined(unaryFunctions[node._value])) { node.evaluate = getEvaluateUnaryFunction(node._value); } } else if (node._type === ExpressionNodeType$1.BINARY) { if (node._value === "+") { node.evaluate = node._evaluatePlus; } else if (node._value === "-") { node.evaluate = node._evaluateMinus; } else if (node._value === "*") { node.evaluate = node._evaluateTimes; } else if (node._value === "/") { node.evaluate = node._evaluateDivide; } else if (node._value === "%") { node.evaluate = node._evaluateMod; } else if (node._value === "===") { node.evaluate = node._evaluateEqualsStrict; } else if (node._value === "!==") { node.evaluate = node._evaluateNotEqualsStrict; } else if (node._value === "<") { node.evaluate = node._evaluateLessThan; } else if (node._value === "<=") { node.evaluate = node._evaluateLessThanOrEquals; } else if (node._value === ">") { node.evaluate = node._evaluateGreaterThan; } else if (node._value === ">=") { node.evaluate = node._evaluateGreaterThanOrEquals; } else if (node._value === "&&") { node.evaluate = node._evaluateAnd; } else if (node._value === "||") { node.evaluate = node._evaluateOr; } else if (node._value === "=~") { node.evaluate = node._evaluateRegExpMatch; } else if (node._value === "!~") { node.evaluate = node._evaluateRegExpNotMatch; } else if (defined(binaryFunctions[node._value])) { node.evaluate = getEvaluateBinaryFunction(node._value); } } else if (node._type === ExpressionNodeType$1.TERNARY) { node.evaluate = getEvaluateTernaryFunction(node._value); } else if (node._type === ExpressionNodeType$1.MEMBER) { if (node._value === "brackets") { node.evaluate = node._evaluateMemberBrackets; } else { node.evaluate = node._evaluateMemberDot; } } else if (node._type === ExpressionNodeType$1.ARRAY) { node.evaluate = node._evaluateArray; } else if (node._type === ExpressionNodeType$1.VARIABLE) { node.evaluate = node._evaluateVariable; } else if (node._type === ExpressionNodeType$1.VARIABLE_IN_STRING) { node.evaluate = node._evaluateVariableString; } else if (node._type === ExpressionNodeType$1.LITERAL_COLOR) { node.evaluate = node._evaluateLiteralColor; } else if (node._type === ExpressionNodeType$1.LITERAL_VECTOR) { node.evaluate = node._evaluateLiteralVector; } else if (node._type === ExpressionNodeType$1.LITERAL_STRING) { node.evaluate = node._evaluateLiteralString; } else if (node._type === ExpressionNodeType$1.REGEX) { node.evaluate = node._evaluateRegExp; } else if (node._type === ExpressionNodeType$1.BUILTIN_VARIABLE) { if (node._value === "tiles3d_tileset_time") { node.evaluate = evaluateTilesetTime; } } else { node.evaluate = node._evaluateLiteral; } } function evaluateTilesetTime(feature) { if (!defined(feature)) { return 0.0; } return feature.content.tileset.timeSinceLoad; } function getEvaluateUnaryFunction(call) { var evaluate = unaryFunctions[call]; return function (feature) { var left = this._left.evaluate(feature); return evaluate(call, left); }; } function getEvaluateBinaryFunction(call) { var evaluate = binaryFunctions[call]; return function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); return evaluate(call, left, right); }; } function getEvaluateTernaryFunction(call) { var evaluate = ternaryFunctions[call]; return function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); var test = this._test.evaluate(feature); return evaluate(call, left, right, test); }; } function getFeatureProperty(feature, name) { // Returns undefined if the feature is not defined or the property name is not defined for that feature if (defined(feature)) { return feature.getProperty(name); } } Node$1.prototype._evaluateLiteral = function () { return this._value; }; Node$1.prototype._evaluateLiteralColor = function (feature) { var color = scratchColor$k; var args = this._left; if (this._value === "color") { if (!defined(args)) { Color.fromBytes(255, 255, 255, 255, color); } else if (args.length > 1) { Color.fromCssColorString(args[0].evaluate(feature), color); color.alpha = args[1].evaluate(feature); } else { Color.fromCssColorString(args[0].evaluate(feature), color); } } else if (this._value === "rgb") { Color.fromBytes( args[0].evaluate(feature), args[1].evaluate(feature), args[2].evaluate(feature), 255, color ); } else if (this._value === "rgba") { // convert between css alpha (0 to 1) and cesium alpha (0 to 255) var a = args[3].evaluate(feature) * 255; Color.fromBytes( args[0].evaluate(feature), args[1].evaluate(feature), args[2].evaluate(feature), a, color ); } else if (this._value === "hsl") { Color.fromHsl( args[0].evaluate(feature), args[1].evaluate(feature), args[2].evaluate(feature), 1.0, color ); } else if (this._value === "hsla") { Color.fromHsl( args[0].evaluate(feature), args[1].evaluate(feature), args[2].evaluate(feature), args[3].evaluate(feature), color ); } return Cartesian4.fromColor(color, scratchStorage.getCartesian4()); }; Node$1.prototype._evaluateLiteralVector = function (feature) { // Gather the components that make up the vector, which includes components from interior vectors. // For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid. // // If the number of components does not equal the vector's size, then a RuntimeError is thrown - with two exceptions: // 1. A vector may be constructed from a larger vector and drop the extra components. // 2. A vector may be constructed from a single component - vec3(1) will become vec3(1, 1, 1). // // Examples of invalid constructors include: // vec4(1, 2) // not enough components // vec3(vec2(1, 2)) // not enough components // vec3(1, 2, 3, 4) // too many components // vec2(vec4(1), 1) // too many components var components = scratchStorage.getArray(); var call = this._value; var args = this._left; var argsLength = args.length; for (var i = 0; i < argsLength; ++i) { var value = args[i].evaluate(feature); if (typeof value === "number") { components.push(value); } else if (value instanceof Cartesian2) { components.push(value.x, value.y); } else if (value instanceof Cartesian3) { components.push(value.x, value.y, value.z); } else if (value instanceof Cartesian4) { components.push(value.x, value.y, value.z, value.w); } else { throw new RuntimeError( call + " argument must be a vector or number. Argument is " + value + "." ); } } var componentsLength = components.length; var vectorLength = parseInt(call.charAt(3)); if (componentsLength === 0) { throw new RuntimeError( "Invalid " + call + " constructor. No valid arguments." ); } else if (componentsLength < vectorLength && componentsLength > 1) { throw new RuntimeError( "Invalid " + call + " constructor. Not enough arguments." ); } else if (componentsLength > vectorLength && argsLength > 1) { throw new RuntimeError( "Invalid " + call + " constructor. Too many arguments." ); } if (componentsLength === 1) { // Add the same component 3 more times var component = components[0]; components.push(component, component, component); } if (call === "vec2") { return Cartesian2.fromArray(components, 0, scratchStorage.getCartesian2()); } else if (call === "vec3") { return Cartesian3.fromArray(components, 0, scratchStorage.getCartesian3()); } else if (call === "vec4") { return Cartesian4.fromArray(components, 0, scratchStorage.getCartesian4()); } }; Node$1.prototype._evaluateLiteralString = function () { return this._value; }; Node$1.prototype._evaluateVariableString = function (feature) { var result = this._value; var match = variableRegex.exec(result); while (match !== null) { var placeholder = match[0]; var variableName = match[1]; var property = getFeatureProperty(feature, variableName); if (!defined(property)) { property = ""; } result = result.replace(placeholder, property); match = variableRegex.exec(result); } return result; }; Node$1.prototype._evaluateVariable = function (feature) { // evaluates to undefined if the property name is not defined for that feature return getFeatureProperty(feature, this._value); }; function checkFeature(ast) { return ast._value === "feature"; } // PERFORMANCE_IDEA: Determine if parent property needs to be computed before runtime Node$1.prototype._evaluateMemberDot = function (feature) { if (checkFeature(this._left)) { return getFeatureProperty(feature, this._right.evaluate(feature)); } var property = this._left.evaluate(feature); if (!defined(property)) { return undefined; } var member = this._right.evaluate(feature); if ( property instanceof Cartesian2 || property instanceof Cartesian3 || property instanceof Cartesian4 ) { // Vector components may be accessed with .r, .g, .b, .a and implicitly with .x, .y, .z, .w if (member === "r") { return property.x; } else if (member === "g") { return property.y; } else if (member === "b") { return property.z; } else if (member === "a") { return property.w; } } return property[member]; }; Node$1.prototype._evaluateMemberBrackets = function (feature) { if (checkFeature(this._left)) { return getFeatureProperty(feature, this._right.evaluate(feature)); } var property = this._left.evaluate(feature); if (!defined(property)) { return undefined; } var member = this._right.evaluate(feature); if ( property instanceof Cartesian2 || property instanceof Cartesian3 || property instanceof Cartesian4 ) { // Vector components may be accessed with [0][1][2][3], ['r']['g']['b']['a'] and implicitly with ['x']['y']['z']['w'] // For Cartesian2 and Cartesian3 out-of-range components will just return undefined if (member === 0 || member === "r") { return property.x; } else if (member === 1 || member === "g") { return property.y; } else if (member === 2 || member === "b") { return property.z; } else if (member === 3 || member === "a") { return property.w; } } return property[member]; }; Node$1.prototype._evaluateArray = function (feature) { var array = []; for (var i = 0; i < this._value.length; i++) { array[i] = this._value[i].evaluate(feature); } return array; }; // PERFORMANCE_IDEA: Have "fast path" functions that deal only with specific types // that we can assign if we know the types before runtime Node$1.prototype._evaluateNot = function (feature) { var left = this._left.evaluate(feature); if (typeof left !== "boolean") { throw new RuntimeError( 'Operator "!" requires a boolean argument. Argument is ' + left + "." ); } return !left; }; Node$1.prototype._evaluateNegative = function (feature) { var left = this._left.evaluate(feature); if (left instanceof Cartesian2) { return Cartesian2.negate(left, scratchStorage.getCartesian2()); } else if (left instanceof Cartesian3) { return Cartesian3.negate(left, scratchStorage.getCartesian3()); } else if (left instanceof Cartesian4) { return Cartesian4.negate(left, scratchStorage.getCartesian4()); } else if (typeof left === "number") { return -left; } throw new RuntimeError( 'Operator "-" requires a vector or number argument. Argument is ' + left + "." ); }; Node$1.prototype._evaluatePositive = function (feature) { var left = this._left.evaluate(feature); if ( !( left instanceof Cartesian2 || left instanceof Cartesian3 || left instanceof Cartesian4 || typeof left === "number" ) ) { throw new RuntimeError( 'Operator "+" requires a vector or number argument. Argument is ' + left + "." ); } return left; }; Node$1.prototype._evaluateLessThan = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (typeof left !== "number" || typeof right !== "number") { throw new RuntimeError( 'Operator "<" requires number arguments. Arguments are ' + left + " and " + right + "." ); } return left < right; }; Node$1.prototype._evaluateLessThanOrEquals = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (typeof left !== "number" || typeof right !== "number") { throw new RuntimeError( 'Operator "<=" requires number arguments. Arguments are ' + left + " and " + right + "." ); } return left <= right; }; Node$1.prototype._evaluateGreaterThan = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (typeof left !== "number" || typeof right !== "number") { throw new RuntimeError( 'Operator ">" requires number arguments. Arguments are ' + left + " and " + right + "." ); } return left > right; }; Node$1.prototype._evaluateGreaterThanOrEquals = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (typeof left !== "number" || typeof right !== "number") { throw new RuntimeError( 'Operator ">=" requires number arguments. Arguments are ' + left + " and " + right + "." ); } return left >= right; }; Node$1.prototype._evaluateOr = function (feature) { var left = this._left.evaluate(feature); if (typeof left !== "boolean") { throw new RuntimeError( 'Operator "||" requires boolean arguments. First argument is ' + left + "." ); } // short circuit the expression if (left) { return true; } var right = this._right.evaluate(feature); if (typeof right !== "boolean") { throw new RuntimeError( 'Operator "||" requires boolean arguments. Second argument is ' + right + "." ); } return left || right; }; Node$1.prototype._evaluateAnd = function (feature) { var left = this._left.evaluate(feature); if (typeof left !== "boolean") { throw new RuntimeError( 'Operator "&&" requires boolean arguments. First argument is ' + left + "." ); } // short circuit the expression if (!left) { return false; } var right = this._right.evaluate(feature); if (typeof right !== "boolean") { throw new RuntimeError( 'Operator "&&" requires boolean arguments. Second argument is ' + right + "." ); } return left && right; }; Node$1.prototype._evaluatePlus = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (right instanceof Cartesian2 && left instanceof Cartesian2) { return Cartesian2.add(left, right, scratchStorage.getCartesian2()); } else if (right instanceof Cartesian3 && left instanceof Cartesian3) { return Cartesian3.add(left, right, scratchStorage.getCartesian3()); } else if (right instanceof Cartesian4 && left instanceof Cartesian4) { return Cartesian4.add(left, right, scratchStorage.getCartesian4()); } else if (typeof left === "string" || typeof right === "string") { // If only one argument is a string the other argument calls its toString function. return left + right; } else if (typeof left === "number" && typeof right === "number") { return left + right; } throw new RuntimeError( 'Operator "+" requires vector or number arguments of matching types, or at least one string argument. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateMinus = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (right instanceof Cartesian2 && left instanceof Cartesian2) { return Cartesian2.subtract(left, right, scratchStorage.getCartesian2()); } else if (right instanceof Cartesian3 && left instanceof Cartesian3) { return Cartesian3.subtract(left, right, scratchStorage.getCartesian3()); } else if (right instanceof Cartesian4 && left instanceof Cartesian4) { return Cartesian4.subtract(left, right, scratchStorage.getCartesian4()); } else if (typeof left === "number" && typeof right === "number") { return left - right; } throw new RuntimeError( 'Operator "-" requires vector or number arguments of matching types. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateTimes = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (right instanceof Cartesian2 && left instanceof Cartesian2) { return Cartesian2.multiplyComponents( left, right, scratchStorage.getCartesian2() ); } else if (right instanceof Cartesian2 && typeof left === "number") { return Cartesian2.multiplyByScalar( right, left, scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian2 && typeof right === "number") { return Cartesian2.multiplyByScalar( left, right, scratchStorage.getCartesian2() ); } else if (right instanceof Cartesian3 && left instanceof Cartesian3) { return Cartesian3.multiplyComponents( left, right, scratchStorage.getCartesian3() ); } else if (right instanceof Cartesian3 && typeof left === "number") { return Cartesian3.multiplyByScalar( right, left, scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian3 && typeof right === "number") { return Cartesian3.multiplyByScalar( left, right, scratchStorage.getCartesian3() ); } else if (right instanceof Cartesian4 && left instanceof Cartesian4) { return Cartesian4.multiplyComponents( left, right, scratchStorage.getCartesian4() ); } else if (right instanceof Cartesian4 && typeof left === "number") { return Cartesian4.multiplyByScalar( right, left, scratchStorage.getCartesian4() ); } else if (left instanceof Cartesian4 && typeof right === "number") { return Cartesian4.multiplyByScalar( left, right, scratchStorage.getCartesian4() ); } else if (typeof left === "number" && typeof right === "number") { return left * right; } throw new RuntimeError( 'Operator "*" requires vector or number arguments. If both arguments are vectors they must be matching types. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateDivide = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (right instanceof Cartesian2 && left instanceof Cartesian2) { return Cartesian2.divideComponents( left, right, scratchStorage.getCartesian2() ); } else if (left instanceof Cartesian2 && typeof right === "number") { return Cartesian2.divideByScalar( left, right, scratchStorage.getCartesian2() ); } else if (right instanceof Cartesian3 && left instanceof Cartesian3) { return Cartesian3.divideComponents( left, right, scratchStorage.getCartesian3() ); } else if (left instanceof Cartesian3 && typeof right === "number") { return Cartesian3.divideByScalar( left, right, scratchStorage.getCartesian3() ); } else if (right instanceof Cartesian4 && left instanceof Cartesian4) { return Cartesian4.divideComponents( left, right, scratchStorage.getCartesian4() ); } else if (left instanceof Cartesian4 && typeof right === "number") { return Cartesian4.divideByScalar( left, right, scratchStorage.getCartesian4() ); } else if (typeof left === "number" && typeof right === "number") { return left / right; } throw new RuntimeError( 'Operator "/" requires vector or number arguments of matching types, or a number as the second argument. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateMod = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (right instanceof Cartesian2 && left instanceof Cartesian2) { return Cartesian2.fromElements( left.x % right.x, left.y % right.y, scratchStorage.getCartesian2() ); } else if (right instanceof Cartesian3 && left instanceof Cartesian3) { return Cartesian3.fromElements( left.x % right.x, left.y % right.y, left.z % right.z, scratchStorage.getCartesian3() ); } else if (right instanceof Cartesian4 && left instanceof Cartesian4) { return Cartesian4.fromElements( left.x % right.x, left.y % right.y, left.z % right.z, left.w % right.w, scratchStorage.getCartesian4() ); } else if (typeof left === "number" && typeof right === "number") { return left % right; } throw new RuntimeError( 'Operator "%" requires vector or number arguments of matching types. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateEqualsStrict = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if ( (right instanceof Cartesian2 && left instanceof Cartesian2) || (right instanceof Cartesian3 && left instanceof Cartesian3) || (right instanceof Cartesian4 && left instanceof Cartesian4) ) { return left.equals(right); } return left === right; }; Node$1.prototype._evaluateNotEqualsStrict = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if ( (right instanceof Cartesian2 && left instanceof Cartesian2) || (right instanceof Cartesian3 && left instanceof Cartesian3) || (right instanceof Cartesian4 && left instanceof Cartesian4) ) { return !left.equals(right); } return left !== right; }; Node$1.prototype._evaluateConditional = function (feature) { var test = this._test.evaluate(feature); if (typeof test !== "boolean") { throw new RuntimeError( "Conditional argument of conditional expression must be a boolean. Argument is " + test + "." ); } if (test) { return this._left.evaluate(feature); } return this._right.evaluate(feature); }; Node$1.prototype._evaluateNaN = function (feature) { return isNaN(this._left.evaluate(feature)); }; Node$1.prototype._evaluateIsFinite = function (feature) { return isFinite(this._left.evaluate(feature)); }; Node$1.prototype._evaluateIsExactClass = function (feature) { if (defined(feature)) { return feature.isExactClass(this._left.evaluate(feature)); } return false; }; Node$1.prototype._evaluateIsClass = function (feature) { if (defined(feature)) { return feature.isClass(this._left.evaluate(feature)); } return false; }; Node$1.prototype._evaluateGetExactClassName = function (feature) { if (defined(feature)) { return feature.getExactClassName(); } }; Node$1.prototype._evaluateBooleanConversion = function (feature) { return Boolean(this._left.evaluate(feature)); }; Node$1.prototype._evaluateNumberConversion = function (feature) { return Number(this._left.evaluate(feature)); }; Node$1.prototype._evaluateStringConversion = function (feature) { return String(this._left.evaluate(feature)); }; Node$1.prototype._evaluateRegExp = function (feature) { var pattern = this._value.evaluate(feature); var flags = ""; if (defined(this._left)) { flags = this._left.evaluate(feature); } var exp; try { exp = new RegExp(pattern, flags); } catch (e) { throw new RuntimeError(e); } return exp; }; Node$1.prototype._evaluateRegExpTest = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (!(left instanceof RegExp && typeof right === "string")) { throw new RuntimeError( "RegExp.test requires the first argument to be a RegExp and the second argument to be a string. Arguments are " + left + " and " + right + "." ); } return left.test(right); }; Node$1.prototype._evaluateRegExpMatch = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (left instanceof RegExp && typeof right === "string") { return left.test(right); } else if (right instanceof RegExp && typeof left === "string") { return right.test(left); } throw new RuntimeError( 'Operator "=~" requires one RegExp argument and one string argument. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateRegExpNotMatch = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (left instanceof RegExp && typeof right === "string") { return !left.test(right); } else if (right instanceof RegExp && typeof left === "string") { return !right.test(left); } throw new RuntimeError( 'Operator "!~" requires one RegExp argument and one string argument. Arguments are ' + left + " and " + right + "." ); }; Node$1.prototype._evaluateRegExpExec = function (feature) { var left = this._left.evaluate(feature); var right = this._right.evaluate(feature); if (!(left instanceof RegExp && typeof right === "string")) { throw new RuntimeError( "RegExp.exec requires the first argument to be a RegExp and the second argument to be a string. Arguments are " + left + " and " + right + "." ); } var exec = left.exec(right); if (!defined(exec)) { return null; } return exec[1]; }; Node$1.prototype._evaluateToString = function (feature) { var left = this._left.evaluate(feature); if ( left instanceof RegExp || left instanceof Cartesian2 || left instanceof Cartesian3 || left instanceof Cartesian4 ) { return String(left); } throw new RuntimeError('Unexpected function call "' + this._value + '".'); }; function convertHSLToRGB(ast) { // Check if the color contains any nested expressions to see if the color can be converted here. // E.g. "hsl(0.9, 0.6, 0.7)" is able to convert directly to rgb, "hsl(0.9, 0.6, ${Height})" is not. var channels = ast._left; var length = channels.length; for (var i = 0; i < length; ++i) { if (channels[i]._type !== ExpressionNodeType$1.LITERAL_NUMBER) { return undefined; } } var h = channels[0]._value; var s = channels[1]._value; var l = channels[2]._value; var a = length === 4 ? channels[3]._value : 1.0; return Color.fromHsl(h, s, l, a, scratchColor$k); } function convertRGBToColor(ast) { // Check if the color contains any nested expressions to see if the color can be converted here. // E.g. "rgb(255, 255, 255)" is able to convert directly to Color, "rgb(255, 255, ${Height})" is not. var channels = ast._left; var length = channels.length; for (var i = 0; i < length; ++i) { if (channels[i]._type !== ExpressionNodeType$1.LITERAL_NUMBER) { return undefined; } } var color = scratchColor$k; color.red = channels[0]._value / 255.0; color.green = channels[1]._value / 255.0; color.blue = channels[2]._value / 255.0; color.alpha = length === 4 ? channels[3]._value : 1.0; return color; } function numberToString(number) { if (number % 1 === 0) { // Add a .0 to whole numbers return number.toFixed(1); } return number.toString(); } function colorToVec3(color) { var r = numberToString(color.red); var g = numberToString(color.green); var b = numberToString(color.blue); return "vec3(" + r + ", " + g + ", " + b + ")"; } function colorToVec4(color) { var r = numberToString(color.red); var g = numberToString(color.green); var b = numberToString(color.blue); var a = numberToString(color.alpha); return "vec4(" + r + ", " + g + ", " + b + ", " + a + ")"; } function getExpressionArray(array, propertyNameMap, shaderState, parent) { var length = array.length; var expressions = new Array(length); for (var i = 0; i < length; ++i) { expressions[i] = array[i].getShaderExpression( propertyNameMap, shaderState, parent ); } return expressions; } function getVariableName(variableName, propertyNameMap) { if (!defined(propertyNameMap[variableName])) { throw new RuntimeError( 'Style references a property "' + variableName + '" that does not exist or is not styleable.' ); } return propertyNameMap[variableName]; } var nullSentinel = "czm_infinity"; // null just needs to be some sentinel value that will cause "[expression] === null" to be false in nearly all cases. GLSL doesn't have a NaN constant so use czm_infinity. Node$1.prototype.getShaderExpression = function ( propertyNameMap, shaderState, parent ) { var color; var left; var right; var test; var type = this._type; var value = this._value; if (defined(this._left)) { if (Array.isArray(this._left)) { // Left can be an array if the type is LITERAL_COLOR or LITERAL_VECTOR left = getExpressionArray(this._left, propertyNameMap, shaderState, this); } else { left = this._left.getShaderExpression(propertyNameMap, shaderState, this); } } if (defined(this._right)) { right = this._right.getShaderExpression(propertyNameMap, shaderState, this); } if (defined(this._test)) { test = this._test.getShaderExpression(propertyNameMap, shaderState, this); } if (Array.isArray(this._value)) { // For ARRAY type value = getExpressionArray(this._value, propertyNameMap, shaderState, this); } switch (type) { case ExpressionNodeType$1.VARIABLE: if (checkFeature(this)) { return undefined; } return getVariableName(value, propertyNameMap); case ExpressionNodeType$1.UNARY: // Supported types: +, -, !, Boolean, Number if (value === "Boolean") { return "bool(" + left + ")"; } else if (value === "Number") { return "float(" + left + ")"; } else if (value === "round") { return "floor(" + left + " + 0.5)"; } else if (defined(unaryFunctions[value])) { return value + "(" + left + ")"; } else if (value === "isNaN") { // In GLSL 2.0 use isnan instead return "(" + left + " != " + left + ")"; } else if (value === "isFinite") { // In GLSL 2.0 use isinf instead. GLSL doesn't have an infinity constant so use czm_infinity which is an arbitrarily big enough number. return "(abs(" + left + ") < czm_infinity)"; } else if ( value === "String" || value === "isExactClass" || value === "isClass" || value === "getExactClassName" ) { throw new RuntimeError( 'Error generating style shader: "' + value + '" is not supported.' ); } return value + left; case ExpressionNodeType$1.BINARY: // Supported types: ||, &&, ===, !==, <, >, <=, >=, +, -, *, /, % if (value === "%") { return "mod(" + left + ", " + right + ")"; } else if (value === "===") { return "(" + left + " == " + right + ")"; } else if (value === "!==") { return "(" + left + " != " + right + ")"; } else if (value === "atan2") { return "atan(" + left + ", " + right + ")"; } else if (defined(binaryFunctions[value])) { return value + "(" + left + ", " + right + ")"; } return "(" + left + " " + value + " " + right + ")"; case ExpressionNodeType$1.TERNARY: if (defined(ternaryFunctions[value])) { return value + "(" + left + ", " + right + ", " + test + ")"; } break; case ExpressionNodeType$1.CONDITIONAL: return "(" + test + " ? " + left + " : " + right + ")"; case ExpressionNodeType$1.MEMBER: if (checkFeature(this._left)) { return getVariableName(right, propertyNameMap); } // This is intended for accessing the components of vector properties. String members aren't supported. // Check for 0.0 rather than 0 because all numbers are previously converted to decimals. if (right === "r" || right === "x" || right === "0.0") { return left + "[0]"; } else if (right === "g" || right === "y" || right === "1.0") { return left + "[1]"; } else if (right === "b" || right === "z" || right === "2.0") { return left + "[2]"; } else if (right === "a" || right === "w" || right === "3.0") { return left + "[3]"; } return left + "[int(" + right + ")]"; case ExpressionNodeType$1.FUNCTION_CALL: throw new RuntimeError( 'Error generating style shader: "' + value + '" is not supported.' ); case ExpressionNodeType$1.ARRAY: if (value.length === 4) { return ( "vec4(" + value[0] + ", " + value[1] + ", " + value[2] + ", " + value[3] + ")" ); } else if (value.length === 3) { return "vec3(" + value[0] + ", " + value[1] + ", " + value[2] + ")"; } else if (value.length === 2) { return "vec2(" + value[0] + ", " + value[1] + ")"; } throw new RuntimeError( "Error generating style shader: Invalid array length. Array length should be 2, 3, or 4." ); case ExpressionNodeType$1.REGEX: throw new RuntimeError( "Error generating style shader: Regular expressions are not supported." ); case ExpressionNodeType$1.VARIABLE_IN_STRING: throw new RuntimeError( "Error generating style shader: Converting a variable to a string is not supported." ); case ExpressionNodeType$1.LITERAL_NULL: return nullSentinel; case ExpressionNodeType$1.LITERAL_BOOLEAN: return value ? "true" : "false"; case ExpressionNodeType$1.LITERAL_NUMBER: return numberToString(value); case ExpressionNodeType$1.LITERAL_STRING: if (defined(parent) && parent._type === ExpressionNodeType$1.MEMBER) { if ( value === "r" || value === "g" || value === "b" || value === "a" || value === "x" || value === "y" || value === "z" || value === "w" || checkFeature(parent._left) ) { return value; } } // Check for css color strings color = Color.fromCssColorString(value, scratchColor$k); if (defined(color)) { return colorToVec3(color); } throw new RuntimeError( "Error generating style shader: String literals are not supported." ); case ExpressionNodeType$1.LITERAL_COLOR: var args = left; if (value === "color") { if (!defined(args)) { return "vec4(1.0)"; } else if (args.length > 1) { var rgb = args[0]; var alpha = args[1]; if (alpha !== "1.0") { shaderState.translucent = true; } return "vec4(" + rgb + ", " + alpha + ")"; } return "vec4(" + args[0] + ", 1.0)"; } else if (value === "rgb") { color = convertRGBToColor(this); if (defined(color)) { return colorToVec4(color); } return ( "vec4(" + args[0] + " / 255.0, " + args[1] + " / 255.0, " + args[2] + " / 255.0, 1.0)" ); } else if (value === "rgba") { if (args[3] !== "1.0") { shaderState.translucent = true; } color = convertRGBToColor(this); if (defined(color)) { return colorToVec4(color); } return ( "vec4(" + args[0] + " / 255.0, " + args[1] + " / 255.0, " + args[2] + " / 255.0, " + args[3] + ")" ); } else if (value === "hsl") { color = convertHSLToRGB(this); if (defined(color)) { return colorToVec4(color); } return ( "vec4(czm_HSLToRGB(vec3(" + args[0] + ", " + args[1] + ", " + args[2] + ")), 1.0)" ); } else if (value === "hsla") { color = convertHSLToRGB(this); if (defined(color)) { if (color.alpha !== 1.0) { shaderState.translucent = true; } return colorToVec4(color); } if (args[3] !== "1.0") { shaderState.translucent = true; } return ( "vec4(czm_HSLToRGB(vec3(" + args[0] + ", " + args[1] + ", " + args[2] + ")), " + args[3] + ")" ); } break; case ExpressionNodeType$1.LITERAL_VECTOR: //>>includeStart('debug', pragmas.debug); if (!defined(left)) { throw new DeveloperError( "left should always be defined for type ExpressionNodeType.LITERAL_VECTOR" ); } //>>includeEnd('debug'); var length = left.length; var vectorExpression = value + "("; for (var i = 0; i < length; ++i) { vectorExpression += left[i]; if (i < length - 1) { vectorExpression += ", "; } } vectorExpression += ")"; return vectorExpression; case ExpressionNodeType$1.LITERAL_REGEX: throw new RuntimeError( "Error generating style shader: Regular expressions are not supported." ); case ExpressionNodeType$1.LITERAL_UNDEFINED: return nullSentinel; case ExpressionNodeType$1.BUILTIN_VARIABLE: if (value === "tiles3d_tileset_time") { return "u_time"; } } }; /** * Creates a batch of classification meshes. * * @alias Vector3DTilePrimitive * @constructor * * @param {Object} options An object with following properties: * @param {Float32Array} options.positions The positions of the meshes. * @param {Uint16Array|Uint32Array} options.indices The indices of the triangulated meshes. The indices must be contiguous so that * the indices for mesh n are in [i, i + indexCounts[n]] where i = sum{indexCounts[0], indexCounts[n - 1]}. * @param {Uint32Array} options.indexCounts The number of indices for each mesh. * @param {Uint32Array} options.indexOffsets The offset into the index buffer for each mesh. * @param {Vector3DTileBatch[]} options.batchedIndices The index offset and count for each batch with the same color. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched meshes. * @param {Uint16Array} options.batchIds The batch ids for each mesh. * @param {Uint16Array} options.vertexBatchIds The batch id for each vertex. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of meshes. * @param {BoundingSphere[]} options.boundingVolumes The bounding volume for each mesh. * @param {ClassificationType} [options.classificationType] What this tile will classify. * * @private */ function Vector3DTilePrimitive(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._batchTable = options.batchTable; this._batchIds = options.batchIds; // These arrays are released after VAO creation. this._positions = options.positions; this._vertexBatchIds = options.vertexBatchIds; // These arrays are kept for re-batching indices based on colors. // If WebGL 2 is supported, indices will be released and re-batching uses buffer-to-buffer copies. this._indices = options.indices; this._indexCounts = options.indexCounts; this._indexOffsets = options.indexOffsets; this._batchedIndices = options.batchedIndices; this._boundingVolume = options.boundingVolume; this._boundingVolumes = options.boundingVolumes; this._center = defaultValue(options.center, Cartesian3.ZERO); this._va = undefined; this._sp = undefined; this._spStencil = undefined; this._spPick = undefined; this._uniformMap = undefined; // Only used with WebGL 2 to ping-pong ibos after copy. this._vaSwap = undefined; this._rsStencilDepthPass = undefined; this._rsStencilDepthPass3DTiles = undefined; this._rsColorPass = undefined; this._rsPickPass = undefined; this._rsWireframe = undefined; this._commands = []; this._commandsIgnoreShow = []; this._pickCommands = []; this._constantColor = Color.clone(Color.WHITE); this._highlightColor = this._constantColor; this._batchDirty = true; this._pickCommandsDirty = true; this._framesSinceLastRebatch = 0; this._updatingAllCommands = false; this._trianglesLength = this._indices.length / 3; this._geometryByteLength = this._indices.byteLength + this._positions.byteLength + this._vertexBatchIds.byteLength; /** * Draw the wireframe of the classification meshes. * @type {Boolean} * @default false */ this.debugWireframe = false; this._debugWireframe = this.debugWireframe; this._wireframeDirty = false; /** * Forces a re-batch instead of waiting after a number of frames have been rendered. For testing only. * @type {Boolean} * @default false */ this.forceRebatch = false; /** * What this tile will classify. * @type {ClassificationType} * @default ClassificationType.BOTH */ this.classificationType = defaultValue( options.classificationType, ClassificationType$1.BOTH ); // Hidden options this._vertexShaderSource = options._vertexShaderSource; this._fragmentShaderSource = options._fragmentShaderSource; this._attributeLocations = options._attributeLocations; this._uniformMap = options._uniformMap; this._pickId = options._pickId; this._modelMatrix = options._modelMatrix; this._boundingSphere = options._boundingSphere; this._batchIdLookUp = {}; var length = this._batchIds.length; for (var i = 0; i < length; ++i) { var batchId = this._batchIds[i]; this._batchIdLookUp[batchId] = i; } } Object.defineProperties(Vector3DTilePrimitive.prototype, { /** * Gets the number of triangles. * * @memberof Vector3DTilePrimitive.prototype * * @type {Number} * @readonly */ trianglesLength: { get: function () { return this._trianglesLength; }, }, /** * Gets the geometry memory in bytes. * * @memberof Vector3DTilePrimitive.prototype * * @type {Number} * @readonly */ geometryByteLength: { get: function () { return this._geometryByteLength; }, }, }); var defaultAttributeLocations = { position: 0, a_batchId: 1, }; function createVertexArray$3(primitive, context) { if (defined(primitive._va)) { return; } var positionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: primitive._positions, usage: BufferUsage$1.STATIC_DRAW, }); var idBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: primitive._vertexBatchIds, usage: BufferUsage$1.STATIC_DRAW, }); var indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: primitive._indices, usage: BufferUsage$1.DYNAMIC_DRAW, indexDatatype: primitive._indices.BYTES_PER_ELEMENT === 2 ? IndexDatatype$1.UNSIGNED_SHORT : IndexDatatype$1.UNSIGNED_INT, }); var vertexAttributes = [ { index: 0, vertexBuffer: positionBuffer, componentDatatype: ComponentDatatype$1.fromTypedArray(primitive._positions), componentsPerAttribute: 3, }, { index: 1, vertexBuffer: idBuffer, componentDatatype: ComponentDatatype$1.fromTypedArray( primitive._vertexBatchIds ), componentsPerAttribute: 1, }, ]; primitive._va = new VertexArray({ context: context, attributes: vertexAttributes, indexBuffer: indexBuffer, }); if (context.webgl2) { primitive._vaSwap = new VertexArray({ context: context, attributes: vertexAttributes, indexBuffer: Buffer$1.createIndexBuffer({ context: context, sizeInBytes: indexBuffer.sizeInBytes, usage: BufferUsage$1.DYNAMIC_DRAW, indexDatatype: indexBuffer.indexDatatype, }), }); } primitive._batchedPositions = undefined; primitive._transferrableBatchIds = undefined; primitive._vertexBatchIds = undefined; primitive._verticesPromise = undefined; } function createShaders$2(primitive, context) { if (defined(primitive._sp)) { return; } var batchTable = primitive._batchTable; var attributeLocations = defaultValue( primitive._attributeLocations, defaultAttributeLocations ); var pickId = primitive._pickId; var vertexShaderSource = primitive._vertexShaderSource; var fragmentShaderSource = primitive._fragmentShaderSource; if (defined(vertexShaderSource)) { primitive._sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vertexShaderSource, fragmentShaderSource: fragmentShaderSource, attributeLocations: attributeLocations, }); primitive._spStencil = primitive._sp; fragmentShaderSource = ShaderSource.replaceMain( fragmentShaderSource, "czm_non_pick_main" ); fragmentShaderSource = fragmentShaderSource + "void main() \n" + "{ \n" + " czm_non_pick_main(); \n" + " gl_FragColor = " + pickId + "; \n" + "} \n"; primitive._spPick = ShaderProgram.fromCache({ context: context, vertexShaderSource: vertexShaderSource, fragmentShaderSource: fragmentShaderSource, attributeLocations: attributeLocations, }); return; } var vsSource = batchTable.getVertexShaderCallback( false, "a_batchId", undefined )(VectorTileVS); var fsSource = batchTable.getFragmentShaderCallback()( ShadowVolumeFS, false, undefined ); pickId = batchTable.getPickId(); var vs = new ShaderSource({ sources: [vsSource], }); var fs = new ShaderSource({ defines: ["VECTOR_TILE"], sources: [fsSource], }); primitive._sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); vs = new ShaderSource({ sources: [VectorTileVS], }); fs = new ShaderSource({ defines: ["VECTOR_TILE"], sources: [ShadowVolumeFS], }); primitive._spStencil = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); fsSource = ShaderSource.replaceMain(fsSource, "czm_non_pick_main"); fsSource = fsSource + "\n" + "void main() \n" + "{ \n" + " czm_non_pick_main(); \n" + " gl_FragColor = " + pickId + "; \n" + "} \n"; var pickVS = new ShaderSource({ sources: [vsSource], }); var pickFS = new ShaderSource({ defines: ["VECTOR_TILE"], sources: [fsSource], }); primitive._spPick = ShaderProgram.fromCache({ context: context, vertexShaderSource: pickVS, fragmentShaderSource: pickFS, attributeLocations: attributeLocations, }); } function getStencilDepthRenderState(mask3DTiles) { var stencilFunction = mask3DTiles ? StencilFunction$1.EQUAL : StencilFunction$1.ALWAYS; return { colorMask: { red: false, green: false, blue: false, alpha: false, }, stencilTest: { enabled: true, frontFunction: stencilFunction, frontOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.DECREMENT_WRAP, zPass: StencilOperation$1.KEEP, }, backFunction: stencilFunction, backOperation: { fail: StencilOperation$1.KEEP, zFail: StencilOperation$1.INCREMENT_WRAP, zPass: StencilOperation$1.KEEP, }, reference: StencilConstants$1.CESIUM_3D_TILE_MASK, mask: StencilConstants$1.CESIUM_3D_TILE_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: true, func: DepthFunction$1.LESS_OR_EQUAL, }, depthMask: false, }; } var colorRenderState = { stencilTest: { enabled: true, frontFunction: StencilFunction$1.NOT_EQUAL, frontOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, backFunction: StencilFunction$1.NOT_EQUAL, backOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, reference: 0, mask: StencilConstants$1.CLASSIFICATION_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: false, }, depthMask: false, blending: BlendingState$1.ALPHA_BLEND, }; var pickRenderState = { stencilTest: { enabled: true, frontFunction: StencilFunction$1.NOT_EQUAL, frontOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, backFunction: StencilFunction$1.NOT_EQUAL, backOperation: { fail: StencilOperation$1.ZERO, zFail: StencilOperation$1.ZERO, zPass: StencilOperation$1.ZERO, }, reference: 0, mask: StencilConstants$1.CLASSIFICATION_MASK, }, stencilMask: StencilConstants$1.CLASSIFICATION_MASK, depthTest: { enabled: false, }, depthMask: false, }; function createRenderStates$3(primitive) { if (defined(primitive._rsStencilDepthPass)) { return; } primitive._rsStencilDepthPass = RenderState.fromCache( getStencilDepthRenderState(false) ); primitive._rsStencilDepthPass3DTiles = RenderState.fromCache( getStencilDepthRenderState(true) ); primitive._rsColorPass = RenderState.fromCache(colorRenderState); primitive._rsPickPass = RenderState.fromCache(pickRenderState); } var modifiedModelViewScratch$2 = new Matrix4(); var rtcScratch$2 = new Cartesian3(); function createUniformMap$5(primitive, context) { if (defined(primitive._uniformMap)) { return; } var uniformMap = { u_modifiedModelViewProjection: function () { var viewMatrix = context.uniformState.view; var projectionMatrix = context.uniformState.projection; Matrix4.clone(viewMatrix, modifiedModelViewScratch$2); Matrix4.multiplyByPoint( modifiedModelViewScratch$2, primitive._center, rtcScratch$2 ); Matrix4.setTranslation( modifiedModelViewScratch$2, rtcScratch$2, modifiedModelViewScratch$2 ); Matrix4.multiply( projectionMatrix, modifiedModelViewScratch$2, modifiedModelViewScratch$2 ); return modifiedModelViewScratch$2; }, u_highlightColor: function () { return primitive._highlightColor; }, }; primitive._uniformMap = primitive._batchTable.getUniformMapCallback()( uniformMap ); } function copyIndicesCPU( indices, newIndices, currentOffset, offsets, counts, batchIds, batchIdLookUp ) { var sizeInBytes = indices.constructor.BYTES_PER_ELEMENT; var batchedIdsLength = batchIds.length; for (var j = 0; j < batchedIdsLength; ++j) { var batchedId = batchIds[j]; var index = batchIdLookUp[batchedId]; var offset = offsets[index]; var count = counts[index]; var subarray = new indices.constructor( indices.buffer, sizeInBytes * offset, count ); newIndices.set(subarray, currentOffset); offsets[index] = currentOffset; currentOffset += count; } return currentOffset; } function rebatchCPU(primitive, batchedIndices) { var indices = primitive._indices; var indexOffsets = primitive._indexOffsets; var indexCounts = primitive._indexCounts; var batchIdLookUp = primitive._batchIdLookUp; var newIndices = new indices.constructor(indices.length); var current = batchedIndices.pop(); var newBatchedIndices = [current]; var currentOffset = copyIndicesCPU( indices, newIndices, 0, indexOffsets, indexCounts, current.batchIds, batchIdLookUp ); current.offset = 0; current.count = currentOffset; while (batchedIndices.length > 0) { var next = batchedIndices.pop(); if (Color.equals(next.color, current.color)) { currentOffset = copyIndicesCPU( indices, newIndices, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp ); current.batchIds = current.batchIds.concat(next.batchIds); current.count = currentOffset - current.offset; } else { var offset = currentOffset; currentOffset = copyIndicesCPU( indices, newIndices, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp ); next.offset = offset; next.count = currentOffset - offset; newBatchedIndices.push(next); current = next; } } primitive._va.indexBuffer.copyFromArrayView(newIndices); primitive._indices = newIndices; primitive._batchedIndices = newBatchedIndices; } function copyIndicesGPU( readBuffer, writeBuffer, currentOffset, offsets, counts, batchIds, batchIdLookUp ) { var sizeInBytes = readBuffer.bytesPerIndex; var batchedIdsLength = batchIds.length; for (var j = 0; j < batchedIdsLength; ++j) { var batchedId = batchIds[j]; var index = batchIdLookUp[batchedId]; var offset = offsets[index]; var count = counts[index]; writeBuffer.copyFromBuffer( readBuffer, offset * sizeInBytes, currentOffset * sizeInBytes, count * sizeInBytes ); offsets[index] = currentOffset; currentOffset += count; } return currentOffset; } function rebatchGPU(primitive, batchedIndices) { var indexOffsets = primitive._indexOffsets; var indexCounts = primitive._indexCounts; var batchIdLookUp = primitive._batchIdLookUp; var current = batchedIndices.pop(); var newBatchedIndices = [current]; var readBuffer = primitive._va.indexBuffer; var writeBuffer = primitive._vaSwap.indexBuffer; var currentOffset = copyIndicesGPU( readBuffer, writeBuffer, 0, indexOffsets, indexCounts, current.batchIds, batchIdLookUp ); current.offset = 0; current.count = currentOffset; while (batchedIndices.length > 0) { var next = batchedIndices.pop(); if (Color.equals(next.color, current.color)) { currentOffset = copyIndicesGPU( readBuffer, writeBuffer, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp ); current.batchIds = current.batchIds.concat(next.batchIds); current.count = currentOffset - current.offset; } else { var offset = currentOffset; currentOffset = copyIndicesGPU( readBuffer, writeBuffer, currentOffset, indexOffsets, indexCounts, next.batchIds, batchIdLookUp ); next.offset = offset; next.count = currentOffset - offset; newBatchedIndices.push(next); current = next; } } var temp = primitive._va; primitive._va = primitive._vaSwap; primitive._vaSwap = temp; primitive._batchedIndices = newBatchedIndices; } function compareColors(a, b) { return b.color.toRgba() - a.color.toRgba(); } // PERFORMANCE_IDEA: For WebGL 2, we can use copyBufferSubData for buffer-to-buffer copies. // PERFORMANCE_IDEA: Not supported, but we could use glMultiDrawElements here. function rebatchCommands(primitive, context) { if (!primitive._batchDirty) { return false; } var batchedIndices = primitive._batchedIndices; var length = batchedIndices.length; var needToRebatch = false; var colorCounts = {}; for (var i = 0; i < length; ++i) { var color = batchedIndices[i].color; var rgba = color.toRgba(); if (defined(colorCounts[rgba])) { needToRebatch = true; break; } else { colorCounts[rgba] = true; } } if (!needToRebatch) { primitive._batchDirty = false; return false; } if ( needToRebatch && !primitive.forceRebatch && primitive._framesSinceLastRebatch < 120 ) { ++primitive._framesSinceLastRebatch; return; } batchedIndices.sort(compareColors); if (context.webgl2) { rebatchGPU(primitive, batchedIndices); } else { rebatchCPU(primitive, batchedIndices); } primitive._framesSinceLastRebatch = 0; primitive._batchDirty = false; primitive._pickCommandsDirty = true; primitive._wireframeDirty = true; return true; } function createColorCommands(primitive, context) { var needsRebatch = rebatchCommands(primitive, context); var commands = primitive._commands; var batchedIndices = primitive._batchedIndices; var length = batchedIndices.length; var commandsLength = length * 2; if ( defined(commands) && !needsRebatch && commands.length === commandsLength ) { return; } commands.length = commandsLength; var vertexArray = primitive._va; var sp = primitive._sp; var modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY); var uniformMap = primitive._uniformMap; var bv = primitive._boundingVolume; for (var j = 0; j < length; ++j) { var offset = batchedIndices[j].offset; var count = batchedIndices[j].count; var stencilDepthCommand = commands[j * 2]; if (!defined(stencilDepthCommand)) { stencilDepthCommand = commands[j * 2] = new DrawCommand({ owner: primitive, }); } stencilDepthCommand.vertexArray = vertexArray; stencilDepthCommand.modelMatrix = modelMatrix; stencilDepthCommand.offset = offset; stencilDepthCommand.count = count; stencilDepthCommand.renderState = primitive._rsStencilDepthPass; stencilDepthCommand.shaderProgram = sp; stencilDepthCommand.uniformMap = uniformMap; stencilDepthCommand.boundingVolume = bv; stencilDepthCommand.cull = false; stencilDepthCommand.pass = Pass$1.TERRAIN_CLASSIFICATION; var stencilDepthDerivedCommand = DrawCommand.shallowClone( stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset ); stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles; stencilDepthDerivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand; var colorCommand = commands[j * 2 + 1]; if (!defined(colorCommand)) { colorCommand = commands[j * 2 + 1] = new DrawCommand({ owner: primitive, }); } colorCommand.vertexArray = vertexArray; colorCommand.modelMatrix = modelMatrix; colorCommand.offset = offset; colorCommand.count = count; colorCommand.renderState = primitive._rsColorPass; colorCommand.shaderProgram = sp; colorCommand.uniformMap = uniformMap; colorCommand.boundingVolume = bv; colorCommand.cull = false; colorCommand.pass = Pass$1.TERRAIN_CLASSIFICATION; var colorDerivedCommand = DrawCommand.shallowClone( colorCommand, colorCommand.derivedCommands.tileset ); colorDerivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; colorCommand.derivedCommands.tileset = colorDerivedCommand; } primitive._commandsDirty = true; } function createColorCommandsIgnoreShow(primitive, frameState) { if ( primitive.classificationType === ClassificationType$1.TERRAIN || !frameState.invertClassification || (defined(primitive._commandsIgnoreShow) && !primitive._commandsDirty) ) { return; } var commands = primitive._commands; var commandsIgnoreShow = primitive._commandsIgnoreShow; var spStencil = primitive._spStencil; var commandsLength = commands.length; var length = (commandsIgnoreShow.length = commandsLength / 2); var commandIndex = 0; for (var j = 0; j < length; ++j) { var commandIgnoreShow = (commandsIgnoreShow[j] = DrawCommand.shallowClone( commands[commandIndex], commandsIgnoreShow[j] )); commandIgnoreShow.shaderProgram = spStencil; commandIgnoreShow.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION_IGNORE_SHOW; commandIndex += 2; } primitive._commandsDirty = false; } function createPickCommands(primitive) { if (!primitive._pickCommandsDirty) { return; } var length = primitive._indexOffsets.length; var pickCommands = primitive._pickCommands; pickCommands.length = length * 2; var vertexArray = primitive._va; var spStencil = primitive._spStencil; var spPick = primitive._spPick; var modelMatrix = defaultValue(primitive._modelMatrix, Matrix4.IDENTITY); var uniformMap = primitive._uniformMap; for (var j = 0; j < length; ++j) { var offset = primitive._indexOffsets[j]; var count = primitive._indexCounts[j]; var bv = defined(primitive._boundingVolumes) ? primitive._boundingVolumes[j] : primitive.boundingVolume; var stencilDepthCommand = pickCommands[j * 2]; if (!defined(stencilDepthCommand)) { stencilDepthCommand = pickCommands[j * 2] = new DrawCommand({ owner: primitive, pickOnly: true, }); } stencilDepthCommand.vertexArray = vertexArray; stencilDepthCommand.modelMatrix = modelMatrix; stencilDepthCommand.offset = offset; stencilDepthCommand.count = count; stencilDepthCommand.renderState = primitive._rsStencilDepthPass; stencilDepthCommand.shaderProgram = spStencil; stencilDepthCommand.uniformMap = uniformMap; stencilDepthCommand.boundingVolume = bv; stencilDepthCommand.pass = Pass$1.TERRAIN_CLASSIFICATION; var stencilDepthDerivedCommand = DrawCommand.shallowClone( stencilDepthCommand, stencilDepthCommand.derivedCommands.tileset ); stencilDepthDerivedCommand.renderState = primitive._rsStencilDepthPass3DTiles; stencilDepthDerivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; stencilDepthCommand.derivedCommands.tileset = stencilDepthDerivedCommand; var colorCommand = pickCommands[j * 2 + 1]; if (!defined(colorCommand)) { colorCommand = pickCommands[j * 2 + 1] = new DrawCommand({ owner: primitive, pickOnly: true, }); } colorCommand.vertexArray = vertexArray; colorCommand.modelMatrix = modelMatrix; colorCommand.offset = offset; colorCommand.count = count; colorCommand.renderState = primitive._rsPickPass; colorCommand.shaderProgram = spPick; colorCommand.uniformMap = uniformMap; colorCommand.boundingVolume = bv; colorCommand.pass = Pass$1.TERRAIN_CLASSIFICATION; var colorDerivedCommand = DrawCommand.shallowClone( colorCommand, colorCommand.derivedCommands.tileset ); colorDerivedCommand.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; colorCommand.derivedCommands.tileset = colorDerivedCommand; } primitive._pickCommandsDirty = false; } /** * Creates features for each mesh and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed. */ Vector3DTilePrimitive.prototype.createFeatures = function (content, features) { var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; features[batchId] = new Cesium3DTileFeature(content, batchId); } }; /** * Colors the entire tile when enabled is true. The resulting color will be (mesh batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePrimitive.prototype.applyDebugSettings = function (enabled, color) { this._highlightColor = enabled ? color : this._constantColor; }; function clearStyle$2(polygons, features) { polygons._updatingAllCommands = true; var batchIds = polygons._batchIds; var length = batchIds.length; var i; for (i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.show = true; feature.color = Color.WHITE; } var batchedIndices = polygons._batchedIndices; length = batchedIndices.length; for (i = 0; i < length; ++i) { batchedIndices[i].color = Color.clone(Color.WHITE); } polygons._updatingAllCommands = false; polygons._batchDirty = true; } var scratchColor$j = new Color(); var DEFAULT_COLOR_VALUE$1 = Color.WHITE; var DEFAULT_SHOW_VALUE$1 = true; var complexExpressionReg = /\$/; /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePrimitive.prototype.applyStyle = function (style, features) { if (!defined(style)) { clearStyle$2(this, features); return; } var colorExpression = style.color; var isSimpleStyle = colorExpression instanceof Expression && !complexExpressionReg.test(colorExpression.expression); this._updatingAllCommands = isSimpleStyle; var batchIds = this._batchIds; var length = batchIds.length; var i; for (i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.color = defined(style.color) ? style.color.evaluateColor(feature, scratchColor$j) : DEFAULT_COLOR_VALUE$1; feature.show = defined(style.show) ? style.show.evaluate(feature) : DEFAULT_SHOW_VALUE$1; } if (isSimpleStyle) { var batchedIndices = this._batchedIndices; length = batchedIndices.length; for (i = 0; i < length; ++i) { batchedIndices[i].color = Color.clone(Color.WHITE); } this._updatingAllCommands = false; this._batchDirty = true; } }; /** * Call when updating the color of a mesh with batchId changes color. The meshes will need to be re-batched * on the next update. * * @param {Number} batchId The batch id of the meshes whose color has changed. * @param {Color} color The new polygon color. */ Vector3DTilePrimitive.prototype.updateCommands = function (batchId, color) { if (this._updatingAllCommands) { return; } var batchIdLookUp = this._batchIdLookUp; var index = batchIdLookUp[batchId]; if (!defined(index)) { return; } var indexOffsets = this._indexOffsets; var indexCounts = this._indexCounts; var offset = indexOffsets[index]; var count = indexCounts[index]; var batchedIndices = this._batchedIndices; var length = batchedIndices.length; var i; for (i = 0; i < length; ++i) { var batchedOffset = batchedIndices[i].offset; var batchedCount = batchedIndices[i].count; if (offset >= batchedOffset && offset < batchedOffset + batchedCount) { break; } } batchedIndices.push( new Vector3DTileBatch({ color: Color.clone(color), offset: offset, count: count, batchIds: [batchId], }) ); var startIds = []; var endIds = []; var batchIds = batchedIndices[i].batchIds; var batchIdsLength = batchIds.length; for (var j = 0; j < batchIdsLength; ++j) { var id = batchIds[j]; if (id === batchId) { continue; } var offsetIndex = batchIdLookUp[id]; if (indexOffsets[offsetIndex] < offset) { startIds.push(id); } else { endIds.push(id); } } if (endIds.length !== 0) { batchedIndices.push( new Vector3DTileBatch({ color: Color.clone(batchedIndices[i].color), offset: offset + count, count: batchedIndices[i].offset + batchedIndices[i].count - (offset + count), batchIds: endIds, }) ); } if (startIds.length !== 0) { batchedIndices[i].count = offset - batchedIndices[i].offset; batchedIndices[i].batchIds = startIds; } else { batchedIndices.splice(i, 1); } this._batchDirty = true; }; function queueCommands$1(primitive, frameState, commands, commandsIgnoreShow) { var classificationType = primitive.classificationType; var queueTerrainCommands = classificationType !== ClassificationType$1.CESIUM_3D_TILE; var queue3DTilesCommands = classificationType !== ClassificationType$1.TERRAIN; var commandList = frameState.commandList; var commandLength = commands.length; var command; var i; for (i = 0; i < commandLength; ++i) { if (queueTerrainCommands) { command = commands[i]; command.pass = Pass$1.TERRAIN_CLASSIFICATION; commandList.push(command); } if (queue3DTilesCommands) { command = commands[i].derivedCommands.tileset; command.pass = Pass$1.CESIUM_3D_TILE_CLASSIFICATION; commandList.push(command); } } if (!frameState.invertClassification || !defined(commandsIgnoreShow)) { return; } commandLength = commandsIgnoreShow.length; for (i = 0; i < commandLength; ++i) { commandList.push(commandsIgnoreShow[i]); } } function queueWireframeCommands(frameState, commands) { var commandList = frameState.commandList; var commandLength = commands.length; for (var i = 0; i < commandLength; i += 2) { var command = commands[i + 1]; command.pass = Pass$1.OPAQUE; commandList.push(command); } } function updateWireframe$2(primitive) { var earlyExit = primitive.debugWireframe === primitive._debugWireframe; earlyExit = earlyExit && !(primitive.debugWireframe && primitive._wireframeDirty); if (earlyExit) { return; } if (!defined(primitive._rsWireframe)) { primitive._rsWireframe = RenderState.fromCache({}); } var rs; var type; if (primitive.debugWireframe) { rs = primitive._rsWireframe; type = PrimitiveType$1.LINES; } else { rs = primitive._rsColorPass; type = PrimitiveType$1.TRIANGLES; } var commands = primitive._commands; var commandLength = commands.length; for (var i = 0; i < commandLength; i += 2) { var command = commands[i + 1]; command.renderState = rs; command.primitiveType = type; } primitive._debugWireframe = primitive.debugWireframe; primitive._wireframeDirty = false; } /** * Updates the batches and queues the commands for rendering. * * @param {FrameState} frameState The current frame state. */ Vector3DTilePrimitive.prototype.update = function (frameState) { var context = frameState.context; createVertexArray$3(this, context); createShaders$2(this, context); createRenderStates$3(this); createUniformMap$5(this, context); var passes = frameState.passes; if (passes.render) { createColorCommands(this, context); createColorCommandsIgnoreShow(this, frameState); updateWireframe$2(this); if (this._debugWireframe) { queueWireframeCommands(frameState, this._commands); } else { queueCommands$1(this, frameState, this._commands, this._commandsIgnoreShow); } } if (passes.pick) { createPickCommands(this); queueCommands$1(this, frameState, this._pickCommands); } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTilePrimitive.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTilePrimitive.prototype.destroy = function () { this._va = this._va && this._va.destroy(); this._sp = this._sp && this._sp.destroy(); this._spPick = this._spPick && this._spPick.destroy(); this._vaSwap = this._vaSwap && this._vaSwap.destroy(); return destroyObject(this); }; var boundingSphereCartesian3Scratch$1 = new Cartesian3(); var ModelState$1 = ModelUtility.ModelState; /////////////////////////////////////////////////////////////////////////// /** * A 3D model for classifying other 3D assets based on glTF, the runtime 3D asset format. * This is a special case when a model of a 3D tileset becomes a classifier when setting {@link Cesium3DTileset#classificationType}. * * @alias ClassificationModel * @constructor * * @private * * @param {Object} options Object with the following properties: * @param {ArrayBuffer|Uint8Array} options.gltf A binary glTF buffer. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {ClassificationType} [options.classificationType] What this model will classify. * * @exception {RuntimeError} Only binary glTF is supported. * @exception {RuntimeError} Buffer data must be embedded in the binary glTF. * @exception {RuntimeError} Only one node is supported for classification and it must have a mesh. * @exception {RuntimeError} Only one mesh is supported when using b3dm for classification. * @exception {RuntimeError} Only one primitive per mesh is supported when using b3dm for classification. * @exception {RuntimeError} The mesh must have a position attribute. * @exception {RuntimeError} The mesh must have a batch id attribute. */ function ClassificationModel(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var gltf = options.gltf; if (gltf instanceof ArrayBuffer) { gltf = new Uint8Array(gltf); } if (gltf instanceof Uint8Array) { // Parse and update binary glTF gltf = parseGlb(gltf); updateVersion(gltf); addDefaults(gltf); processModelMaterialsCommon(gltf); processPbrMaterials(gltf); } else { throw new RuntimeError("Only binary glTF is supported as a classifier."); } ForEach.buffer(gltf, function (buffer) { if (!defined(buffer.extras._pipeline.source)) { throw new RuntimeError( "Buffer data must be embedded in the binary gltf." ); } }); var gltfNodes = gltf.nodes; var gltfMeshes = gltf.meshes; var gltfNode = gltfNodes[0]; var meshId = gltfNode.mesh; if (gltfNodes.length !== 1 || !defined(meshId)) { throw new RuntimeError( "Only one node is supported for classification and it must have a mesh." ); } if (gltfMeshes.length !== 1) { throw new RuntimeError( "Only one mesh is supported when using b3dm for classification." ); } var gltfPrimitives = gltfMeshes[0].primitives; if (gltfPrimitives.length !== 1) { throw new RuntimeError( "Only one primitive per mesh is supported when using b3dm for classification." ); } var gltfPositionAttribute = gltfPrimitives[0].attributes.POSITION; if (!defined(gltfPositionAttribute)) { throw new RuntimeError("The mesh must have a position attribute."); } var gltfBatchIdAttribute = gltfPrimitives[0].attributes._BATCHID; if (!defined(gltfBatchIdAttribute)) { throw new RuntimeError("The mesh must have a batch id attribute."); } this._gltf = gltf; /** * Determines if the model primitive will be shown. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * The 4x4 transformation matrix that transforms the model from model to world coordinates. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type {Matrix4} * * @default {@link Matrix4.IDENTITY} * * @example * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0); * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin); */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = Matrix4.clone(this.modelMatrix); this._ready = false; this._readyPromise = when.defer(); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds * to one draw command. A glTF mesh has an array of primitives, often of length one. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this._debugShowBoundingVolume = false; /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the model in wireframe. *

* * @type {Boolean} * * @default false */ this.debugWireframe = defaultValue(options.debugWireframe, false); this._debugWireframe = false; this._classificationType = options.classificationType; // Undocumented options this._vertexShaderLoaded = options.vertexShaderLoaded; this._classificationShaderLoaded = options.classificationShaderLoaded; this._uniformMapLoaded = options.uniformMapLoaded; this._pickIdLoaded = options.pickIdLoaded; this._ignoreCommands = defaultValue(options.ignoreCommands, false); this._upAxis = defaultValue(options.upAxis, Axis$1.Y); this._batchTable = options.batchTable; this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and axis this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins this._boundingSphere = undefined; this._scaledBoundingSphere = new BoundingSphere(); this._state = ModelState$1.NEEDS_LOAD; this._loadResources = undefined; this._mode = undefined; this._dirty = false; // true when the model was transformed this frame this._nodeMatrix = new Matrix4(); this._primitive = undefined; this._extensionsUsed = undefined; // Cached used glTF extensions this._extensionsRequired = undefined; // Cached required glTF extensions this._quantizedUniforms = undefined; // Quantized uniforms for WEB3D_quantized_attributes this._buffers = {}; this._vertexArray = undefined; this._shaderProgram = undefined; this._uniformMap = undefined; this._geometryByteLength = 0; this._trianglesLength = 0; // CESIUM_RTC extension this._rtcCenter = undefined; // reference to either 3D or 2D this._rtcCenterEye = undefined; // in eye coordinates this._rtcCenter3D = undefined; // in world coordinates this._rtcCenter2D = undefined; // in projected world coordinates } Object.defineProperties(ClassificationModel.prototype, { /** * The object for the glTF JSON, including properties with default values omitted * from the JSON provided to this model. * * @memberof ClassificationModel.prototype * * @type {Object} * @readonly * * @default undefined */ gltf: { get: function () { return this._gltf; }, }, /** * The model's bounding sphere in its local coordinate system. * * @memberof ClassificationModel.prototype * * @type {BoundingSphere} * @readonly * * @default undefined * * @exception {DeveloperError} The model is not loaded. Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true. * * @example * // Center in WGS84 coordinates * var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3()); */ boundingSphere: { get: function () { //>>includeStart('debug', pragmas.debug); if (this._state !== ModelState$1.LOADED) { throw new DeveloperError( "The model is not loaded. Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true." ); } //>>includeEnd('debug'); var modelMatrix = this.modelMatrix; var nonUniformScale = Matrix4.getScale( modelMatrix, boundingSphereCartesian3Scratch$1 ); var scaledBoundingSphere = this._scaledBoundingSphere; scaledBoundingSphere.center = Cartesian3.multiplyComponents( this._boundingSphere.center, nonUniformScale, scaledBoundingSphere.center ); scaledBoundingSphere.radius = Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius; if (defined(this._rtcCenter)) { Cartesian3.add( this._rtcCenter, scaledBoundingSphere.center, scaledBoundingSphere.center ); } return scaledBoundingSphere; }, }, /** * When true, this model is ready to render, i.e., the external binary, image, * and shader files were downloaded and the WebGL resources were created. This is set to * true right before {@link ClassificationModel#readyPromise} is resolved. * * @memberof ClassificationModel.prototype * * @type {Boolean} * @readonly * * @default false */ ready: { get: function () { return this._ready; }, }, /** * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image, * and shader files were downloaded and the WebGL resources were created. *

* This promise is resolved at the end of the frame before the first frame the model is rendered in. *

* * @memberof ClassificationModel.prototype * @type {Promise.} * @readonly * * @see ClassificationModel#ready */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * Returns true if the model was transformed this frame * * @memberof ClassificationModel.prototype * * @type {Boolean} * @readonly * * @private */ dirty: { get: function () { return this._dirty; }, }, /** * Returns an object with all of the glTF extensions used. * * @memberof ClassificationModel.prototype * * @type {Object} * @readonly */ extensionsUsed: { get: function () { if (!defined(this._extensionsUsed)) { this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf); } return this._extensionsUsed; }, }, /** * Returns an object with all of the glTF extensions required. * * @memberof ClassificationModel.prototype * * @type {Object} * @readonly */ extensionsRequired: { get: function () { if (!defined(this._extensionsRequired)) { this._extensionsRequired = ModelUtility.getRequiredExtensions( this.gltf ); } return this._extensionsRequired; }, }, /** * Gets the model's up-axis. * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up. * * @memberof ClassificationModel.prototype * * @type {Number} * @default Axis.Y * @readonly * * @private */ upAxis: { get: function () { return this._upAxis; }, }, /** * Gets the model's triangle count. * * @private */ trianglesLength: { get: function () { return this._trianglesLength; }, }, /** * Gets the model's geometry memory in bytes. This includes all vertex and index buffers. * * @private */ geometryByteLength: { get: function () { return this._geometryByteLength; }, }, /** * Gets the model's texture memory in bytes. * * @private */ texturesByteLength: { get: function () { return 0; }, }, /** * Gets the model's classification type. * @memberof ClassificationModel.prototype * @type {ClassificationType} */ classificationType: { get: function () { return this._classificationType; }, }, }); /////////////////////////////////////////////////////////////////////////// function addBuffersToLoadResources$1(model) { var gltf = model.gltf; var loadResources = model._loadResources; ForEach.buffer(gltf, function (buffer, id) { loadResources.buffers[id] = buffer.extras._pipeline.source; }); } function parseBufferViews$1(model) { var bufferViews = model.gltf.bufferViews; var vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate; // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below. ForEach.bufferView(model.gltf, function (bufferView, id) { if (bufferView.target === WebGLConstants$1.ARRAY_BUFFER) { vertexBuffersToCreate.enqueue(id); } }); var indexBuffersToCreate = model._loadResources.indexBuffersToCreate; var indexBufferIds = {}; // The Cesium Renderer requires knowing the datatype for an index buffer // at creation type, which is not part of the glTF bufferview so loop // through glTF accessors to create the bufferview's index buffer. ForEach.accessor(model.gltf, function (accessor) { var bufferViewId = accessor.bufferView; var bufferView = bufferViews[bufferViewId]; if ( bufferView.target === WebGLConstants$1.ELEMENT_ARRAY_BUFFER && !defined(indexBufferIds[bufferViewId]) ) { indexBufferIds[bufferViewId] = true; indexBuffersToCreate.enqueue({ id: bufferViewId, componentType: accessor.componentType, }); } }); } function createVertexBuffer$2(bufferViewId, model) { var loadResources = model._loadResources; var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; var vertexBuffer = loadResources.getBuffer(bufferView); model._buffers[bufferViewId] = vertexBuffer; model._geometryByteLength += vertexBuffer.byteLength; } function createIndexBuffer$1(bufferViewId, componentType, model) { var loadResources = model._loadResources; var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; var indexBuffer = { typedArray: loadResources.getBuffer(bufferView), indexDatatype: componentType, }; model._buffers[bufferViewId] = indexBuffer; model._geometryByteLength += indexBuffer.typedArray.byteLength; } function createBuffers$1(model) { var loadResources = model._loadResources; if (loadResources.pendingBufferLoads !== 0) { return; } var vertexBuffersToCreate = loadResources.vertexBuffersToCreate; var indexBuffersToCreate = loadResources.indexBuffersToCreate; while (vertexBuffersToCreate.length > 0) { createVertexBuffer$2(vertexBuffersToCreate.dequeue(), model); } while (indexBuffersToCreate.length > 0) { var i = indexBuffersToCreate.dequeue(); createIndexBuffer$1(i.id, i.componentType, model); } } function modifyShaderForQuantizedAttributes$1(shader, model) { var primitive = model.gltf.meshes[0].primitives[0]; var result = ModelUtility.modifyShaderForQuantizedAttributes( model.gltf, primitive, shader ); model._quantizedUniforms = result.uniforms; return result.shader; } function modifyShader$1(shader, callback) { if (defined(callback)) { shader = callback(shader); } return shader; } function createProgram$1(model) { var gltf = model.gltf; var positionName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "POSITION" ); var batchIdName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "_BATCHID" ); var attributeLocations = {}; attributeLocations[positionName] = 0; attributeLocations[batchIdName] = 1; var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "MODELVIEWPROJECTION" ); var uniformDecl; var toClip; if (!defined(modelViewProjectionName)) { var projectionName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "PROJECTION" ); var modelViewName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "MODELVIEW" ); if (!defined(modelViewName)) { modelViewName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "CESIUM_RTC_MODELVIEW" ); } uniformDecl = "uniform mat4 " + modelViewName + ";\n" + "uniform mat4 " + projectionName + ";\n"; toClip = projectionName + " * " + modelViewName + " * vec4(" + positionName + ", 1.0)"; } else { uniformDecl = "uniform mat4 " + modelViewProjectionName + ";\n"; toClip = modelViewProjectionName + " * vec4(" + positionName + ", 1.0)"; } var computePosition = " vec4 positionInClipCoords = " + toClip + ";\n"; var vs = "attribute vec3 " + positionName + ";\n" + "attribute float " + batchIdName + ";\n" + uniformDecl + "void main() {\n" + computePosition + " gl_Position = czm_depthClamp(positionInClipCoords);\n" + "}\n"; var fs = "#ifdef GL_EXT_frag_depth\n" + "#extension GL_EXT_frag_depth : enable\n" + "#endif\n" + "void main() \n" + "{ \n" + " gl_FragColor = vec4(1.0); \n" + " czm_writeDepthClamp();\n" + "}\n"; if (model.extensionsUsed.WEB3D_quantized_attributes) { vs = modifyShaderForQuantizedAttributes$1(vs, model); } var drawVS = modifyShader$1(vs, model._vertexShaderLoaded); var drawFS = modifyShader$1(fs, model._classificationShaderLoaded); model._shaderProgram = { vertexShaderSource: drawVS, fragmentShaderSource: drawFS, attributeLocations: attributeLocations, }; } function getAttributeLocations$2() { return { POSITION: 0, _BATCHID: 1, }; } function createVertexArray$2(model) { var loadResources = model._loadResources; if (!loadResources.finishedBuffersCreation() || defined(model._vertexArray)) { return; } var rendererBuffers = model._buffers; var gltf = model.gltf; var accessors = gltf.accessors; var meshes = gltf.meshes; var primitives = meshes[0].primitives; var primitive = primitives[0]; var attributeLocations = getAttributeLocations$2(); var attributes = {}; ForEach.meshPrimitiveAttribute(primitive, function ( accessorId, attributeName ) { // Skip if the attribute is not used by the material, e.g., because the asset // was exported with an attribute that wasn't used and the asset wasn't optimized. var attributeLocation = attributeLocations[attributeName]; if (defined(attributeLocation)) { var a = accessors[accessorId]; attributes[attributeName] = { index: attributeLocation, vertexBuffer: rendererBuffers[a.bufferView], componentsPerAttribute: numberOfComponentsForType(a.type), componentDatatype: a.componentType, offsetInBytes: a.byteOffset, strideInBytes: getAccessorByteStride(gltf, a), }; } }); var indexBuffer; if (defined(primitive.indices)) { var accessor = accessors[primitive.indices]; indexBuffer = rendererBuffers[accessor.bufferView]; } model._vertexArray = { attributes: attributes, indexBuffer: indexBuffer, }; } var gltfSemanticUniforms = { PROJECTION: function (uniformState, model) { return ModelUtility.getGltfSemanticUniforms().PROJECTION( uniformState, model ); }, MODELVIEW: function (uniformState, model) { return ModelUtility.getGltfSemanticUniforms().MODELVIEW( uniformState, model ); }, CESIUM_RTC_MODELVIEW: function (uniformState, model) { return ModelUtility.getGltfSemanticUniforms().CESIUM_RTC_MODELVIEW( uniformState, model ); }, MODELVIEWPROJECTION: function (uniformState, model) { return ModelUtility.getGltfSemanticUniforms().MODELVIEWPROJECTION( uniformState, model ); }, }; function createUniformMap$4(model, context) { if (defined(model._uniformMap)) { return; } var uniformMap = {}; ForEach.technique(model.gltf, function (technique) { ForEach.techniqueUniform(technique, function (uniform, uniformName) { if ( !defined(uniform.semantic) || !defined(gltfSemanticUniforms[uniform.semantic]) ) { return; } uniformMap[uniformName] = gltfSemanticUniforms[uniform.semantic]( context.uniformState, model ); }); }); model._uniformMap = uniformMap; } function createUniformsForQuantizedAttributes$1(model, primitive) { return ModelUtility.createUniformsForQuantizedAttributes( model.gltf, primitive, model._quantizedUniforms ); } function triangleCountFromPrimitiveIndices$1(primitive, indicesCount) { switch (primitive.mode) { case PrimitiveType$1.TRIANGLES: return indicesCount / 3; case PrimitiveType$1.TRIANGLE_STRIP: case PrimitiveType$1.TRIANGLE_FAN: return Math.max(indicesCount - 2, 0); default: return 0; } } function createPrimitive$2(model) { var batchTable = model._batchTable; var uniformMap = model._uniformMap; var vertexArray = model._vertexArray; var gltf = model.gltf; var accessors = gltf.accessors; var gltfMeshes = gltf.meshes; var primitive = gltfMeshes[0].primitives[0]; var ix = accessors[primitive.indices]; var positionAccessor = primitive.attributes.POSITION; var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor); var boundingSphere = BoundingSphere.fromCornerPoints( Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max) ); var offset; var count; if (defined(ix)) { count = ix.count; offset = ix.byteOffset / IndexDatatype$1.getSizeInBytes(ix.componentType); // glTF has offset in bytes. Cesium has offsets in indices } else { var positions = accessors[primitive.attributes.POSITION]; count = positions.count; offset = 0; } // Update model triangle count using number of indices model._trianglesLength += triangleCountFromPrimitiveIndices$1(primitive, count); // Allow callback to modify the uniformMap if (defined(model._uniformMapLoaded)) { uniformMap = model._uniformMapLoaded(uniformMap); } // Add uniforms for decoding quantized attributes if used if (model.extensionsUsed.WEB3D_quantized_attributes) { var quantizedUniformMap = createUniformsForQuantizedAttributes$1( model, primitive ); uniformMap = combine$2(uniformMap, quantizedUniformMap); } var attribute = vertexArray.attributes.POSITION; var componentDatatype = attribute.componentDatatype; var typedArray = attribute.vertexBuffer; var byteOffset = typedArray.byteOffset; var bufferLength = typedArray.byteLength / ComponentDatatype$1.getSizeInBytes(componentDatatype); var positionsBuffer = ComponentDatatype$1.createArrayBufferView( componentDatatype, typedArray.buffer, byteOffset, bufferLength ); attribute = vertexArray.attributes._BATCHID; componentDatatype = attribute.componentDatatype; typedArray = attribute.vertexBuffer; byteOffset = typedArray.byteOffset; bufferLength = typedArray.byteLength / ComponentDatatype$1.getSizeInBytes(componentDatatype); var vertexBatchIds = ComponentDatatype$1.createArrayBufferView( componentDatatype, typedArray.buffer, byteOffset, bufferLength ); var buffer = vertexArray.indexBuffer.typedArray; var indices; if (vertexArray.indexBuffer.indexDatatype === IndexDatatype$1.UNSIGNED_SHORT) { indices = new Uint16Array( buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint16Array.BYTES_PER_ELEMENT ); } else { indices = new Uint32Array( buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint32Array.BYTES_PER_ELEMENT ); } positionsBuffer = arraySlice(positionsBuffer); vertexBatchIds = arraySlice(vertexBatchIds); indices = arraySlice(indices, offset, offset + count); var batchIds = []; var indexCounts = []; var indexOffsets = []; var batchedIndices = []; var currentId = vertexBatchIds[indices[0]]; batchIds.push(currentId); indexOffsets.push(0); var batchId; var indexOffset; var indexCount; var indicesLength = indices.length; for (var j = 1; j < indicesLength; ++j) { batchId = vertexBatchIds[indices[j]]; if (batchId !== currentId) { indexOffset = indexOffsets[indexOffsets.length - 1]; indexCount = j - indexOffset; batchIds.push(batchId); indexCounts.push(indexCount); indexOffsets.push(j); batchedIndices.push( new Vector3DTileBatch({ offset: indexOffset, count: indexCount, batchIds: [currentId], color: Color.WHITE, }) ); currentId = batchId; } } indexOffset = indexOffsets[indexOffsets.length - 1]; indexCount = indicesLength - indexOffset; indexCounts.push(indexCount); batchedIndices.push( new Vector3DTileBatch({ offset: indexOffset, count: indexCount, batchIds: [currentId], color: Color.WHITE, }) ); var shader = model._shaderProgram; var vertexShaderSource = shader.vertexShaderSource; var fragmentShaderSource = shader.fragmentShaderSource; var attributeLocations = shader.attributeLocations; var pickId = defined(model._pickIdLoaded) ? model._pickIdLoaded() : undefined; model._primitive = new Vector3DTilePrimitive({ classificationType: model._classificationType, positions: positionsBuffer, indices: indices, indexOffsets: indexOffsets, indexCounts: indexCounts, batchIds: batchIds, vertexBatchIds: vertexBatchIds, batchedIndices: batchedIndices, batchTable: batchTable, boundingVolume: new BoundingSphere(), // updated in update() _vertexShaderSource: vertexShaderSource, _fragmentShaderSource: fragmentShaderSource, _attributeLocations: attributeLocations, _uniformMap: uniformMap, _pickId: pickId, _modelMatrix: new Matrix4(), // updated in update() _boundingSphere: boundingSphere, // used to update boundingVolume }); // Release CPU resources model._buffers = undefined; model._vertexArray = undefined; model._shaderProgram = undefined; model._uniformMap = undefined; } function createRuntimeNodes$1(model) { var loadResources = model._loadResources; if (!loadResources.finished()) { return; } if (defined(model._primitive)) { return; } var gltf = model.gltf; var nodes = gltf.nodes; var gltfNode = nodes[0]; model._nodeMatrix = ModelUtility.getTransform(gltfNode, model._nodeMatrix); createPrimitive$2(model); } function createResources$6(model, frameState) { var context = frameState.context; ModelUtility.checkSupportedGlExtensions(model.gltf.glExtensionsUsed, context); createBuffers$1(model); // using glTF bufferViews createProgram$1(model); createVertexArray$2(model); // using glTF meshes createUniformMap$4(model, context); // using glTF materials/techniques createRuntimeNodes$1(model); // using glTF scene } /////////////////////////////////////////////////////////////////////////// var scratchComputedTranslation$2 = new Cartesian4(); var scratchComputedMatrixIn2D$1 = new Matrix4(); function updateNodeModelMatrix( model, modelTransformChanged, justLoaded, projection ) { var computedModelMatrix = model._computedModelMatrix; if (model._mode !== SceneMode$1.SCENE3D && !model._ignoreCommands) { var translation = Matrix4.getColumn( computedModelMatrix, 3, scratchComputedTranslation$2 ); if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { computedModelMatrix = Transforms.basisTo2D( projection, computedModelMatrix, scratchComputedMatrixIn2D$1 ); model._rtcCenter = model._rtcCenter3D; } else { var center = model.boundingSphere.center; var to2D = Transforms.wgs84To2DModelMatrix( projection, center, scratchComputedMatrixIn2D$1 ); computedModelMatrix = Matrix4.multiply( to2D, computedModelMatrix, scratchComputedMatrixIn2D$1 ); if (defined(model._rtcCenter)) { Matrix4.setTranslation( computedModelMatrix, Cartesian4.UNIT_W, computedModelMatrix ); model._rtcCenter = model._rtcCenter2D; } } } var primitive = model._primitive; if (modelTransformChanged || justLoaded) { Matrix4.multiplyTransformation( computedModelMatrix, model._nodeMatrix, primitive._modelMatrix ); BoundingSphere.transform( primitive._boundingSphere, primitive._modelMatrix, primitive._boundingVolume ); if (defined(model._rtcCenter)) { Cartesian3.add( model._rtcCenter, primitive._boundingVolume.center, primitive._boundingVolume.center ); } } } /////////////////////////////////////////////////////////////////////////// ClassificationModel.prototype.updateCommands = function (batchId, color) { this._primitive.updateCommands(batchId, color); }; ClassificationModel.prototype.update = function (frameState) { if (frameState.mode === SceneMode$1.MORPHING) { return; } if (!FeatureDetection.supportsWebP.initialized) { FeatureDetection.supportsWebP.initialize(); return; } var supportsWebP = FeatureDetection.supportsWebP(); if (this._state === ModelState$1.NEEDS_LOAD && defined(this.gltf)) { this._state = ModelState$1.LOADING; if (this._state !== ModelState$1.FAILED) { var extensions = this.gltf.extensions; if (defined(extensions) && defined(extensions.CESIUM_RTC)) { var center = Cartesian3.fromArray(extensions.CESIUM_RTC.center); if (!Cartesian3.equals(center, Cartesian3.ZERO)) { this._rtcCenter3D = center; var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var cartographic = ellipsoid.cartesianToCartographic( this._rtcCenter3D ); var projectedCart = projection.project(cartographic); Cartesian3.fromElements( projectedCart.z, projectedCart.x, projectedCart.y, projectedCart ); this._rtcCenter2D = projectedCart; this._rtcCenterEye = new Cartesian3(); this._rtcCenter = this._rtcCenter3D; } } this._loadResources = new ModelLoadResources(); ModelUtility.parseBuffers(this); } } var loadResources = this._loadResources; var justLoaded = false; if (this._state === ModelState$1.LOADING) { // Transition from LOADING -> LOADED once resources are downloaded and created. // Textures may continue to stream in while in the LOADED state. if (loadResources.pendingBufferLoads === 0) { ModelUtility.checkSupportedExtensions( this.extensionsRequired, supportsWebP ); addBuffersToLoadResources$1(this); parseBufferViews$1(this); this._boundingSphere = ModelUtility.computeBoundingSphere(this); this._initialRadius = this._boundingSphere.radius; createResources$6(this, frameState); } if (loadResources.finished()) { this._state = ModelState$1.LOADED; justLoaded = true; } } if (defined(loadResources) && this._state === ModelState$1.LOADED) { if (!justLoaded) { createResources$6(this, frameState); } if (loadResources.finished()) { this._loadResources = undefined; // Clear CPU memory since WebGL resources were created. } } var show = this.show; if ((show && this._state === ModelState$1.LOADED) || justLoaded) { this._dirty = false; var modelMatrix = this.modelMatrix; var modeChanged = frameState.mode !== this._mode; this._mode = frameState.mode; // ClassificationModel's model matrix needs to be updated var modelTransformChanged = !Matrix4.equals(this._modelMatrix, modelMatrix) || modeChanged; if (modelTransformChanged || justLoaded) { Matrix4.clone(modelMatrix, this._modelMatrix); var computedModelMatrix = this._computedModelMatrix; Matrix4.clone(modelMatrix, computedModelMatrix); if (this._upAxis === Axis$1.Y) { Matrix4.multiplyTransformation( computedModelMatrix, Axis$1.Y_UP_TO_Z_UP, computedModelMatrix ); } else if (this._upAxis === Axis$1.X) { Matrix4.multiplyTransformation( computedModelMatrix, Axis$1.X_UP_TO_Z_UP, computedModelMatrix ); } } // Update modelMatrix throughout the graph as needed if (modelTransformChanged || justLoaded) { updateNodeModelMatrix( this, modelTransformChanged, justLoaded, frameState.mapProjection ); this._dirty = true; } } if (justLoaded) { // Called after modelMatrix update. var model = this; frameState.afterRender.push(function () { model._ready = true; model._readyPromise.resolve(model); }); return; } if (show && !this._ignoreCommands) { this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume; this._primitive.debugWireframe = this.debugWireframe; this._primitive.update(frameState); } }; ClassificationModel.prototype.isDestroyed = function () { return false; }; ClassificationModel.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); return destroyObject(this); }; /** * A Plane in Hessian Normal form to be used with {@link ClippingPlaneCollection}. * Compatible with mathematics functions in {@link Plane} * * @alias ClippingPlane * @constructor * * @param {Cartesian3} normal The plane's normal (normalized). * @param {Number} distance The shortest distance from the origin to the plane. The sign of * distance determines which side of the plane the origin * is on. If distance 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. */ function ClippingPlane(normal, distance) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("normal", normal); Check.typeOf.number("distance", distance); //>>includeEnd('debug'); this._distance = distance; this._normal = new UpdateChangedCartesian3(normal, this); this.onChangeCallback = undefined; this.index = -1; // to be set by ClippingPlaneCollection } Object.defineProperties(ClippingPlane.prototype, { /** * The shortest distance from the origin to the plane. The sign of * distance determines which side of the plane the origin * is on. If distance 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} * @memberof ClippingPlane.prototype */ distance: { get: function () { return this._distance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); if (defined(this.onChangeCallback) && value !== this._distance) { this.onChangeCallback(this.index); } this._distance = value; }, }, /** * The plane's normal. * * @type {Cartesian3} * @memberof ClippingPlane.prototype */ normal: { get: function () { return this._normal; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("value", value); //>>includeEnd('debug'); if ( defined(this.onChangeCallback) && !Cartesian3.equals(this._normal._cartesian3, value) ) { this.onChangeCallback(this.index); } // Set without firing callback again Cartesian3.clone(value, this._normal._cartesian3); }, }, }); /** * Create a ClippingPlane from a Plane object. * * @param {Plane} plane The plane containing parameters to copy * @param {ClippingPlane} [result] The object on which to store the result * @returns {ClippingPlane} The ClippingPlane generated from the plane's parameters. */ ClippingPlane.fromPlane = function (plane, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("plane", plane); //>>includeEnd('debug'); if (!defined(result)) { result = new ClippingPlane(plane.normal, plane.distance); } else { result.normal = plane.normal; result.distance = plane.distance; } return result; }; /** * Clones the ClippingPlane without setting its ownership. * @param {ClippingPlane} clippingPlane The ClippingPlane to be cloned * @param {ClippingPlane} [result] The object on which to store the cloned parameters. * @returns {ClippingPlane} a clone of the input ClippingPlane */ ClippingPlane.clone = function (clippingPlane, result) { if (!defined(result)) { return new ClippingPlane(clippingPlane.normal, clippingPlane.distance); } result.normal = clippingPlane.normal; result.distance = clippingPlane.distance; return result; }; /** * Wrapper on Cartesian3 that allows detection of Plane changes from "members of members," for example: * * var clippingPlane = new ClippingPlane(...); * clippingPlane.normal.z = -1.0; * * @private */ function UpdateChangedCartesian3(normal, clippingPlane) { this._clippingPlane = clippingPlane; this._cartesian3 = Cartesian3.clone(normal); } Object.defineProperties(UpdateChangedCartesian3.prototype, { x: { get: function () { return this._cartesian3.x; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); if ( defined(this._clippingPlane.onChangeCallback) && value !== this._cartesian3.x ) { this._clippingPlane.onChangeCallback(this._clippingPlane.index); } this._cartesian3.x = value; }, }, y: { get: function () { return this._cartesian3.y; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); if ( defined(this._clippingPlane.onChangeCallback) && value !== this._cartesian3.y ) { this._clippingPlane.onChangeCallback(this._clippingPlane.index); } this._cartesian3.y = value; }, }, z: { get: function () { return this._cartesian3.z; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); if ( defined(this._clippingPlane.onChangeCallback) && value !== this._cartesian3.z ) { this._clippingPlane.onChangeCallback(this._clippingPlane.index); } this._cartesian3.z = value; }, }, }); /** * Specifies a set of clipping planes. Clipping planes selectively disable rendering in a region on the * outside of the specified list of {@link ClippingPlane} objects for a single gltf model, 3D Tileset, or the globe. *

* In general the clipping planes' coordinates are relative to the object they're attached to, so a plane with distance set to 0 will clip * through the center of the object. *

*

* For 3D Tiles, the root tile's transform is used to position the clipping planes. If a transform is not defined, the root tile's {@link Cesium3DTile#boundingSphere} is used instead. *

* * @alias ClippingPlaneCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {ClippingPlane[]} [options.planes=[]] An array of {@link ClippingPlane} objects used to selectively disable rendering on the outside of each plane. * @param {Boolean} [options.enabled=true] Determines whether the clipping planes are active. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix specifying an additional transform relative to the clipping planes original coordinate system. * @param {Boolean} [options.unionClippingRegions=false] If true, a region will be clipped if it is on the outside of any plane in the collection. Otherwise, a region will only be clipped if it is on the outside of every plane. * @param {Color} [options.edgeColor=Color.WHITE] The color applied to highlight the edge along which an object is clipped. * @param {Number} [options.edgeWidth=0.0] The width, in pixels, of the highlight applied to the edge along which an object is clipped. * * @demo {@link https://sandcastle.cesium.com/?src=3D%20Tiles%20Clipping%20Planes.html|Clipping 3D Tiles and glTF models.} * @demo {@link https://sandcastle.cesium.com/?src=Terrain%20Clipping%20Planes.html|Clipping the Globe.} * * @example * // This clipping plane's distance is positive, which means its normal * // is facing the origin. This will clip everything that is behind * // the plane, which is anything with y coordinate < -5. * var clippingPlanes = new Cesium.ClippingPlaneCollection({ * planes : [ * new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), 5.0) * ], * }); * // Create an entity and attach the ClippingPlaneCollection to the model. * var entity = viewer.entities.add({ * position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, 10000), * model : { * uri : 'model.gltf', * minimumPixelSize : 128, * maximumScale : 20000, * clippingPlanes : clippingPlanes * } * }); * viewer.zoomTo(entity); */ function ClippingPlaneCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._planes = []; // Do partial texture updates if just one plane is dirty. // If many planes are dirty, refresh the entire texture. this._dirtyIndex = -1; this._multipleDirtyPlanes = false; this._enabled = defaultValue(options.enabled, true); /** * The 4x4 transformation matrix specifying an additional transform relative to the clipping planes * original coordinate system. * * @type {Matrix4} * @default Matrix4.IDENTITY */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); /** * The color applied to highlight the edge along which an object is clipped. * * @type {Color} * @default Color.WHITE */ this.edgeColor = Color.clone(defaultValue(options.edgeColor, Color.WHITE)); /** * The width, in pixels, of the highlight applied to the edge along which an object is clipped. * * @type {Number} * @default 0.0 */ this.edgeWidth = defaultValue(options.edgeWidth, 0.0); /** * An event triggered when a new clipping plane is added to the collection. Event handlers * are passed the new plane and the index at which it was added. * @type {Event} * @default Event() */ this.planeAdded = new Event(); /** * An event triggered when a new clipping plane is removed from the collection. Event handlers * are passed the new plane and the index from which it was removed. * @type {Event} * @default Event() */ this.planeRemoved = new Event(); // If this ClippingPlaneCollection has an owner, only its owner should update or destroy it. // This is because in a Cesium3DTileset multiple models may reference the tileset's ClippingPlaneCollection. this._owner = undefined; var unionClippingRegions = defaultValue(options.unionClippingRegions, false); this._unionClippingRegions = unionClippingRegions; this._testIntersection = unionClippingRegions ? unionIntersectFunction : defaultIntersectFunction; this._uint8View = undefined; this._float32View = undefined; this._clippingPlanesTexture = undefined; // Add each ClippingPlane object. var planes = options.planes; if (defined(planes)) { var planesLength = planes.length; for (var i = 0; i < planesLength; ++i) { this.add(planes[i]); } } } function unionIntersectFunction(value) { return value === Intersect$1.OUTSIDE; } function defaultIntersectFunction(value) { return value === Intersect$1.INSIDE; } Object.defineProperties(ClippingPlaneCollection.prototype, { /** * Returns the number of planes in this collection. This is commonly used with * {@link ClippingPlaneCollection#get} to iterate over all the planes * in the collection. * * @memberof ClippingPlaneCollection.prototype * @type {Number} * @readonly */ length: { get: function () { return this._planes.length; }, }, /** * If true, a region will be clipped if it is on the outside of any plane in the * collection. Otherwise, a region will only be clipped if it is on the * outside of every plane. * * @memberof ClippingPlaneCollection.prototype * @type {Boolean} * @default false */ unionClippingRegions: { get: function () { return this._unionClippingRegions; }, set: function (value) { if (this._unionClippingRegions === value) { return; } this._unionClippingRegions = value; this._testIntersection = value ? unionIntersectFunction : defaultIntersectFunction; }, }, /** * If true, clipping will be enabled. * * @memberof ClippingPlaneCollection.prototype * @type {Boolean} * @default true */ enabled: { get: function () { return this._enabled; }, set: function (value) { if (this._enabled === value) { return; } this._enabled = value; }, }, /** * Returns a texture containing packed, untransformed clipping planes. * * @memberof ClippingPlaneCollection.prototype * @type {Texture} * @readonly * @private */ texture: { get: function () { return this._clippingPlanesTexture; }, }, /** * A reference to the ClippingPlaneCollection's owner, if any. * * @memberof ClippingPlaneCollection.prototype * @readonly * @private */ owner: { get: function () { return this._owner; }, }, /** * Returns a Number encapsulating the state for this ClippingPlaneCollection. * * Clipping mode is encoded in the sign of the number, which is just the plane count. * Used for checking if shader regeneration is necessary. * * @memberof ClippingPlaneCollection.prototype * @returns {Number} A Number that describes the ClippingPlaneCollection's state. * @readonly * @private */ clippingPlanesState: { get: function () { return this._unionClippingRegions ? this._planes.length : -this._planes.length; }, }, }); function setIndexDirty(collection, index) { // If there's already a different _dirtyIndex set, more than one plane has changed since update. // Entire texture must be reloaded collection._multipleDirtyPlanes = collection._multipleDirtyPlanes || (collection._dirtyIndex !== -1 && collection._dirtyIndex !== index); collection._dirtyIndex = index; } /** * Adds the specified {@link ClippingPlane} to the collection to be used to selectively disable rendering * on the outside of each plane. Use {@link ClippingPlaneCollection#unionClippingRegions} to modify * how modify the clipping behavior of multiple planes. * * @param {ClippingPlane} plane The ClippingPlane to add to the collection. * * @see ClippingPlaneCollection#unionClippingRegions * @see ClippingPlaneCollection#remove * @see ClippingPlaneCollection#removeAll */ ClippingPlaneCollection.prototype.add = function (plane) { var newPlaneIndex = this._planes.length; var that = this; plane.onChangeCallback = function (index) { setIndexDirty(that, index); }; plane.index = newPlaneIndex; setIndexDirty(this, newPlaneIndex); this._planes.push(plane); this.planeAdded.raiseEvent(plane, newPlaneIndex); }; /** * Returns the plane in the collection at the specified index. Indices are zero-based * and increase as planes are added. Removing a plane shifts all planes after * it to the left, changing their indices. This function is commonly used with * {@link ClippingPlaneCollection#length} to iterate over all the planes * in the collection. * * @param {Number} index The zero-based index of the plane. * @returns {ClippingPlane} The ClippingPlane at the specified index. * * @see ClippingPlaneCollection#length */ ClippingPlaneCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("index", index); //>>includeEnd('debug'); return this._planes[index]; }; function indexOf(planes, plane) { var length = planes.length; for (var i = 0; i < length; ++i) { if (Plane.equals(planes[i], plane)) { return i; } } return -1; } /** * Checks whether this collection contains a ClippingPlane equal to the given ClippingPlane. * * @param {ClippingPlane} [clippingPlane] The ClippingPlane to check for. * @returns {Boolean} true if this collection contains the ClippingPlane, false otherwise. * * @see ClippingPlaneCollection#get */ ClippingPlaneCollection.prototype.contains = function (clippingPlane) { return indexOf(this._planes, clippingPlane) !== -1; }; /** * Removes the first occurrence of the given ClippingPlane from the collection. * * @param {ClippingPlane} clippingPlane * @returns {Boolean} true if the plane was removed; false if the plane was not found in the collection. * * @see ClippingPlaneCollection#add * @see ClippingPlaneCollection#contains * @see ClippingPlaneCollection#removeAll */ ClippingPlaneCollection.prototype.remove = function (clippingPlane) { var planes = this._planes; var index = indexOf(planes, clippingPlane); if (index === -1) { return false; } // Unlink this ClippingPlaneCollection from the ClippingPlane if (clippingPlane instanceof ClippingPlane) { clippingPlane.onChangeCallback = undefined; clippingPlane.index = -1; } // Shift and update indices var length = planes.length - 1; for (var i = index; i < length; ++i) { var planeToKeep = planes[i + 1]; planes[i] = planeToKeep; if (planeToKeep instanceof ClippingPlane) { planeToKeep.index = i; } } // Indicate planes texture is dirty this._multipleDirtyPlanes = true; planes.length = length; this.planeRemoved.raiseEvent(clippingPlane, index); return true; }; /** * Removes all planes from the collection. * * @see ClippingPlaneCollection#add * @see ClippingPlaneCollection#remove */ ClippingPlaneCollection.prototype.removeAll = function () { // Dereference this ClippingPlaneCollection from all ClippingPlanes var planes = this._planes; var planesCount = planes.length; for (var i = 0; i < planesCount; ++i) { var plane = planes[i]; if (plane instanceof ClippingPlane) { plane.onChangeCallback = undefined; plane.index = -1; } this.planeRemoved.raiseEvent(plane, i); } this._multipleDirtyPlanes = true; this._planes = []; }; var distanceEncodeScratch = new Cartesian4(); var oct32EncodeScratch = new Cartesian4(); function packPlanesAsUint8(clippingPlaneCollection, startIndex, endIndex) { var uint8View = clippingPlaneCollection._uint8View; var planes = clippingPlaneCollection._planes; var byteIndex = 0; for (var i = startIndex; i < endIndex; ++i) { var plane = planes[i]; var oct32Normal = AttributeCompression.octEncodeToCartesian4( plane.normal, oct32EncodeScratch ); uint8View[byteIndex] = oct32Normal.x; uint8View[byteIndex + 1] = oct32Normal.y; uint8View[byteIndex + 2] = oct32Normal.z; uint8View[byteIndex + 3] = oct32Normal.w; var encodedDistance = Cartesian4.packFloat( plane.distance, distanceEncodeScratch ); uint8View[byteIndex + 4] = encodedDistance.x; uint8View[byteIndex + 5] = encodedDistance.y; uint8View[byteIndex + 6] = encodedDistance.z; uint8View[byteIndex + 7] = encodedDistance.w; byteIndex += 8; } } // Pack starting at the beginning of the buffer to allow partial update function packPlanesAsFloats(clippingPlaneCollection, startIndex, endIndex) { var float32View = clippingPlaneCollection._float32View; var planes = clippingPlaneCollection._planes; var floatIndex = 0; for (var i = startIndex; i < endIndex; ++i) { var plane = planes[i]; var normal = plane.normal; float32View[floatIndex] = normal.x; float32View[floatIndex + 1] = normal.y; float32View[floatIndex + 2] = normal.z; float32View[floatIndex + 3] = plane.distance; floatIndex += 4; // each plane is 4 floats } } function computeTextureResolution(pixelsNeeded, result) { var maxSize = ContextLimits.maximumTextureSize; result.x = Math.min(pixelsNeeded, maxSize); result.y = Math.ceil(pixelsNeeded / result.x); return result; } var textureResolutionScratch$1 = new Cartesian2(); /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * build the resources for clipping planes. *

* Do not call this function directly. *

*/ ClippingPlaneCollection.prototype.update = function (frameState) { var clippingPlanesTexture = this._clippingPlanesTexture; var context = frameState.context; var useFloatTexture = ClippingPlaneCollection.useFloatTexture(context); // Compute texture requirements for current planes // In RGBA FLOAT, A plane is 4 floats packed to a RGBA. // In RGBA UNSIGNED_BYTE, A plane is a float in [0, 1) packed to RGBA and an Oct32 quantized normal, // so 8 bits or 2 pixels in RGBA. var pixelsNeeded = useFloatTexture ? this.length : this.length * 2; if (defined(clippingPlanesTexture)) { var currentPixelCount = clippingPlanesTexture.width * clippingPlanesTexture.height; // Recreate the texture to double current requirement if it isn't big enough or is 4 times larger than it needs to be. // Optimization note: this isn't exactly the classic resizeable array algorithm // * not necessarily checking for resize after each add/remove operation // * random-access deletes instead of just pops // * alloc ops likely more expensive than demonstrable via big-O analysis if ( currentPixelCount < pixelsNeeded || pixelsNeeded < 0.25 * currentPixelCount ) { clippingPlanesTexture.destroy(); clippingPlanesTexture = undefined; this._clippingPlanesTexture = undefined; } } // If there are no clipping planes, there's nothing to update. if (this.length === 0) { return; } if (!defined(clippingPlanesTexture)) { var requiredResolution = computeTextureResolution( pixelsNeeded, textureResolutionScratch$1 ); // Allocate twice as much space as needed to avoid frequent texture reallocation. // Allocate in the Y direction, since texture may be as wide as context texture support. requiredResolution.y *= 2; if (useFloatTexture) { clippingPlanesTexture = new Texture({ context: context, width: requiredResolution.x, height: requiredResolution.y, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: PixelDatatype$1.FLOAT, sampler: Sampler.NEAREST, flipY: false, }); this._float32View = new Float32Array( requiredResolution.x * requiredResolution.y * 4 ); } else { clippingPlanesTexture = new Texture({ context: context, width: requiredResolution.x, height: requiredResolution.y, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: PixelDatatype$1.UNSIGNED_BYTE, sampler: Sampler.NEAREST, flipY: false, }); this._uint8View = new Uint8Array( requiredResolution.x * requiredResolution.y * 4 ); } this._clippingPlanesTexture = clippingPlanesTexture; this._multipleDirtyPlanes = true; } var dirtyIndex = this._dirtyIndex; if (!this._multipleDirtyPlanes && dirtyIndex === -1) { return; } if (!this._multipleDirtyPlanes) { // partial updates possible var offsetX = 0; var offsetY = 0; if (useFloatTexture) { offsetY = Math.floor(dirtyIndex / clippingPlanesTexture.width); offsetX = Math.floor(dirtyIndex - offsetY * clippingPlanesTexture.width); packPlanesAsFloats(this, dirtyIndex, dirtyIndex + 1); clippingPlanesTexture.copyFrom( { width: 1, height: 1, arrayBufferView: this._float32View, }, offsetX, offsetY ); } else { offsetY = Math.floor((dirtyIndex * 2) / clippingPlanesTexture.width); offsetX = Math.floor( dirtyIndex * 2 - offsetY * clippingPlanesTexture.width ); packPlanesAsUint8(this, dirtyIndex, dirtyIndex + 1); clippingPlanesTexture.copyFrom( { width: 2, height: 1, arrayBufferView: this._uint8View, }, offsetX, offsetY ); } } else if (useFloatTexture) { packPlanesAsFloats(this, 0, this._planes.length); clippingPlanesTexture.copyFrom({ width: clippingPlanesTexture.width, height: clippingPlanesTexture.height, arrayBufferView: this._float32View, }); } else { packPlanesAsUint8(this, 0, this._planes.length); clippingPlanesTexture.copyFrom({ width: clippingPlanesTexture.width, height: clippingPlanesTexture.height, arrayBufferView: this._uint8View, }); } this._multipleDirtyPlanes = false; this._dirtyIndex = -1; }; var scratchMatrix$4 = new Matrix4(); var scratchPlane = new Plane(Cartesian3.UNIT_X, 0.0); /** * Determines the type intersection with the planes of this ClippingPlaneCollection instance and the specified {@link TileBoundingVolume}. * @private * * @param {Object} tileBoundingVolume The volume to determine the intersection with the planes. * @param {Matrix4} [transform] An optional, additional matrix to transform the plane to world coordinates. * @returns {Intersect} {@link Intersect.INSIDE} if the entire volume is on the side of the planes * the normal is pointing and should be entirely rendered, {@link Intersect.OUTSIDE} * if the entire volume is on the opposite side and should be clipped, and * {@link Intersect.INTERSECTING} if the volume intersects the planes. */ ClippingPlaneCollection.prototype.computeIntersectionWithBoundingVolume = function ( tileBoundingVolume, transform ) { var planes = this._planes; var length = planes.length; var modelMatrix = this.modelMatrix; if (defined(transform)) { modelMatrix = Matrix4.multiply(transform, modelMatrix, scratchMatrix$4); } // If the collection is not set to union the clipping regions, the volume must be outside of all planes to be // considered completely clipped. If the collection is set to union the clipping regions, if the volume can be // outside any the planes, it is considered completely clipped. // Lastly, if not completely clipped, if any plane is intersecting, more calculations must be performed. var intersection = Intersect$1.INSIDE; if (!this.unionClippingRegions && length > 0) { intersection = Intersect$1.OUTSIDE; } for (var i = 0; i < length; ++i) { var plane = planes[i]; Plane.transform(plane, modelMatrix, scratchPlane); // ClippingPlane can be used for Plane math var value = tileBoundingVolume.intersectPlane(scratchPlane); if (value === Intersect$1.INTERSECTING) { intersection = value; } else if (this._testIntersection(value)) { return value; } } return intersection; }; /** * Sets the owner for the input ClippingPlaneCollection if there wasn't another owner. * Destroys the owner's previous ClippingPlaneCollection if setting is successful. * * @param {ClippingPlaneCollection} [clippingPlaneCollection] A ClippingPlaneCollection (or undefined) being attached to an object * @param {Object} owner An Object that should receive the new ClippingPlaneCollection * @param {String} key The Key for the Object to reference the ClippingPlaneCollection * @private */ ClippingPlaneCollection.setOwner = function ( clippingPlaneCollection, owner, key ) { // Don't destroy the ClippingPlaneCollection if it is already owned by newOwner if (clippingPlaneCollection === owner[key]) { return; } // Destroy the existing ClippingPlaneCollection, if any owner[key] = owner[key] && owner[key].destroy(); if (defined(clippingPlaneCollection)) { //>>includeStart('debug', pragmas.debug); if (defined(clippingPlaneCollection._owner)) { throw new DeveloperError( "ClippingPlaneCollection should only be assigned to one object" ); } //>>includeEnd('debug'); clippingPlaneCollection._owner = owner; owner[key] = clippingPlaneCollection; } }; /** * Function for checking if the context will allow clipping planes with floating point textures. * * @param {Context} context The Context that will contain clipped objects and clipping textures. * @returns {Boolean} true if floating point textures can be used for clipping planes. * @private */ ClippingPlaneCollection.useFloatTexture = function (context) { return context.floatingPointTexture; }; /** * Function for getting the clipping plane collection's texture resolution. * If the ClippingPlaneCollection hasn't been updated, returns the resolution that will be * allocated based on the current plane count. * * @param {ClippingPlaneCollection} clippingPlaneCollection The clipping plane collection * @param {Context} context The rendering context * @param {Cartesian2} result A Cartesian2 for the result. * @returns {Cartesian2} The required resolution. * @private */ ClippingPlaneCollection.getTextureResolution = function ( clippingPlaneCollection, context, result ) { var texture = clippingPlaneCollection.texture; if (defined(texture)) { result.x = texture.width; result.y = texture.height; return result; } var pixelsNeeded = ClippingPlaneCollection.useFloatTexture(context) ? clippingPlaneCollection.length : clippingPlaneCollection.length * 2; var requiredResolution = computeTextureResolution(pixelsNeeded, result); // Allocate twice as much space as needed to avoid frequent texture reallocation. requiredResolution.y *= 2; return requiredResolution; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see ClippingPlaneCollection#destroy */ ClippingPlaneCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * clippingPlanes = clippingPlanes && clippingPlanes.destroy(); * * @see ClippingPlaneCollection#isDestroyed */ ClippingPlaneCollection.prototype.destroy = function () { this._clippingPlanesTexture = this._clippingPlanesTexture && this._clippingPlanesTexture.destroy(); return destroyObject(this); }; /** * Defines different modes for blending between a target color and a primitive's source color. * * HIGHLIGHT multiplies the source color by the target color * REPLACE replaces the source color with the target color * MIX blends the source color and target color together * * @enum {Number} * * @see Model.colorBlendMode */ var ColorBlendMode = { HIGHLIGHT: 0, REPLACE: 1, MIX: 2, }; /** * @private */ ColorBlendMode.getColorBlend = function (colorBlendMode, colorBlendAmount) { if (colorBlendMode === ColorBlendMode.HIGHLIGHT) { return 0.0; } else if (colorBlendMode === ColorBlendMode.REPLACE) { return 1.0; } else if (colorBlendMode === ColorBlendMode.MIX) { // The value 0.0 is reserved for highlight, so clamp to just above 0.0. return CesiumMath.clamp(colorBlendAmount, CesiumMath.EPSILON4, 1.0); } }; var ColorBlendMode$1 = Object.freeze(ColorBlendMode); /** * @private */ function DracoLoader() {} // Maximum concurrency to use when decoding draco models DracoLoader._maxDecodingConcurrency = Math.max( FeatureDetection.hardwareConcurrency - 1, 1 ); // Exposed for testing purposes DracoLoader._decoderTaskProcessor = undefined; DracoLoader._taskProcessorReady = false; DracoLoader._getDecoderTaskProcessor = function () { if (!defined(DracoLoader._decoderTaskProcessor)) { var processor = new TaskProcessor( "decodeDraco", DracoLoader._maxDecodingConcurrency ); processor .initWebAssemblyModule({ modulePath: "ThirdParty/Workers/draco_wasm_wrapper.js", wasmBinaryFile: "ThirdParty/draco_decoder.wasm", fallbackModulePath: "ThirdParty/Workers/draco_decoder.js", }) .then(function () { DracoLoader._taskProcessorReady = true; }); DracoLoader._decoderTaskProcessor = processor; } return DracoLoader._decoderTaskProcessor; }; /** * Returns true if the model uses or requires KHR_draco_mesh_compression. * * @private */ DracoLoader.hasExtension = function (model) { return ( defined(model.extensionsRequired.KHR_draco_mesh_compression) || defined(model.extensionsUsed.KHR_draco_mesh_compression) ); }; function addBufferToLoadResources(loadResources, typedArray) { // Create a new id to differentiate from original glTF bufferViews var bufferViewId = "runtime." + Object.keys(loadResources.createdBufferViews).length; var loadResourceBuffers = loadResources.buffers; var id = Object.keys(loadResourceBuffers).length; loadResourceBuffers[id] = typedArray; loadResources.createdBufferViews[bufferViewId] = { buffer: id, byteOffset: 0, byteLength: typedArray.byteLength, }; return bufferViewId; } function addNewVertexBuffer(typedArray, model, context) { var loadResources = model._loadResources; var id = addBufferToLoadResources(loadResources, typedArray); loadResources.vertexBuffersToCreate.enqueue(id); return id; } function addNewIndexBuffer(indexArray, model, context) { var typedArray = indexArray.typedArray; var loadResources = model._loadResources; var id = addBufferToLoadResources(loadResources, typedArray); loadResources.indexBuffersToCreate.enqueue({ id: id, componentType: ComponentDatatype$1.fromTypedArray(typedArray), }); return { bufferViewId: id, numberOfIndices: indexArray.numberOfIndices, }; } function scheduleDecodingTask( decoderTaskProcessor, model, loadResources, context ) { if (!DracoLoader._taskProcessorReady) { // The task processor is not ready to schedule tasks return; } var taskData = loadResources.primitivesToDecode.peek(); if (!defined(taskData)) { // All primitives are processing return; } var promise = decoderTaskProcessor.scheduleTask(taskData, [ taskData.array.buffer, ]); if (!defined(promise)) { // Cannot schedule another task this frame return; } loadResources.activeDecodingTasks++; loadResources.primitivesToDecode.dequeue(); return promise.then(function (result) { loadResources.activeDecodingTasks--; var decodedIndexBuffer = addNewIndexBuffer( result.indexArray, model); var attributes = {}; var decodedAttributeData = result.attributeData; for (var attributeName in decodedAttributeData) { if (decodedAttributeData.hasOwnProperty(attributeName)) { var attribute = decodedAttributeData[attributeName]; var vertexArray = attribute.array; var vertexBufferView = addNewVertexBuffer(vertexArray, model); var data = attribute.data; data.bufferView = vertexBufferView; attributes[attributeName] = data; } } model._decodedData[taskData.mesh + ".primitive." + taskData.primitive] = { bufferView: decodedIndexBuffer.bufferViewId, numberOfIndices: decodedIndexBuffer.numberOfIndices, attributes: attributes, }; }); } DracoLoader._decodedModelResourceCache = undefined; /** * Parses draco extension on model primitives and * adds the decoding data to the model's load resources. * * @private */ DracoLoader.parse = function (model, context) { if (!DracoLoader.hasExtension(model)) { return; } var loadResources = model._loadResources; var cacheKey = model.cacheKey; if (defined(cacheKey)) { if (!defined(DracoLoader._decodedModelResourceCache)) { if (!defined(context.cache.modelDecodingCache)) { context.cache.modelDecodingCache = {}; } DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache; } // Decoded data for model will be loaded from cache var cachedData = DracoLoader._decodedModelResourceCache[cacheKey]; if (defined(cachedData)) { cachedData.count++; loadResources.pendingDecodingCache = true; return; } } var dequantizeInShader = model._dequantizeInShader; var gltf = model.gltf; ForEach.mesh(gltf, function (mesh, meshId) { ForEach.meshPrimitive(mesh, function (primitive, primitiveId) { if (!defined(primitive.extensions)) { return; } var compressionData = primitive.extensions.KHR_draco_mesh_compression; if (!defined(compressionData)) { return; } var bufferView = gltf.bufferViews[compressionData.bufferView]; var typedArray = arraySlice( gltf.buffers[bufferView.buffer].extras._pipeline.source, bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength ); loadResources.primitivesToDecode.enqueue({ mesh: meshId, primitive: primitiveId, array: typedArray, bufferView: bufferView, compressedAttributes: compressionData.attributes, dequantizeInShader: dequantizeInShader, }); }); }); }; /** * Schedules decoding tasks available this frame. * @private */ DracoLoader.decodeModel = function (model, context) { if (!DracoLoader.hasExtension(model)) { return when.resolve(); } var loadResources = model._loadResources; var cacheKey = model.cacheKey; if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) { var cachedData = DracoLoader._decodedModelResourceCache[cacheKey]; // Load decoded data for model when cache is ready if (defined(cachedData) && loadResources.pendingDecodingCache) { return when(cachedData.ready, function () { model._decodedData = cachedData.data; loadResources.pendingDecodingCache = false; }); } // Decoded data for model should be cached when ready DracoLoader._decodedModelResourceCache[cacheKey] = { ready: false, count: 1, data: undefined, }; } if (loadResources.primitivesToDecode.length === 0) { // No more tasks to schedule return when.resolve(); } var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); var decodingPromises = []; var promise = scheduleDecodingTask( decoderTaskProcessor, model, loadResources); while (defined(promise)) { decodingPromises.push(promise); promise = scheduleDecodingTask( decoderTaskProcessor, model, loadResources); } return when.all(decodingPromises); }; /** * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled. * @private */ DracoLoader.decodePointCloud = function (parameters) { var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); if (!DracoLoader._taskProcessorReady) { // The task processor is not ready to schedule tasks return; } return decoderTaskProcessor.scheduleTask(parameters, [ parameters.buffer.buffer, ]); }; /** * Caches a models decoded data so it doesn't need to decode more than once. * @private */ DracoLoader.cacheDataForModel = function (model) { var cacheKey = model.cacheKey; if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) { var cachedData = DracoLoader._decodedModelResourceCache[cacheKey]; if (defined(cachedData)) { cachedData.ready = true; cachedData.data = model._decodedData; } } }; /** * Destroys the cached data that this model references if it is no longer in use. * @private */ DracoLoader.destroyCachedDataForModel = function (model) { var cacheKey = model.cacheKey; if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) { var cachedData = DracoLoader._decodedModelResourceCache[cacheKey]; if (defined(cachedData) && --cachedData.count === 0) { delete DracoLoader._decodedModelResourceCache[cacheKey]; } } }; /** * Gets a GLSL snippet that clips a fragment using the `clip` function from {@link getClippingFunction} and styles it. * * @param {String} samplerUniformName Name of the uniform for the clipping planes texture sampler. * @param {String} matrixUniformName Name of the uniform for the clipping planes matrix. * @param {String} styleUniformName Name of the uniform for the clipping planes style, a vec4. * @returns {String} A string containing GLSL that clips and styles the current fragment. * @private */ function getClipAndStyleCode( samplerUniformName, matrixUniformName, styleUniformName ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("samplerUniformName", samplerUniformName); Check.typeOf.string("matrixUniformName", matrixUniformName); Check.typeOf.string("styleUniformName", styleUniformName); //>>includeEnd('debug'); var shaderCode = " float clipDistance = clip(gl_FragCoord, " + samplerUniformName + ", " + matrixUniformName + "); \n" + " vec4 clippingPlanesEdgeColor = vec4(1.0); \n" + " clippingPlanesEdgeColor.rgb = " + styleUniformName + ".rgb; \n" + " float clippingPlanesEdgeWidth = " + styleUniformName + ".a; \n" + " if (clipDistance > 0.0 && clipDistance < clippingPlanesEdgeWidth) \n" + " { \n" + " gl_FragColor = clippingPlanesEdgeColor;\n" + " } \n"; return shaderCode; } var textureResolutionScratch = new Cartesian2(); /** * Gets the GLSL functions needed to retrieve clipping planes from a ClippingPlaneCollection's texture. * * @param {ClippingPlaneCollection} clippingPlaneCollection ClippingPlaneCollection with a defined texture. * @param {Context} context The current rendering context. * @returns {String} A string containing GLSL functions for retrieving clipping planes. * @private */ function getClippingFunction(clippingPlaneCollection, context) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("clippingPlaneCollection", clippingPlaneCollection); Check.typeOf.object("context", context); //>>includeEnd('debug'); var unionClippingRegions = clippingPlaneCollection.unionClippingRegions; var clippingPlanesLength = clippingPlaneCollection.length; var usingFloatTexture = ClippingPlaneCollection.useFloatTexture(context); var textureResolution = ClippingPlaneCollection.getTextureResolution( clippingPlaneCollection, context, textureResolutionScratch ); var width = textureResolution.x; var height = textureResolution.y; var functions = usingFloatTexture ? getClippingPlaneFloat(width, height) : getClippingPlaneUint8(width, height); functions += "\n"; functions += unionClippingRegions ? clippingFunctionUnion(clippingPlanesLength) : clippingFunctionIntersect(clippingPlanesLength); return functions; } function clippingFunctionUnion(clippingPlanesLength) { var functionString = "float clip(vec4 fragCoord, sampler2D clippingPlanes, mat4 clippingPlanesMatrix)\n" + "{\n" + " vec4 position = czm_windowToEyeCoordinates(fragCoord);\n" + " vec3 clipNormal = vec3(0.0);\n" + " vec3 clipPosition = vec3(0.0);\n" + " float clipAmount;\n" + // For union planes, we want to get the min distance. So we set the initial value to the first plane distance in the loop below. " float pixelWidth = czm_metersPerPixel(position);\n" + " bool breakAndDiscard = false;\n" + " for (int i = 0; i < " + clippingPlanesLength + "; ++i)\n" + " {\n" + " vec4 clippingPlane = getClippingPlane(clippingPlanes, i, clippingPlanesMatrix);\n" + " clipNormal = clippingPlane.xyz;\n" + " clipPosition = -clippingPlane.w * clipNormal;\n" + " float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;\n" + " clipAmount = czm_branchFreeTernary(i == 0, amount, min(amount, clipAmount));\n" + " if (amount <= 0.0)\n" + " {\n" + " breakAndDiscard = true;\n" + " break;\n" + // HLSL compiler bug if we discard here: https://bugs.chromium.org/p/angleproject/issues/detail?id=1945#c6 " }\n" + " }\n" + " if (breakAndDiscard) {\n" + " discard;\n" + " }\n" + " return clipAmount;\n" + "}\n"; return functionString; } function clippingFunctionIntersect(clippingPlanesLength) { var functionString = "float clip(vec4 fragCoord, sampler2D clippingPlanes, mat4 clippingPlanesMatrix)\n" + "{\n" + " bool clipped = true;\n" + " vec4 position = czm_windowToEyeCoordinates(fragCoord);\n" + " vec3 clipNormal = vec3(0.0);\n" + " vec3 clipPosition = vec3(0.0);\n" + " float clipAmount = 0.0;\n" + " float pixelWidth = czm_metersPerPixel(position);\n" + " for (int i = 0; i < " + clippingPlanesLength + "; ++i)\n" + " {\n" + " vec4 clippingPlane = getClippingPlane(clippingPlanes, i, clippingPlanesMatrix);\n" + " clipNormal = clippingPlane.xyz;\n" + " clipPosition = -clippingPlane.w * clipNormal;\n" + " float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;\n" + " clipAmount = max(amount, clipAmount);\n" + " clipped = clipped && (amount <= 0.0);\n" + " }\n" + " if (clipped)\n" + " {\n" + " discard;\n" + " }\n" + " return clipAmount;\n" + "}\n"; return functionString; } function getClippingPlaneFloat(width, height) { var pixelWidth = 1.0 / width; var pixelHeight = 1.0 / height; var pixelWidthString = pixelWidth + ""; if (pixelWidthString.indexOf(".") === -1) { pixelWidthString += ".0"; } var pixelHeightString = pixelHeight + ""; if (pixelHeightString.indexOf(".") === -1) { pixelHeightString += ".0"; } var functionString = "vec4 getClippingPlane(highp sampler2D packedClippingPlanes, int clippingPlaneNumber, mat4 transform)\n" + "{\n" + " int pixY = clippingPlaneNumber / " + width + ";\n" + " int pixX = clippingPlaneNumber - (pixY * " + width + ");\n" + " float u = (float(pixX) + 0.5) * " + pixelWidthString + ";\n" + // sample from center of pixel " float v = (float(pixY) + 0.5) * " + pixelHeightString + ";\n" + " vec4 plane = texture2D(packedClippingPlanes, vec2(u, v));\n" + " return czm_transformPlane(plane, transform);\n" + "}\n"; return functionString; } function getClippingPlaneUint8(width, height) { var pixelWidth = 1.0 / width; var pixelHeight = 1.0 / height; var pixelWidthString = pixelWidth + ""; if (pixelWidthString.indexOf(".") === -1) { pixelWidthString += ".0"; } var pixelHeightString = pixelHeight + ""; if (pixelHeightString.indexOf(".") === -1) { pixelHeightString += ".0"; } var functionString = "vec4 getClippingPlane(highp sampler2D packedClippingPlanes, int clippingPlaneNumber, mat4 transform)\n" + "{\n" + " int clippingPlaneStartIndex = clippingPlaneNumber * 2;\n" + // clipping planes are two pixels each " int pixY = clippingPlaneStartIndex / " + width + ";\n" + " int pixX = clippingPlaneStartIndex - (pixY * " + width + ");\n" + " float u = (float(pixX) + 0.5) * " + pixelWidthString + ";\n" + // sample from center of pixel " float v = (float(pixY) + 0.5) * " + pixelHeightString + ";\n" + " vec4 oct32 = texture2D(packedClippingPlanes, vec2(u, v)) * 255.0;\n" + " vec2 oct = vec2(oct32.x * 256.0 + oct32.y, oct32.z * 256.0 + oct32.w);\n" + " vec4 plane;\n" + " plane.xyz = czm_octDecode(oct, 65535.0);\n" + " plane.w = czm_unpackFloat(texture2D(packedClippingPlanes, vec2(u + " + pixelWidthString + ", v)));\n" + " return czm_transformPlane(plane, transform);\n" + "}\n"; return functionString; } /** * @private */ var JobType = { TEXTURE: 0, PROGRAM: 1, BUFFER: 2, NUMBER_OF_JOB_TYPES: 3, }; var JobType$1 = Object.freeze(JobType); /** * @private */ function ModelAnimationCache() {} var dataUriRegex = /^data\:/i; function getAccessorKey(model, accessor) { var gltf = model.gltf; var buffers = gltf.buffers; var bufferViews = gltf.bufferViews; var bufferView = bufferViews[accessor.bufferView]; var buffer = buffers[bufferView.buffer]; var byteOffset = bufferView.byteOffset + accessor.byteOffset; var byteLength = accessor.count * numberOfComponentsForType(accessor.type); var uriKey = dataUriRegex.test(buffer.uri) ? "" : buffer.uri; return model.cacheKey + "//" + uriKey + "/" + byteOffset + "/" + byteLength; } var cachedAnimationParameters = {}; ModelAnimationCache.getAnimationParameterValues = function (model, accessor) { var key = getAccessorKey(model, accessor); var values = cachedAnimationParameters[key]; if (!defined(values)) { // Cache miss var gltf = model.gltf; var buffers = gltf.buffers; var bufferViews = gltf.bufferViews; var bufferView = bufferViews[accessor.bufferView]; var bufferId = bufferView.buffer; var buffer = buffers[bufferId]; var source = buffer.extras._pipeline.source; var componentType = accessor.componentType; var type = accessor.type; var numberOfComponents = numberOfComponentsForType(type); var count = accessor.count; var byteStride = getAccessorByteStride(gltf, accessor); values = new Array(count); var accessorByteOffset = defaultValue(accessor.byteOffset, 0); var byteOffset = bufferView.byteOffset + accessorByteOffset; for (var i = 0; i < count; i++) { var typedArrayView = ComponentDatatype$1.createArrayBufferView( componentType, source.buffer, source.byteOffset + byteOffset, numberOfComponents ); if (type === "SCALAR") { values[i] = typedArrayView[0]; } else if (type === "VEC3") { values[i] = Cartesian3.fromArray(typedArrayView); } else if (type === "VEC4") { values[i] = Quaternion.unpack(typedArrayView); } byteOffset += byteStride; } // GLTF_SPEC: Support more parameter types when glTF supports targeting materials. https://github.com/KhronosGroup/glTF/issues/142 if (defined(model.cacheKey)) { // Only cache when we can create a unique id cachedAnimationParameters[key] = values; } } return values; }; var cachedAnimationSplines = {}; function getAnimationSplineKey(model, animationName, samplerName) { return model.cacheKey + "//" + animationName + "/" + samplerName; } function ConstantSpline(value) { this._value = value; } ConstantSpline.prototype.evaluate = function (time, result) { return this._value; }; ConstantSpline.prototype.wrapTime = function (time) { return 0.0; }; ConstantSpline.prototype.clampTime = function (time) { return 0.0; }; function SteppedSpline(backingSpline) { this._spline = backingSpline; this._lastTimeIndex = 0; } SteppedSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval; SteppedSpline.prototype.evaluate = function (time, result) { var i = (this._lastTimeIndex = this.findTimeInterval( time, this._lastTimeIndex )); var times = this._spline.times; var steppedTime = time >= times[i + 1] ? times[i + 1] : times[i]; return this._spline.evaluate(steppedTime, result); }; Object.defineProperties(SteppedSpline.prototype, { times: { get: function () { return this._spline.times; }, }, }); SteppedSpline.prototype.wrapTime = function (time) { return this._spline.wrapTime(time); }; SteppedSpline.prototype.clampTime = function (time) { return this._spline.clampTime(time); }; ModelAnimationCache.getAnimationSpline = function ( model, animationName, animation, samplerName, sampler, input, path, output ) { var key = getAnimationSplineKey(model, animationName, samplerName); var spline = cachedAnimationSplines[key]; if (!defined(spline)) { var times = input; var controlPoints = output; if (times.length === 1 && controlPoints.length === 1) { spline = new ConstantSpline(controlPoints[0]); } else if ( sampler.interpolation === "LINEAR" || sampler.interpolation === "STEP" ) { if (path === "translation" || path === "scale") { spline = new LinearSpline({ times: times, points: controlPoints, }); } else if (path === "rotation") { spline = new QuaternionSpline({ times: times, points: controlPoints, }); } else if (path === "weights") { spline = new WeightSpline({ times: times, weights: controlPoints, }); } if (defined(spline) && sampler.interpolation === "STEP") { spline = new SteppedSpline(spline); } } if (defined(model.cacheKey)) { // Only cache when we can create a unique id cachedAnimationSplines[key] = spline; } } return spline; }; var cachedSkinInverseBindMatrices = {}; ModelAnimationCache.getSkinInverseBindMatrices = function (model, accessor) { var key = getAccessorKey(model, accessor); var matrices = cachedSkinInverseBindMatrices[key]; if (!defined(matrices)) { // Cache miss var gltf = model.gltf; var buffers = gltf.buffers; var bufferViews = gltf.bufferViews; var bufferViewId = accessor.bufferView; var bufferView = bufferViews[bufferViewId]; var bufferId = bufferView.buffer; var buffer = buffers[bufferId]; var source = buffer.extras._pipeline.source; var componentType = accessor.componentType; var type = accessor.type; var count = accessor.count; var byteStride = getAccessorByteStride(gltf, accessor); var byteOffset = bufferView.byteOffset + accessor.byteOffset; var numberOfComponents = numberOfComponentsForType(type); matrices = new Array(count); if (componentType === WebGLConstants$1.FLOAT && type === AttributeType$1.MAT4) { for (var i = 0; i < count; ++i) { var typedArrayView = ComponentDatatype$1.createArrayBufferView( componentType, source.buffer, source.byteOffset + byteOffset, numberOfComponents ); matrices[i] = Matrix4.fromArray(typedArrayView); byteOffset += byteStride; } } cachedSkinInverseBindMatrices[key] = matrices; } return matrices; }; /** * Determines if and how a glTF animation is looped. * * @enum {Number} * * @see ModelAnimationCollection#add */ var ModelAnimationLoop = { /** * Play the animation once; do not loop it. * * @type {Number} * @constant */ NONE: 0, /** * Loop the animation playing it from the start immediately after it stops. * * @type {Number} * @constant */ REPEAT: 1, /** * Loop the animation. First, playing it forward, then in reverse, then forward, and so on. * * @type {Number} * @constant */ MIRRORED_REPEAT: 2, }; var ModelAnimationLoop$1 = Object.freeze(ModelAnimationLoop); /** * @private */ var ModelAnimationState = Object.freeze({ STOPPED: 0, ANIMATING: 1, }); /** * An active glTF animation. A glTF asset can contain animations. An active animation * is an animation that is currently playing or scheduled to be played because it was * added to a model's {@link ModelAnimationCollection}. An active animation is an * instance of an animation; for example, there can be multiple active animations * for the same glTF animation, each with a different start time. *

* Create this by calling {@link ModelAnimationCollection#add}. *

* * @alias ModelAnimation * @internalConstructor * @class * * @see ModelAnimationCollection#add */ function ModelAnimation(options, model, runtimeAnimation) { this._name = runtimeAnimation.name; this._startTime = JulianDate.clone(options.startTime); this._delay = defaultValue(options.delay, 0.0); // in seconds this._stopTime = options.stopTime; /** * When true, the animation is removed after it stops playing. * This is slightly more efficient that not removing it, but if, for example, * time is reversed, the animation is not played again. * * @type {Boolean} * @default false */ this.removeOnStop = defaultValue(options.removeOnStop, false); this._multiplier = defaultValue(options.multiplier, 1.0); this._reverse = defaultValue(options.reverse, false); this._loop = defaultValue(options.loop, ModelAnimationLoop$1.NONE); /** * The event fired when this animation is started. This can be used, for * example, to play a sound or start a particle system, when the animation starts. *

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * animation.start.addEventListener(function(model, animation) { * console.log('Animation started: ' + animation.name); * }); */ this.start = new Event(); /** * The event fired when on each frame when this animation is updated. The * current time of the animation, relative to the glTF animation time span, is * passed to the event, which allows, for example, starting new animations at a * specific time relative to a playing animation. *

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * animation.update.addEventListener(function(model, animation, time) { * console.log('Animation updated: ' + animation.name + '. glTF animation time: ' + time); * }); */ this.update = new Event(); /** * The event fired when this animation is stopped. This can be used, for * example, to play a sound or start a particle system, when the animation stops. *

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * animation.stop.addEventListener(function(model, animation) { * console.log('Animation stopped: ' + animation.name); * }); */ this.stop = new Event(); this._state = ModelAnimationState.STOPPED; this._runtimeAnimation = runtimeAnimation; // Set during animation update this._computedStartTime = undefined; this._duration = undefined; // To avoid allocations in ModelAnimationCollection.update var that = this; this._raiseStartEvent = function () { that.start.raiseEvent(model, that); }; this._updateEventTime = 0.0; this._raiseUpdateEvent = function () { that.update.raiseEvent(model, that, that._updateEventTime); }; this._raiseStopEvent = function () { that.stop.raiseEvent(model, that); }; } Object.defineProperties(ModelAnimation.prototype, { /** * The glTF animation name that identifies this animation. * * @memberof ModelAnimation.prototype * * @type {String} * @readonly */ name: { get: function () { return this._name; }, }, /** * The scene time to start playing this animation. When this is undefined, * the animation starts at the next frame. * * @memberof ModelAnimation.prototype * * @type {JulianDate} * @readonly * * @default undefined */ startTime: { get: function () { return this._startTime; }, }, /** * The delay, in seconds, from {@link ModelAnimation#startTime} to start playing. * * @memberof ModelAnimation.prototype * * @type {Number} * @readonly * * @default undefined */ delay: { get: function () { return this._delay; }, }, /** * The scene time to stop playing this animation. When this is undefined, * the animation is played for its full duration and perhaps repeated depending on * {@link ModelAnimation#loop}. * * @memberof ModelAnimation.prototype * * @type {JulianDate} * @readonly * * @default undefined */ stopTime: { get: function () { return this._stopTime; }, }, /** * Values greater than 1.0 increase the speed that the animation is played relative * to the scene clock speed; values less than 1.0 decrease the speed. A value of * 1.0 plays the animation at the speed in the glTF animation mapped to the scene * clock speed. For example, if the scene is played at 2x real-time, a two-second glTF animation * will play in one second even if multiplier is 1.0. * * @memberof ModelAnimation.prototype * * @type {Number} * @readonly * * @default 1.0 */ multiplier: { get: function () { return this._multiplier; }, }, /** * When true, the animation is played in reverse. * * @memberof ModelAnimation.prototype * * @type {Boolean} * @readonly * * @default false */ reverse: { get: function () { return this._reverse; }, }, /** * Determines if and how the animation is looped. * * @memberof ModelAnimation.prototype * * @type {ModelAnimationLoop} * @readonly * * @default {@link ModelAnimationLoop.NONE} */ loop: { get: function () { return this._loop; }, }, }); /** * A collection of active model animations. Access this using {@link Model#activeAnimations}. * * @alias ModelAnimationCollection * @internalConstructor * @class * * @see Model#activeAnimations */ function ModelAnimationCollection(model) { /** * The event fired when an animation is added to the collection. This can be used, for * example, to keep a UI in sync. * * @type {Event} * @default new Event() * * @example * model.activeAnimations.animationAdded.addEventListener(function(model, animation) { * console.log('Animation added: ' + animation.name); * }); */ this.animationAdded = new Event(); /** * The event fired when an animation is removed from the collection. This can be used, for * example, to keep a UI in sync. * * @type {Event} * @default new Event() * * @example * model.activeAnimations.animationRemoved.addEventListener(function(model, animation) { * console.log('Animation removed: ' + animation.name); * }); */ this.animationRemoved = new Event(); this._model = model; this._scheduledAnimations = []; this._previousTime = undefined; } Object.defineProperties(ModelAnimationCollection.prototype, { /** * The number of animations in the collection. * * @memberof ModelAnimationCollection.prototype * * @type {Number} * @readonly */ length: { get: function () { return this._scheduledAnimations.length; }, }, }); function add(collection, index, options) { var model = collection._model; var animations = model._runtime.animations; var animation = animations[index]; var scheduledAnimation = new ModelAnimation(options, model, animation); collection._scheduledAnimations.push(scheduledAnimation); collection.animationAdded.raiseEvent(model, scheduledAnimation); return scheduledAnimation; } /** * Creates and adds an animation with the specified initial properties to the collection. *

* This raises the {@link ModelAnimationCollection#animationAdded} event so, for example, a UI can stay in sync. *

* * @param {Object} options Object with the following properties: * @param {String} [options.name] The glTF animation name that identifies the animation. Must be defined if options.index is undefined. * @param {Number} [options.index] The glTF animation index that identifies the animation. Must be defined if options.name is undefined. * @param {JulianDate} [options.startTime] The scene time to start playing the animation. When this is undefined, the animation starts at the next frame. * @param {Number} [options.delay=0.0] The delay, in seconds, from startTime to start playing. * @param {JulianDate} [options.stopTime] The scene time to stop playing the animation. When this is undefined, the animation is played for its full duration. * @param {Boolean} [options.removeOnStop=false] When true, the animation is removed after it stops playing. * @param {Number} [options.multiplier=1.0] Values greater than 1.0 increase the speed that the animation is played relative to the scene clock speed; values less than 1.0 decrease the speed. * @param {Boolean} [options.reverse=false] When true, the animation is played in reverse. * @param {ModelAnimationLoop} [options.loop=ModelAnimationLoop.NONE] Determines if and how the animation is looped. * @returns {ModelAnimation} The animation that was added to the collection. * * @exception {DeveloperError} Animations are not loaded. Wait for the {@link Model#readyPromise} to resolve. * @exception {DeveloperError} options.name must be a valid animation name. * @exception {DeveloperError} options.index must be a valid animation index. * @exception {DeveloperError} Either options.name or options.index must be defined. * @exception {DeveloperError} options.multiplier must be greater than zero. * * @example * // Example 1. Add an animation by name * model.activeAnimations.add({ * name : 'animation name' * }); * * // Example 2. Add an animation by index * model.activeAnimations.add({ * index : 0 * }); * * @example * // Example 3. Add an animation and provide all properties and events * var startTime = Cesium.JulianDate.now(); * * var animation = model.activeAnimations.add({ * name : 'another animation name', * startTime : startTime, * delay : 0.0, // Play at startTime (default) * stopTime : Cesium.JulianDate.addSeconds(startTime, 4.0, new Cesium.JulianDate()), * removeOnStop : false, // Do not remove when animation stops (default) * multiplier : 2.0, // Play at double speed * reverse : true, // Play in reverse * loop : Cesium.ModelAnimationLoop.REPEAT // Loop the animation * }); * * animation.start.addEventListener(function(model, animation) { * console.log('Animation started: ' + animation.name); * }); * animation.update.addEventListener(function(model, animation, time) { * console.log('Animation updated: ' + animation.name + '. glTF animation time: ' + time); * }); * animation.stop.addEventListener(function(model, animation) { * console.log('Animation stopped: ' + animation.name); * }); */ ModelAnimationCollection.prototype.add = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var model = this._model; var animations = model._runtime.animations; //>>includeStart('debug', pragmas.debug); if (!defined(animations)) { throw new DeveloperError( "Animations are not loaded. Wait for Model.readyPromise to resolve." ); } if (!defined(options.name) && !defined(options.index)) { throw new DeveloperError( "Either options.name or options.index must be defined." ); } if (defined(options.multiplier) && options.multiplier <= 0.0) { throw new DeveloperError("options.multiplier must be greater than zero."); } if ( defined(options.index) && (options.index >= animations.length || options.index < 0) ) { throw new DeveloperError("options.index must be a valid animation index."); } //>>includeEnd('debug'); if (defined(options.index)) { return add(this, options.index, options); } // Find the index of the animation with the given name var index; var length = animations.length; for (var i = 0; i < length; ++i) { if (animations[i].name === options.name) { index = i; break; } } //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("options.name must be a valid animation name."); } //>>includeEnd('debug'); return add(this, index, options); }; /** * Creates and adds an animation with the specified initial properties to the collection * for each animation in the model. *

* This raises the {@link ModelAnimationCollection#animationAdded} event for each model so, for example, a UI can stay in sync. *

* * @param {Object} [options] Object with the following properties: * @param {JulianDate} [options.startTime] The scene time to start playing the animations. When this is undefined, the animations starts at the next frame. * @param {Number} [options.delay=0.0] The delay, in seconds, from startTime to start playing. * @param {JulianDate} [options.stopTime] The scene time to stop playing the animations. When this is undefined, the animations are played for its full duration. * @param {Boolean} [options.removeOnStop=false] When true, the animations are removed after they stop playing. * @param {Number} [options.multiplier=1.0] Values greater than 1.0 increase the speed that the animations play relative to the scene clock speed; values less than 1.0 decrease the speed. * @param {Boolean} [options.reverse=false] When true, the animations are played in reverse. * @param {ModelAnimationLoop} [options.loop=ModelAnimationLoop.NONE] Determines if and how the animations are looped. * @returns {ModelAnimation[]} An array of {@link ModelAnimation} objects, one for each animation added to the collection. If there are no glTF animations, the array is empty. * * @exception {DeveloperError} Animations are not loaded. Wait for the {@link Model#readyPromise} to resolve. * @exception {DeveloperError} options.multiplier must be greater than zero. * * @example * model.activeAnimations.addAll({ * multiplier : 0.5, // Play at half-speed * loop : Cesium.ModelAnimationLoop.REPEAT // Loop the animations * }); */ ModelAnimationCollection.prototype.addAll = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!defined(this._model._runtime.animations)) { throw new DeveloperError( "Animations are not loaded. Wait for Model.readyPromise to resolve." ); } if (defined(options.multiplier) && options.multiplier <= 0.0) { throw new DeveloperError("options.multiplier must be greater than zero."); } //>>includeEnd('debug'); var scheduledAnimations = []; var model = this._model; var animations = model._runtime.animations; var length = animations.length; for (var i = 0; i < length; ++i) { scheduledAnimations.push(add(this, i, options)); } return scheduledAnimations; }; /** * Removes an animation from the collection. *

* This raises the {@link ModelAnimationCollection#animationRemoved} event so, for example, a UI can stay in sync. *

*

* An animation can also be implicitly removed from the collection by setting {@link ModelAnimation#removeOnStop} to * true. The {@link ModelAnimationCollection#animationRemoved} event is still fired when the animation is removed. *

* * @param {ModelAnimation} animation The animation to remove. * @returns {Boolean} true if the animation was removed; false if the animation was not found in the collection. * * @example * var a = model.activeAnimations.add({ * name : 'animation name' * }); * model.activeAnimations.remove(a); // Returns true */ ModelAnimationCollection.prototype.remove = function (animation) { if (defined(animation)) { var animations = this._scheduledAnimations; var i = animations.indexOf(animation); if (i !== -1) { animations.splice(i, 1); this.animationRemoved.raiseEvent(this._model, animation); return true; } } return false; }; /** * Removes all animations from the collection. *

* This raises the {@link ModelAnimationCollection#animationRemoved} event for each * animation so, for example, a UI can stay in sync. *

*/ ModelAnimationCollection.prototype.removeAll = function () { var model = this._model; var animations = this._scheduledAnimations; var length = animations.length; this._scheduledAnimations = []; for (var i = 0; i < length; ++i) { this.animationRemoved.raiseEvent(model, animations[i]); } }; /** * Determines whether this collection contains a given animation. * * @param {ModelAnimation} animation The animation to check for. * @returns {Boolean} true if this collection contains the animation, false otherwise. */ ModelAnimationCollection.prototype.contains = function (animation) { if (defined(animation)) { return this._scheduledAnimations.indexOf(animation) !== -1; } return false; }; /** * Returns the animation in the collection at the specified index. Indices are zero-based * and increase as animations are added. Removing an animation shifts all animations after * it to the left, changing their indices. This function is commonly used to iterate over * all the animations in the collection. * * @param {Number} index The zero-based index of the animation. * @returns {ModelAnimation} The animation at the specified index. * * @example * // Output the names of all the animations in the collection. * var animations = model.activeAnimations; * var length = animations.length; * for (var i = 0; i < length; ++i) { * console.log(animations.get(i).name); * } */ ModelAnimationCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); return this._scheduledAnimations[index]; }; function animateChannels(runtimeAnimation, localAnimationTime) { var channelEvaluators = runtimeAnimation.channelEvaluators; var length = channelEvaluators.length; for (var i = 0; i < length; ++i) { channelEvaluators[i](localAnimationTime); } } var animationsToRemove = []; function createAnimationRemovedFunction( modelAnimationCollection, model, animation ) { return function () { modelAnimationCollection.animationRemoved.raiseEvent(model, animation); }; } /** * @private */ ModelAnimationCollection.prototype.update = function (frameState) { var scheduledAnimations = this._scheduledAnimations; var length = scheduledAnimations.length; if (length === 0) { // No animations - quick return for performance this._previousTime = undefined; return false; } if (JulianDate.equals(frameState.time, this._previousTime)) { // Animations are currently only time-dependent so do not animate when paused or picking return false; } this._previousTime = JulianDate.clone(frameState.time, this._previousTime); var animationOccured = false; var sceneTime = frameState.time; var model = this._model; for (var i = 0; i < length; ++i) { var scheduledAnimation = scheduledAnimations[i]; var runtimeAnimation = scheduledAnimation._runtimeAnimation; if (!defined(scheduledAnimation._computedStartTime)) { scheduledAnimation._computedStartTime = JulianDate.addSeconds( defaultValue(scheduledAnimation.startTime, sceneTime), scheduledAnimation.delay, new JulianDate() ); } if (!defined(scheduledAnimation._duration)) { scheduledAnimation._duration = runtimeAnimation.stopTime * (1.0 / scheduledAnimation.multiplier); } var startTime = scheduledAnimation._computedStartTime; var duration = scheduledAnimation._duration; var stopTime = scheduledAnimation.stopTime; // [0.0, 1.0] normalized local animation time var delta = duration !== 0.0 ? JulianDate.secondsDifference(sceneTime, startTime) / duration : 0.0; // Clamp delta to stop time, if defined. if ( duration !== 0.0 && defined(stopTime) && JulianDate.greaterThan(sceneTime, stopTime) ) { delta = JulianDate.secondsDifference(stopTime, startTime) / duration; } var pastStartTime = delta >= 0.0; // Play animation if // * we are after the start time or the animation is being repeated, and // * before the end of the animation's duration or the animation is being repeated, and // * we did not reach a user-provided stop time. var repeat = scheduledAnimation.loop === ModelAnimationLoop$1.REPEAT || scheduledAnimation.loop === ModelAnimationLoop$1.MIRRORED_REPEAT; var play = (pastStartTime || (repeat && !defined(scheduledAnimation.startTime))) && (delta <= 1.0 || repeat) && (!defined(stopTime) || JulianDate.lessThanOrEquals(sceneTime, stopTime)); // If it IS, or WAS, animating... if (play || scheduledAnimation._state === ModelAnimationState.ANIMATING) { // STOPPED -> ANIMATING state transition? if (play && scheduledAnimation._state === ModelAnimationState.STOPPED) { scheduledAnimation._state = ModelAnimationState.ANIMATING; if (scheduledAnimation.start.numberOfListeners > 0) { frameState.afterRender.push(scheduledAnimation._raiseStartEvent); } } // Truncate to [0.0, 1.0] for repeating animations if (scheduledAnimation.loop === ModelAnimationLoop$1.REPEAT) { delta = delta - Math.floor(delta); } else if ( scheduledAnimation.loop === ModelAnimationLoop$1.MIRRORED_REPEAT ) { var floor = Math.floor(delta); var fract = delta - floor; // When even use (1.0 - fract) to mirror repeat delta = floor % 2 === 1.0 ? 1.0 - fract : fract; } if (scheduledAnimation.reverse) { delta = 1.0 - delta; } var localAnimationTime = delta * duration * scheduledAnimation.multiplier; // Clamp in case floating-point roundoff goes outside the animation's first or last keyframe localAnimationTime = CesiumMath.clamp( localAnimationTime, runtimeAnimation.startTime, runtimeAnimation.stopTime ); animateChannels(runtimeAnimation, localAnimationTime); if (scheduledAnimation.update.numberOfListeners > 0) { scheduledAnimation._updateEventTime = localAnimationTime; frameState.afterRender.push(scheduledAnimation._raiseUpdateEvent); } animationOccured = true; if (!play) { // ANIMATING -> STOPPED state transition? scheduledAnimation._state = ModelAnimationState.STOPPED; if (scheduledAnimation.stop.numberOfListeners > 0) { frameState.afterRender.push(scheduledAnimation._raiseStopEvent); } if (scheduledAnimation.removeOnStop) { animationsToRemove.push(scheduledAnimation); } } } } // Remove animations that stopped length = animationsToRemove.length; for (var j = 0; j < length; ++j) { var animationToRemove = animationsToRemove[j]; scheduledAnimations.splice( scheduledAnimations.indexOf(animationToRemove), 1 ); frameState.afterRender.push( createAnimationRemovedFunction(this, model, animationToRemove) ); } animationsToRemove.length = 0; return animationOccured; }; /** * A model's material with modifiable parameters. A glTF material * contains parameters defined by the material's technique with values * defined by the technique and potentially overridden by the material. * This class allows changing these values at runtime. *

* Use {@link Model#getMaterial} to create an instance. *

* * @alias ModelMaterial * @internalConstructor * @class * * @see Model#getMaterial */ function ModelMaterial(model, material, id) { this._name = material.name; this._id = id; this._uniformMap = model._uniformMaps[id]; this._technique = undefined; this._program = undefined; this._values = undefined; } Object.defineProperties(ModelMaterial.prototype, { /** * The value of the name property of this material. * * @memberof ModelMaterial.prototype * * @type {String} * @readonly */ name: { get: function () { return this._name; }, }, /** * The index of the material. * * @memberof ModelMaterial.prototype * * @type {String} * @readonly */ id: { get: function () { return this._id; }, }, }); /** * Assigns a value to a material parameter. The type for value * depends on the glTF type of the parameter. It will be a floating-point * number, Cartesian, or matrix. * * @param {String} name The name of the parameter. * @param {*} [value] The value to assign to the parameter. * * @exception {DeveloperError} name must match a parameter name in the material's technique that is targetable and not optimized out. * * @example * material.setValue('diffuse', new Cesium.Cartesian4(1.0, 0.0, 0.0, 1.0)); // vec4 * material.setValue('shininess', 256.0); // scalar */ ModelMaterial.prototype.setValue = function (name, value) { //>>includeStart('debug', pragmas.debug); if (!defined(name)) { throw new DeveloperError("name is required."); } //>>includeEnd('debug'); var uniformName = "u_" + name; var v = this._uniformMap.values[uniformName]; //>>includeStart('debug', pragmas.debug); if (!defined(v)) { throw new DeveloperError( "name must match a parameter name in the material's technique that is targetable and not optimized out." ); } //>>includeEnd('debug'); v.value = v.clone(value, v.value); }; /** * Returns the value of the parameter with the given name. The type of the * returned object depends on the glTF type of the parameter. It will be a floating-point * number, Cartesian, or matrix. * * @param {String} name The name of the parameter. * @returns {*} The value of the parameter or undefined if the parameter does not exist. */ ModelMaterial.prototype.getValue = function (name) { //>>includeStart('debug', pragmas.debug); if (!defined(name)) { throw new DeveloperError("name is required."); } //>>includeEnd('debug'); var uniformName = "u_" + name; var v = this._uniformMap.values[uniformName]; if (!defined(v)) { return undefined; } return v.value; }; /** * A model's mesh and its materials. *

* Use {@link Model#getMesh} to create an instance. *

* * @alias ModelMesh * @internalConstructor * @class * * @see Model#getMesh */ function ModelMesh(mesh, runtimeMaterialsById, id) { var materials = []; var primitives = mesh.primitives; var length = primitives.length; for (var i = 0; i < length; ++i) { var p = primitives[i]; materials[i] = runtimeMaterialsById[p.material]; } this._name = mesh.name; this._materials = materials; this._id = id; } Object.defineProperties(ModelMesh.prototype, { /** * The value of the name property of this mesh. * * @memberof ModelMesh.prototype * * @type {String} * @readonly */ name: { get: function () { return this._name; }, }, /** * The index of the mesh. * * @memberof ModelMesh.prototype * * @type {String} * @readonly */ id: { get: function () { return this._id; }, }, /** * An array of {@link ModelMaterial} instances indexed by the mesh's * primitive indices. * * @memberof ModelMesh.prototype * * @type {ModelMaterial[]} * @readonly */ materials: { get: function () { return this._materials; }, }, }); /** * A model node with a transform for user-defined animations. A glTF asset can * contain animations that target a node's transform. This class allows * changing a node's transform externally so animation can be driven by another * source, not just an animation in the glTF asset. *

* Use {@link Model#getNode} to create an instance. *

* * @alias ModelNode * @internalConstructor * @class * * @example * var node = model.getNode('LOD3sp'); * node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix); * * @see Model#getNode */ function ModelNode(model, node, runtimeNode, id, matrix) { this._model = model; this._runtimeNode = runtimeNode; this._name = node.name; this._id = id; /** * @private */ this.useMatrix = false; this._show = true; this._matrix = Matrix4.clone(matrix); this._originalMatrix = Matrix4.clone(matrix); } Object.defineProperties(ModelNode.prototype, { /** * The value of the name property of this node. * * @memberof ModelNode.prototype * * @type {String} * @readonly */ name: { get: function () { return this._name; }, }, /** * The index of the node. * * @memberof ModelNode.prototype * * @type {String} * @readonly */ id: { get: function () { return this._id; }, }, /** * Determines if this node and its children will be shown. * * @memberof ModelNode.prototype * @type {Boolean} * * @default true */ show: { get: function () { return this._show; }, set: function (value) { if (this._show !== value) { this._show = value; this._model._perNodeShowDirty = true; } }, }, /** * The node's 4x4 matrix transform from its local coordinates to * its parent's. *

* For changes to take effect, this property must be assigned to; * setting individual elements of the matrix will not work. *

* * @memberof ModelNode.prototype * @type {Matrix4} */ matrix: { get: function () { return this._matrix; }, set: function (value) { this._matrix = Matrix4.clone(value, this._matrix); this.useMatrix = true; var model = this._model; model._cesiumAnimationsDirty = true; this._runtimeNode.dirtyNumber = model._maxDirtyNumber; }, }, /** * Gets the node's original 4x4 matrix transform from its local coordinates to * its parent's, without any node transformations or articulations applied. * * @memberof ModelNode.prototype * @type {Matrix4} */ originalMatrix: { get: function () { return this._originalMatrix; }, }, }); /** * @private */ ModelNode.prototype.setMatrix = function (matrix) { // Update matrix but do not set the dirty flag since this is used internally // to keep the matrix in-sync during a glTF animation. Matrix4.clone(matrix, this._matrix); }; // glTF does not allow an index value of 65535 because this is the primitive // restart value in some APIs. var MAX_GLTF_UINT16_INDEX = 65534; /** * Creates face outlines for glTF primitives with the `CESIUM_primitive_outline` extension. * @private */ function ModelOutlineLoader() {} /** * Returns true if the model uses or requires CESIUM_primitive_outline. * @private */ ModelOutlineLoader.hasExtension = function (model) { return ( defined(model.extensionsRequired.CESIUM_primitive_outline) || defined(model.extensionsUsed.CESIUM_primitive_outline) ); }; /** * Arranges to outline any primitives with the CESIUM_primitive_outline extension. * It is expected that all buffer data is loaded and available in * `extras._pipeline.source` before this function is called, and that vertex * and index WebGL buffers are not yet created. * @private */ ModelOutlineLoader.outlinePrimitives = function (model) { if (!ModelOutlineLoader.hasExtension(model)) { return; } var gltf = model.gltf; // Assumption: A single bufferView contains a single zero-indexed range of vertices. // No trickery with using large accessor byteOffsets to store multiple zero-based // ranges of vertices in a single bufferView. Use separate bufferViews for that, // you monster. // Note that interleaved vertex attributes (e.g. position0, normal0, uv0, // position1, normal1, uv1, ...) _are_ supported and should not be confused with // the above. var vertexNumberingScopes = []; ForEach.mesh(gltf, function (mesh, meshId) { ForEach.meshPrimitive(mesh, function (primitive, primitiveId) { if (!defined(primitive.extensions)) { return; } var outlineData = primitive.extensions.CESIUM_primitive_outline; if (!defined(outlineData)) { return; } var vertexNumberingScope = getVertexNumberingScope(model, primitive); if (vertexNumberingScope === undefined) { return; } if (vertexNumberingScopes.indexOf(vertexNumberingScope) < 0) { vertexNumberingScopes.push(vertexNumberingScope); } // Add the outline to this primitive addOutline( model, meshId, primitiveId, outlineData.indices, vertexNumberingScope ); }); }); // Update all relevant bufferViews to include the duplicate vertices that are // needed for outlining. for (var i = 0; i < vertexNumberingScopes.length; ++i) { updateBufferViewsWithNewVertices( model, vertexNumberingScopes[i].bufferViews ); } // Remove data not referenced by any bufferViews anymore. compactBuffers(model); }; ModelOutlineLoader.createTexture = function (model, context) { var cache = context.cache.modelOutliningCache; if (!defined(cache)) { cache = context.cache.modelOutliningCache = {}; } if (defined(cache.outlineTexture)) { return cache.outlineTexture; } var maxSize = Math.min(4096, ContextLimits.maximumTextureSize); var size = maxSize; var levelZero = createTexture$1(size); var mipLevels = []; while (size > 1) { size >>= 1; mipLevels.push(createTexture$1(size)); } var texture = new Texture({ context: context, source: { arrayBufferView: levelZero, mipLevels: mipLevels, }, width: maxSize, height: 1, pixelFormat: PixelFormat$1.LUMINANCE, sampler: new Sampler({ wrapS: TextureWrap$1.CLAMP_TO_EDGE, wrapT: TextureWrap$1.CLAMP_TO_EDGE, minificationFilter: TextureMinificationFilter$1.LINEAR_MIPMAP_LINEAR, magnificationFilter: TextureMagnificationFilter$1.LINEAR, }), }); cache.outlineTexture = texture; return texture; }; function addOutline( model, meshId, primitiveId, edgeIndicesAccessorId, vertexNumberingScope ) { var vertexCopies = vertexNumberingScope.vertexCopies; var extraVertices = vertexNumberingScope.extraVertices; var outlineCoordinates = vertexNumberingScope.outlineCoordinates; var gltf = model.gltf; var mesh = gltf.meshes[meshId]; var primitive = mesh.primitives[primitiveId]; var accessors = gltf.accessors; var bufferViews = gltf.bufferViews; // Find the number of vertices in this primitive by looking at // the first attribute. Others are required to be the same. var numVertices; for (var semantic in primitive.attributes) { if (primitive.attributes.hasOwnProperty(semantic)) { var attributeId = primitive.attributes[semantic]; var accessor = accessors[attributeId]; if (defined(accessor)) { numVertices = accessor.count; break; } } } if (!defined(numVertices)) { return undefined; } var triangleIndexAccessorGltf = accessors[primitive.indices]; var triangleIndexBufferViewGltf = bufferViews[triangleIndexAccessorGltf.bufferView]; var edgeIndexAccessorGltf = accessors[edgeIndicesAccessorId]; var edgeIndexBufferViewGltf = bufferViews[edgeIndexAccessorGltf.bufferView]; var loadResources = model._loadResources; var triangleIndexBufferView = loadResources.getBuffer( triangleIndexBufferViewGltf ); var edgeIndexBufferView = loadResources.getBuffer(edgeIndexBufferViewGltf); var triangleIndices = triangleIndexAccessorGltf.componentType === 5123 ? new Uint16Array( triangleIndexBufferView.buffer, triangleIndexBufferView.byteOffset + triangleIndexAccessorGltf.byteOffset, triangleIndexAccessorGltf.count ) : new Uint32Array( triangleIndexBufferView.buffer, triangleIndexBufferView.byteOffset + triangleIndexAccessorGltf.byteOffset, triangleIndexAccessorGltf.count ); var edgeIndices = edgeIndexAccessorGltf.componentType === 5123 ? new Uint16Array( edgeIndexBufferView.buffer, edgeIndexBufferView.byteOffset + edgeIndexAccessorGltf.byteOffset, edgeIndexAccessorGltf.count ) : new Uint32Array( edgeIndexBufferView.buffer, edgeIndexBufferView.byteOffset + edgeIndexAccessorGltf.byteOffset, edgeIndexAccessorGltf.count ); // Make a hash table for quick lookups of whether an edge exists between two // vertices. The hash is a sparse array indexed by // `smallerVertexIndex * totalNumberOfVertices + biggerVertexIndex` // A value of 1 indicates an edge exists between the two vertex indices; any // other value indicates that it does not. We store the // `edgeSmallMultipler` - that is, the number of vertices in the equation // above - at index 0 for easy access to it later. var edgeSmallMultiplier = numVertices; var edges = [edgeSmallMultiplier]; var i; for (i = 0; i < edgeIndices.length; i += 2) { var a = edgeIndices[i]; var b = edgeIndices[i + 1]; var small = Math.min(a, b); var big = Math.max(a, b); edges[small * edgeSmallMultiplier + big] = 1; } // For each triangle, adjust vertex data so that the correct edges are outlined. for (i = 0; i < triangleIndices.length; i += 3) { var i0 = triangleIndices[i]; var i1 = triangleIndices[i + 1]; var i2 = triangleIndices[i + 2]; var all = false; // set this to true to draw a full wireframe. var has01 = all || isHighlighted(edges, i0, i1); var has12 = all || isHighlighted(edges, i1, i2); var has20 = all || isHighlighted(edges, i2, i0); var unmatchableVertexIndex = matchAndStoreCoordinates( outlineCoordinates, i0, i1, i2, has01, has12, has20 ); while (unmatchableVertexIndex >= 0) { // Copy the unmatchable index and try again. var copy; if (unmatchableVertexIndex === i0) { copy = vertexCopies[i0]; } else if (unmatchableVertexIndex === i1) { copy = vertexCopies[i1]; } else { copy = vertexCopies[i2]; } if (copy === undefined) { copy = numVertices + extraVertices.length; var original = unmatchableVertexIndex; while (original >= numVertices) { original = extraVertices[original - numVertices]; } extraVertices.push(original); vertexCopies[unmatchableVertexIndex] = copy; } if ( copy > MAX_GLTF_UINT16_INDEX && triangleIndices instanceof Uint16Array ) { // We outgrew a 16-bit index buffer, switch to 32-bit. triangleIndices = new Uint32Array(triangleIndices); triangleIndexAccessorGltf.componentType = 5125; // UNSIGNED_INT triangleIndexBufferViewGltf.buffer = gltf.buffers.push({ byteLength: triangleIndices.byteLength, extras: { _pipeline: { source: triangleIndices.buffer, }, }, }) - 1; triangleIndexBufferViewGltf.byteLength = triangleIndices.byteLength; triangleIndexBufferViewGltf.byteOffset = 0; model._loadResources.buffers[ triangleIndexBufferViewGltf.buffer ] = new Uint8Array( triangleIndices.buffer, 0, triangleIndices.byteLength ); // The index componentType is also squirreled away in ModelLoadResources. // Hackily update it, or else we'll end up creating the wrong type // of index buffer later. loadResources.indexBuffersToCreate._array.forEach(function (toCreate) { if (toCreate.id === triangleIndexAccessorGltf.bufferView) { toCreate.componentType = triangleIndexAccessorGltf.componentType; } }); } if (unmatchableVertexIndex === i0) { i0 = copy; triangleIndices[i] = copy; } else if (unmatchableVertexIndex === i1) { i1 = copy; triangleIndices[i + 1] = copy; } else { i2 = copy; triangleIndices[i + 2] = copy; } if (defined(triangleIndexAccessorGltf.max)) { triangleIndexAccessorGltf.max[0] = Math.max( triangleIndexAccessorGltf.max[0], copy ); } unmatchableVertexIndex = matchAndStoreCoordinates( outlineCoordinates, i0, i1, i2, has01, has12, has20 ); } } } // Each vertex has three coordinates, a, b, and c. // a is the coordinate that applies to edge 2-0 for the vertex. // b is the coordinate that applies to edge 0-1 for the vertex. // c is the coordinate that applies to edge 1-2 for the vertex. // A single triangle with all edges highlighted: // // | a | b | c | // | 1 | 1 | 0 | // 0 // / \ // / \ // edge 0-1 / \ edge 2-0 // / \ // / \ // | a | b | c | 1-----------2 | a | b | c | // | 0 | 1 | 1 | edge 1-2 | 1 | 0 | 1 | // // There are 6 possible orderings of coordinates a, b, and c: // 0 - abc // 1 - acb // 2 - bac // 3 - bca // 4 - cab // 5 - cba // All vertices must use the _same ordering_ for the edges to be rendered // correctly. So we compute a bitmask for each vertex, where the bit at // each position indicates whether that ordering works (i.e. doesn't // conflict with already-assigned coordinates) for that vertex. // Then we can find an ordering that works for all three vertices with a // bitwise AND. function computeOrderMask(outlineCoordinates, vertexIndex, a, b, c) { var startIndex = vertexIndex * 3; var first = outlineCoordinates[startIndex]; var second = outlineCoordinates[startIndex + 1]; var third = outlineCoordinates[startIndex + 2]; if (first === undefined) { // If one coordinate is undefined, they all are, and all orderings are fine. return 63; // 0b111111; } return ( ((first === a && second === b && third === c) << 0) + ((first === a && second === c && third === b) << 1) + ((first === b && second === a && third === c) << 2) + ((first === b && second === c && third === a) << 3) + ((first === c && second === a && third === b) << 4) + ((first === c && second === b && third === a) << 5) ); } // popcount for integers 0-63, inclusive. // i.e. how many 1s are in the binary representation of the integer. function popcount0to63(value) { return ( (value & 1) + ((value >> 1) & 1) + ((value >> 2) & 1) + ((value >> 3) & 1) + ((value >> 4) & 1) + ((value >> 5) & 1) ); } function matchAndStoreCoordinates( outlineCoordinates, i0, i1, i2, has01, has12, has20 ) { var a0 = has20 ? 1.0 : 0.0; var b0 = has01 ? 1.0 : 0.0; var c0 = 0.0; var i0Mask = computeOrderMask(outlineCoordinates, i0, a0, b0, c0); if (i0Mask === 0) { return i0; } var a1 = 0.0; var b1 = has01 ? 1.0 : 0.0; var c1 = has12 ? 1.0 : 0.0; var i1Mask = computeOrderMask(outlineCoordinates, i1, a1, b1, c1); if (i1Mask === 0) { return i1; } var a2 = has20 ? 1.0 : 0.0; var b2 = 0.0; var c2 = has12 ? 1.0 : 0.0; var i2Mask = computeOrderMask(outlineCoordinates, i2, a2, b2, c2); if (i2Mask === 0) { return i2; } var workingOrders = i0Mask & i1Mask & i2Mask; var a, b, c; if (workingOrders & (1 << 0)) { // 0 - abc a = 0; b = 1; c = 2; } else if (workingOrders & (1 << 1)) { // 1 - acb a = 0; c = 1; b = 2; } else if (workingOrders & (1 << 2)) { // 2 - bac b = 0; a = 1; c = 2; } else if (workingOrders & (1 << 3)) { // 3 - bca b = 0; c = 1; a = 2; } else if (workingOrders & (1 << 4)) { // 4 - cab c = 0; a = 1; b = 2; } else if (workingOrders & (1 << 5)) { // 5 - cba c = 0; b = 1; a = 2; } else { // No ordering works. // Report the most constrained vertex as unmatched so we copy that one. var i0Popcount = popcount0to63(i0Mask); var i1Popcount = popcount0to63(i1Mask); var i2Popcount = popcount0to63(i2Mask); if (i0Popcount < i1Popcount && i0Popcount < i2Popcount) { return i0; } else if (i1Popcount < i2Popcount) { return i1; } return i2; } var i0Start = i0 * 3; outlineCoordinates[i0Start + a] = a0; outlineCoordinates[i0Start + b] = b0; outlineCoordinates[i0Start + c] = c0; var i1Start = i1 * 3; outlineCoordinates[i1Start + a] = a1; outlineCoordinates[i1Start + b] = b1; outlineCoordinates[i1Start + c] = c1; var i2Start = i2 * 3; outlineCoordinates[i2Start + a] = a2; outlineCoordinates[i2Start + b] = b2; outlineCoordinates[i2Start + c] = c2; return -1; } function isHighlighted(edges, i0, i1) { var edgeSmallMultiplier = edges[0]; var index = Math.min(i0, i1) * edgeSmallMultiplier + Math.max(i0, i1); // If i0 and i1 are both 0, then our index will be 0 and we'll end up // accessing the edgeSmallMultiplier that we've sneakily squirreled away // in index 0. But it makes no sense to have an edge between vertex 0 and // itself, so for any edgeSmallMultiplier other than 1 we'll return the // correct answer: false. If edgeSmallMultiplier is 1, that means there is // only a single vertex, so no danger of forming a meaningful triangle // with that. return edges[index] === 1; } function createTexture$1(size) { var texture = new Uint8Array(size); texture[size - 1] = 192; if (size === 8) { texture[size - 1] = 96; } else if (size === 4) { texture[size - 1] = 48; } else if (size === 2) { texture[size - 1] = 24; } else if (size === 1) { texture[size - 1] = 12; } return texture; } function updateBufferViewsWithNewVertices(model, bufferViews) { var gltf = model.gltf; var loadResources = model._loadResources; var i, j; for (i = 0; i < bufferViews.length; ++i) { var bufferView = bufferViews[i]; var vertexNumberingScope = bufferView.extras._pipeline.vertexNumberingScope; // Let the temporary data be garbage collected. bufferView.extras._pipeline.vertexNumberingScope = undefined; var newVertices = vertexNumberingScope.extraVertices; var sourceData = loadResources.getBuffer(bufferView); var byteStride = bufferView.byteStride || 4; var newVerticesLength = newVertices.length; var destData = new Uint8Array( sourceData.byteLength + newVerticesLength * byteStride ); // Copy the original vertices destData.set(sourceData); // Copy the vertices added for outlining for (j = 0; j < newVerticesLength; ++j) { var sourceIndex = newVertices[j] * byteStride; var destIndex = sourceData.length + j * byteStride; for (var k = 0; k < byteStride; ++k) { destData[destIndex + k] = destData[sourceIndex + k]; } } // This bufferView is an independent buffer now. Update the model accordingly. bufferView.byteOffset = 0; bufferView.byteLength = destData.byteLength; var bufferId = gltf.buffers.push({ byteLength: destData.byteLength, extras: { _pipeline: { source: destData.buffer, }, }, }) - 1; bufferView.buffer = bufferId; loadResources.buffers[bufferId] = destData; // Update the accessors to reflect the added vertices. var accessors = vertexNumberingScope.accessors; for (j = 0; j < accessors.length; ++j) { var accessorId = accessors[j]; gltf.accessors[accessorId].count += newVerticesLength; } if (!vertexNumberingScope.createdOutlines) { // Create the buffers, views, and accessors for the outline texture coordinates. var outlineCoordinates = vertexNumberingScope.outlineCoordinates; var outlineCoordinateBuffer = new Float32Array(outlineCoordinates); var bufferIndex = model.gltf.buffers.push({ byteLength: outlineCoordinateBuffer.byteLength, extras: { _pipeline: { source: outlineCoordinateBuffer.buffer, }, }, }) - 1; loadResources.buffers[bufferIndex] = new Uint8Array( outlineCoordinateBuffer.buffer, 0, outlineCoordinateBuffer.byteLength ); var bufferViewIndex = model.gltf.bufferViews.push({ buffer: bufferIndex, byteLength: outlineCoordinateBuffer.byteLength, byteOffset: 0, byteStride: 3 * Float32Array.BYTES_PER_ELEMENT, target: 34962, }) - 1; var accessorIndex = model.gltf.accessors.push({ bufferView: bufferViewIndex, byteOffset: 0, componentType: 5126, count: outlineCoordinateBuffer.length / 3, type: "VEC3", min: [0.0, 0.0, 0.0], max: [1.0, 1.0, 1.0], }) - 1; var primitives = vertexNumberingScope.primitives; for (j = 0; j < primitives.length; ++j) { primitives[j].attributes._OUTLINE_COORDINATES = accessorIndex; } loadResources.vertexBuffersToCreate.enqueue(bufferViewIndex); vertexNumberingScope.createdOutlines = true; } } } function compactBuffers(model) { var gltf = model.gltf; var loadResources = model._loadResources; var i; for (i = 0; i < gltf.buffers.length; ++i) { var buffer = gltf.buffers[i]; var bufferViewsUsingThisBuffer = gltf.bufferViews.filter( usesBuffer.bind(undefined, i) ); var newLength = bufferViewsUsingThisBuffer.reduce(function ( previous, current ) { return previous + current.byteLength; }, 0); if (newLength === buffer.byteLength) { continue; } var newBuffer = new Uint8Array(newLength); var offset = 0; for (var j = 0; j < bufferViewsUsingThisBuffer.length; ++j) { var bufferView = bufferViewsUsingThisBuffer[j]; var sourceData = loadResources.getBuffer(bufferView); newBuffer.set(sourceData, offset); bufferView.byteOffset = offset; offset += sourceData.byteLength; } loadResources.buffers[i] = newBuffer; buffer.extras._pipeline.source = newBuffer.buffer; buffer.byteLength = newLength; } } function usesBuffer(bufferId, bufferView) { return bufferView.buffer === bufferId; } function getVertexNumberingScope(model, primitive) { var attributes = primitive.attributes; if (attributes === undefined) { return undefined; } var gltf = model.gltf; var vertexNumberingScope; // Initialize common details for all bufferViews used by this primitive's vertices. // All bufferViews used by this primitive must use a common vertex numbering scheme. for (var semantic in attributes) { if (!attributes.hasOwnProperty(semantic)) { continue; } var accessorId = attributes[semantic]; var accessor = gltf.accessors[accessorId]; var bufferViewId = accessor.bufferView; var bufferView = gltf.bufferViews[bufferViewId]; if (!defined(bufferView.extras)) { bufferView.extras = {}; } if (!defined(bufferView.extras._pipeline)) { bufferView.extras._pipeline = {}; } if (!defined(bufferView.extras._pipeline.vertexNumberingScope)) { bufferView.extras._pipeline.vertexNumberingScope = vertexNumberingScope || { // Each element in this array is: // a) undefined, if the vertex at this index has no copies // b) the index of the copy. vertexCopies: [], // Extra vertices appended after the ones originally included in the model. // Each element is the index of the vertex that this one is a copy of. extraVertices: [], // The texture coordinates used for outlining, three floats per vertex. outlineCoordinates: [], // The IDs of accessors that use this vertex numbering. accessors: [], // The IDs of bufferViews that use this vertex numbering. bufferViews: [], // The primitives that use this vertex numbering. primitives: [], // True if the buffer for the outlines has already been created. createdOutlines: false, }; } else if ( vertexNumberingScope !== undefined && bufferView.extras._pipeline.vertexNumberingScope !== vertexNumberingScope ) { // Conflicting vertex numbering, let's give up. return undefined; } vertexNumberingScope = bufferView.extras._pipeline.vertexNumberingScope; if (vertexNumberingScope.bufferViews.indexOf(bufferView) < 0) { vertexNumberingScope.bufferViews.push(bufferView); } if (vertexNumberingScope.accessors.indexOf(accessorId) < 0) { vertexNumberingScope.accessors.push(accessorId); } } vertexNumberingScope.primitives.push(primitive); return vertexNumberingScope; } /** * Represents a command to the renderer for GPU Compute (using old-school GPGPU). * * @private * @constructor */ function ComputeCommand(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The vertex array. If none is provided, a viewport quad will be used. * * @type {VertexArray} * @default undefined */ this.vertexArray = options.vertexArray; /** * The fragment shader source. The default vertex shader is ViewportQuadVS. * * @type {ShaderSource} * @default undefined */ this.fragmentShaderSource = options.fragmentShaderSource; /** * The shader program to apply. * * @type {ShaderProgram} * @default undefined */ this.shaderProgram = options.shaderProgram; /** * An object with functions whose names match the uniforms in the shader program * and return values to set those uniforms. * * @type {Object} * @default undefined */ this.uniformMap = options.uniformMap; /** * Texture to use for offscreen rendering. * * @type {Texture} * @default undefined */ this.outputTexture = options.outputTexture; /** * Function that is called immediately before the ComputeCommand is executed. Used to * update any renderer resources. Takes the ComputeCommand as its single argument. * * @type {Function} * @default undefined */ this.preExecute = options.preExecute; /** * Function that is called after the ComputeCommand is executed. Takes the output * texture as its single argument. * * @type {Function} * @default undefined */ this.postExecute = options.postExecute; /** * Function that is called when the command is canceled * * @type {Function} * @default undefined */ this.canceled = options.canceled; /** * Whether the renderer resources will persist beyond this call. If not, they * will be destroyed after completion. * * @type {Boolean} * @default false */ this.persists = defaultValue(options.persists, false); /** * The pass when to render. Always compute pass. * * @type {Pass} * @default Pass.COMPUTE; */ this.pass = Pass$1.COMPUTE; /** * The object who created this command. This is useful for debugging command * execution; it allows us to see who created a command when we only have a * reference to the command, and can be used to selectively execute commands * with {@link Scene#debugCommandFilter}. * * @type {Object} * @default undefined * * @see Scene#debugCommandFilter */ this.owner = options.owner; } /** * Executes the compute command. * * @param {ComputeEngine} computeEngine The context that processes the compute command. */ ComputeCommand.prototype.execute = function (computeEngine) { computeEngine.execute(this); }; //This file is automatically rebuilt by the Cesium build process. var OctahedralProjectionAtlasFS = "varying vec2 v_textureCoordinates;\n\ \n\ uniform float originalSize;\n\ uniform sampler2D texture0;\n\ uniform sampler2D texture1;\n\ uniform sampler2D texture2;\n\ uniform sampler2D texture3;\n\ uniform sampler2D texture4;\n\ uniform sampler2D texture5;\n\ \n\ const float yMipLevel1 = 1.0 - (1.0 / pow(2.0, 1.0));\n\ const float yMipLevel2 = 1.0 - (1.0 / pow(2.0, 2.0));\n\ const float yMipLevel3 = 1.0 - (1.0 / pow(2.0, 3.0));\n\ const float yMipLevel4 = 1.0 - (1.0 / pow(2.0, 4.0));\n\ \n\ void main()\n\ {\n\ vec2 uv = v_textureCoordinates;\n\ vec2 textureSize = vec2(originalSize * 1.5 + 2.0, originalSize);\n\ vec2 pixel = 1.0 / textureSize;\n\ \n\ float mipLevel = 0.0;\n\ \n\ if (uv.x - pixel.x > (textureSize.y / textureSize.x))\n\ {\n\ mipLevel = 1.0;\n\ if (uv.y - pixel.y > yMipLevel1)\n\ {\n\ mipLevel = 2.0;\n\ if (uv.y - pixel.y * 3.0 > yMipLevel2)\n\ {\n\ mipLevel = 3.0;\n\ if (uv.y - pixel.y * 5.0 > yMipLevel3)\n\ {\n\ mipLevel = 4.0;\n\ if (uv.y - pixel.y * 7.0 > yMipLevel4)\n\ {\n\ mipLevel = 5.0;\n\ }\n\ }\n\ }\n\ }\n\ }\n\ \n\ if (mipLevel > 0.0)\n\ {\n\ float scale = pow(2.0, mipLevel);\n\ \n\ uv.y -= (pixel.y * (mipLevel - 1.0) * 2.0);\n\ uv.x *= ((textureSize.x - 2.0) / textureSize.y);\n\ \n\ uv.x -= 1.0 + pixel.x;\n\ uv.y -= (1.0 - (1.0 / pow(2.0, mipLevel - 1.0)));\n\ uv *= scale;\n\ }\n\ else\n\ {\n\ uv.x *= (textureSize.x / textureSize.y);\n\ }\n\ \n\ if(mipLevel == 0.0)\n\ {\n\ gl_FragColor = texture2D(texture0, uv);\n\ }\n\ else if(mipLevel == 1.0)\n\ {\n\ gl_FragColor = texture2D(texture1, uv);\n\ }\n\ else if(mipLevel == 2.0)\n\ {\n\ gl_FragColor = texture2D(texture2, uv);\n\ }\n\ else if(mipLevel == 3.0)\n\ {\n\ gl_FragColor = texture2D(texture3, uv);\n\ }\n\ else if(mipLevel == 4.0)\n\ {\n\ gl_FragColor = texture2D(texture4, uv);\n\ }\n\ else if(mipLevel == 5.0)\n\ {\n\ gl_FragColor = texture2D(texture5, uv);\n\ }\n\ else\n\ {\n\ gl_FragColor = vec4(0.0);\n\ }\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var OctahedralProjectionFS = "varying vec3 v_cubeMapCoordinates;\n\ uniform samplerCube cubeMap;\n\ \n\ void main()\n\ {\n\ vec4 rgbm = textureCube(cubeMap, v_cubeMapCoordinates);\n\ float m = rgbm.a * 16.0;\n\ vec3 r = rgbm.rgb * m;\n\ gl_FragColor = vec4(r * r, 1.0);\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var OctahedralProjectionVS = "attribute vec4 position;\n\ attribute vec3 cubeMapCoordinates;\n\ \n\ varying vec3 v_cubeMapCoordinates;\n\ \n\ void main()\n\ {\n\ gl_Position = position;\n\ v_cubeMapCoordinates = cubeMapCoordinates;\n\ }\n\ "; /** * Packs all mip levels of a cube map into a 2D texture atlas. * * Octahedral projection is a way of putting the cube maps onto a 2D texture * with minimal distortion and easy look up. * See Chapter 16 of WebGL Insights "HDR Image-Based Lighting on the Web" by Jeff Russell * and "Octahedron Environment Maps" for reference. * * @private */ function OctahedralProjectedCubeMap(url) { this._url = url; this._cubeMapBuffers = undefined; this._cubeMaps = undefined; this._texture = undefined; this._mipTextures = undefined; this._va = undefined; this._sp = undefined; this._maximumMipmapLevel = undefined; this._loading = false; this._ready = false; this._readyPromise = when.defer(); } Object.defineProperties(OctahedralProjectedCubeMap.prototype, { /** * The url to the KTX file containing the specular environment map and convoluted mipmaps. * @memberof OctahedralProjectedCubeMap.prototype * @type {String} * @readonly */ url: { get: function () { return this._url; }, }, /** * A texture containing all the packed convolutions. * @memberof OctahedralProjectedCubeMap.prototype * @type {Texture} * @readonly */ texture: { get: function () { return this._texture; }, }, /** * The maximum number of mip levels. * @memberOf OctahedralProjectedCubeMap.prototype * @type {Number} * @readonly */ maximumMipmapLevel: { get: function () { return this._maximumMipmapLevel; }, }, /** * Determines if the texture atlas is complete and ready to use. * @memberof OctahedralProjectedCubeMap.prototype * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, /** * Gets a promise that resolves when the texture atlas is ready to use. * @memberof OctahedralProjectedCubeMap.prototype * @type {Promise} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); OctahedralProjectedCubeMap.isSupported = function (context) { return ( (context.colorBufferHalfFloat && context.halfFloatingPointTexture) || (context.floatingPointTexture && context.colorBufferFloat) ); }; // These vertices are based on figure 1 from "Octahedron Environment Maps". var v1 = new Cartesian3(1.0, 0.0, 0.0); var v2 = new Cartesian3(0.0, 0.0, 1.0); var v3 = new Cartesian3(-1.0, 0.0, 0.0); var v4 = new Cartesian3(0.0, 0.0, -1.0); var v5 = new Cartesian3(0.0, 1.0, 0.0); var v6 = new Cartesian3(0.0, -1.0, 0.0); // top left, left, top, center, right, top right, bottom, bottom left, bottom right var cubeMapCoordinates = [v5, v3, v2, v6, v1, v5, v4, v5, v5]; var length = cubeMapCoordinates.length; var flatCubeMapCoordinates = new Float32Array(length * 3); var offset = 0; for (var i$3 = 0; i$3 < length; ++i$3, offset += 3) { Cartesian3.pack(cubeMapCoordinates[i$3], flatCubeMapCoordinates, offset); } var flatPositions = new Float32Array([ -1.0, 1.0, // top left -1.0, 0.0, // left 0.0, 1.0, // top 0.0, 0.0, // center 1.0, 0.0, // right 1.0, 1.0, // top right 0.0, -1.0, // bottom -1.0, -1.0, // bottom left 1.0, -1.0, // bottom right ]); var indices = new Uint16Array([ 0, 1, 2, // top left, left, top, 2, 3, 1, // top, center, left, 7, 6, 1, // bottom left, bottom, left, 3, 6, 1, // center, bottom, left, 2, 5, 4, // top, top right, right, 3, 4, 2, // center, right, top, 4, 8, 6, // right, bottom right, bottom, 3, 4, 6, //center, right, bottom ]); function createVertexArray$1(context) { var positionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: flatPositions, usage: BufferUsage$1.STATIC_DRAW, }); var cubeMapCoordinatesBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: flatCubeMapCoordinates, usage: BufferUsage$1.STATIC_DRAW, }); var indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: indices, usage: BufferUsage$1.STATIC_DRAW, indexDatatype: IndexDatatype$1.UNSIGNED_SHORT, }); var attributes = [ { index: 0, vertexBuffer: positionBuffer, componentsPerAttribute: 2, componentDatatype: ComponentDatatype$1.FLOAT, }, { index: 1, vertexBuffer: cubeMapCoordinatesBuffer, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, }, ]; return new VertexArray({ context: context, attributes: attributes, indexBuffer: indexBuffer, }); } function createUniformTexture(texture) { return function () { return texture; }; } function cleanupResources(map) { map._va = map._va && map._va.destroy(); map._sp = map._sp && map._sp.destroy(); var i; var length; var cubeMaps = map._cubeMaps; if (defined(cubeMaps)) { length = cubeMaps.length; for (i = 0; i < length; ++i) { cubeMaps[i].destroy(); } } var mipTextures = map._mipTextures; if (defined(mipTextures)) { length = mipTextures.length; for (i = 0; i < length; ++i) { mipTextures[i].destroy(); } } map._va = undefined; map._sp = undefined; map._cubeMaps = undefined; map._cubeMapBuffers = undefined; map._mipTextures = undefined; } /** * Creates compute commands to generate octahedral projections of each cube map * and then renders them to an atlas. *

* Only needs to be called twice. The first call queues the compute commands to generate the atlas. * The second call cleans up unused resources. Every call afterwards is a no-op. *

* * @param {FrameState} frameState The frame state. * * @private */ OctahedralProjectedCubeMap.prototype.update = function (frameState) { var context = frameState.context; if (!OctahedralProjectedCubeMap.isSupported(context)) { return; } if (defined(this._texture) && defined(this._va)) { cleanupResources(this); } if (defined(this._texture)) { return; } if (!defined(this._texture) && !this._loading) { var cachedTexture = context.textureCache.getTexture(this._url); if (defined(cachedTexture)) { cleanupResources(this); this._texture = cachedTexture; this._maximumMipmapLevel = this._texture.maximumMipmapLevel; this._ready = true; this._readyPromise.resolve(); return; } } var cubeMapBuffers = this._cubeMapBuffers; if (!defined(cubeMapBuffers) && !this._loading) { var that = this; loadKTX(this._url) .then(function (buffers) { that._cubeMapBuffers = buffers; that._loading = false; }) .otherwise(this._readyPromise.reject); this._loading = true; } if (!defined(this._cubeMapBuffers)) { return; } this._va = createVertexArray$1(context); this._sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: OctahedralProjectionVS, fragmentShaderSource: OctahedralProjectionFS, attributeLocations: { position: 0, cubeMapCoordinates: 1, }, }); // We only need up to 6 mip levels to avoid artifacts. var length = Math.min(cubeMapBuffers.length, 6); this._maximumMipmapLevel = length - 1; var cubeMaps = (this._cubeMaps = new Array(length)); var mipTextures = (this._mipTextures = new Array(length)); var originalSize = cubeMapBuffers[0].positiveX.width * 2.0; var uniformMap = { originalSize: function () { return originalSize; }, }; var pixelDatatype = context.halfFloatingPointTexture ? PixelDatatype$1.HALF_FLOAT : PixelDatatype$1.FLOAT; var pixelFormat = PixelFormat$1.RGBA; // First we project each cubemap onto a flat octahedron, and write that to a texture. for (var i = 0; i < length; ++i) { // Swap +Y/-Y faces since the octahedral projection expects this order. var positiveY = cubeMapBuffers[i].positiveY; cubeMapBuffers[i].positiveY = cubeMapBuffers[i].negativeY; cubeMapBuffers[i].negativeY = positiveY; var cubeMap = (cubeMaps[i] = new CubeMap({ context: context, source: cubeMapBuffers[i], })); var size = cubeMaps[i].width * 2; var mipTexture = (mipTextures[i] = new Texture({ context: context, width: size, height: size, pixelDatatype: pixelDatatype, pixelFormat: pixelFormat, })); var command = new ComputeCommand({ vertexArray: this._va, shaderProgram: this._sp, uniformMap: { cubeMap: createUniformTexture(cubeMap), }, outputTexture: mipTexture, persists: true, owner: this, }); frameState.commandList.push(command); uniformMap["texture" + i] = createUniformTexture(mipTexture); } this._texture = new Texture({ context: context, width: originalSize * 1.5 + 2.0, // We add a 1 pixel border to avoid linear sampling artifacts. height: originalSize, pixelDatatype: pixelDatatype, pixelFormat: pixelFormat, }); this._texture.maximumMipmapLevel = this._maximumMipmapLevel; context.textureCache.addTexture(this._url, this._texture); var atlasCommand = new ComputeCommand({ fragmentShaderSource: OctahedralProjectionAtlasFS, uniformMap: uniformMap, outputTexture: this._texture, persists: false, owner: this, }); frameState.commandList.push(atlasCommand); this._ready = true; this._readyPromise.resolve(); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see OctahedralProjectedCubeMap#destroy */ OctahedralProjectedCubeMap.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see OctahedralProjectedCubeMap#isDestroyed */ OctahedralProjectedCubeMap.prototype.destroy = function () { cleanupResources(this); this._texture = this._texture && this._texture.destroy(); return destroyObject(this); }; var boundingSphereCartesian3Scratch = new Cartesian3(); var ModelState = ModelUtility.ModelState; // glTF MIME types discussed in https://github.com/KhronosGroup/glTF/issues/412 and https://github.com/KhronosGroup/glTF/issues/943 var defaultModelAccept = "model/gltf-binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01"; var articulationEpsilon = CesiumMath.EPSILON16; /////////////////////////////////////////////////////////////////////////// function setCachedGltf(model, cachedGltf) { model._cachedGltf = cachedGltf; } // glTF JSON can be big given embedded geometry, textures, and animations, so we // cache it across all models using the same url/cache-key. This also reduces the // slight overhead in assigning defaults to missing values. // // Note that this is a global cache, compared to renderer resources, which // are cached per context. function CachedGltf(options) { this._gltf = options.gltf; this.ready = options.ready; this.modelsToLoad = []; this.count = 0; } Object.defineProperties(CachedGltf.prototype, { gltf: { set: function (value) { this._gltf = value; }, get: function () { return this._gltf; }, }, }); CachedGltf.prototype.makeReady = function (gltfJson) { this.gltf = gltfJson; var models = this.modelsToLoad; var length = models.length; for (var i = 0; i < length; ++i) { var m = models[i]; if (!m.isDestroyed()) { setCachedGltf(m, this); } } this.modelsToLoad = undefined; this.ready = true; }; var gltfCache = {}; var uriToGuid = {}; /////////////////////////////////////////////////////////////////////////// /** * A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL. *

* Cesium includes support for geometry and materials, glTF animations, and glTF skinning. * In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable * with {@link Model#getNode}. glTF cameras and lights are not currently supported. *

*

* An external glTF asset is created with {@link Model.fromGltf}. glTF JSON can also be * created at runtime and passed to this constructor function. In either case, the * {@link Model#readyPromise} is resolved when the model is ready to render, i.e., * when the external binary, image, and shader files are downloaded and the WebGL * resources are created. *

*

* Cesium supports glTF assets with the following extensions: *

    *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations} *
  • * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform} *
  • *
*

*

* For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. *

* * @alias Model * @constructor * * @param {Object} [options] Object with the following properties: * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer. * @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}. * @param {Boolean} [options.allowPicking=true] When true, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. * @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models. * @param {Cartesian2} [options.imageBasedLightingFactor=Cartesian2(1.0, 1.0)] Scales diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When undefined the scene's light color is used instead. * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. * @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0. * * @see Model.fromGltf * * @demo {@link https://sandcastle.cesium.com/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo} */ function Model(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var cacheKey = options.cacheKey; this._cacheKey = cacheKey; this._cachedGltf = undefined; this._releaseGltfJson = defaultValue(options.releaseGltfJson, false); this._modelEditor = options.modelEditor; //【hongguo】 田洪果 倾斜模型编辑修改 this._buildStyle = options.buildStyle; //【hongguo】 田洪果 建筑物特效 var cachedGltf; if ( defined(cacheKey) && defined(gltfCache[cacheKey]) && gltfCache[cacheKey].ready ) { // glTF JSON is in cache and ready cachedGltf = gltfCache[cacheKey]; ++cachedGltf.count; } else { // glTF was explicitly provided, e.g., when a user uses the Model constructor directly var gltf = options.gltf; if (defined(gltf)) { if (gltf instanceof ArrayBuffer) { gltf = new Uint8Array(gltf); } if (gltf instanceof Uint8Array) { // Binary glTF var parsedGltf = parseGlb(gltf); cachedGltf = new CachedGltf({ gltf: parsedGltf, ready: true, }); } else { // Normal glTF (JSON) cachedGltf = new CachedGltf({ gltf: options.gltf, ready: true, }); } cachedGltf.count = 1; if (defined(cacheKey)) { gltfCache[cacheKey] = cachedGltf; } } } setCachedGltf(this, cachedGltf); var basePath = defaultValue(options.basePath, ""); this._resource = Resource.createIfNeeded(basePath); // User specified credit var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } this._credit = credit; // Create a list of Credit's so they can be added from the Resource later this._resourceCredits = []; /** * Determines if the model primitive will be shown. * * @type {Boolean} * * @default true */ this.show = defaultValue(options.show, true); /** * The silhouette color. * * @type {Color} * * @default Color.RED */ this.silhouetteColor = defaultValue(options.silhouetteColor, Color.RED); this._silhouetteColor = new Color(); this._silhouetteColorPreviousAlpha = 1.0; this._normalAttributeName = undefined; /** * The size of the silhouette in pixels. * * @type {Number} * * @default 0.0 */ this.silhouetteSize = defaultValue(options.silhouetteSize, 0.0); /** * The 4x4 transformation matrix that transforms the model from model to world coordinates. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type {Matrix4} * * @default {@link Matrix4.IDENTITY} * * @example * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0); * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin); */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = Matrix4.clone(this.modelMatrix); this._clampedModelMatrix = undefined; /** * A uniform scale applied to this model before the {@link Model#modelMatrix}. * Values greater than 1.0 increase the size of the model; values * less than 1.0 decrease. * * @type {Number} * * @default 1.0 */ this.scale = defaultValue(options.scale, 1.0); this._scale = this.scale; /** * The approximate minimum pixel size of the model regardless of zoom. * This can be used to ensure that a model is visible even when the viewer * zooms out. When 0.0, no minimum size is enforced. * * @type {Number} * * @default 0.0 */ this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0); this._minimumPixelSize = this.minimumPixelSize; /** * The maximum scale size for a model. This can be used to give * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model * is never an unreasonable scale. * * @type {Number} */ this.maximumScale = options.maximumScale; this._maximumScale = this.maximumScale; /** * User-defined object returned when the model is picked. * * @type Object * * @default undefined * * @see Scene#pick */ this.id = options.id; this._id = options.id; /** * Returns the height reference of the model * * @type {HeightReference} * * @default HeightReference.NONE */ this.heightReference = defaultValue( options.heightReference, HeightReference$1.NONE ); this._heightReference = this.heightReference; this._heightChanged = false; this._removeUpdateHeightCallback = undefined; var scene = options.scene; this._scene = scene; if (defined(scene) && defined(scene.terrainProviderChanged)) { this._terrainProviderChangedCallback = scene.terrainProviderChanged.addEventListener( function () { this._heightChanged = true; }, this ); } /** * Used for picking primitives that wrap a model. * * @private */ this._pickObject = options.pickObject; this._allowPicking = defaultValue(options.allowPicking, true); this._ready = false; this._readyPromise = when.defer(); /** * The currently playing glTF animations. * * @type {ModelAnimationCollection} */ this.activeAnimations = new ModelAnimationCollection(this); /** * Determines if the model's animations should hold a pose over frames where no keyframes are specified. * * @type {Boolean} */ this.clampAnimations = defaultValue(options.clampAnimations, true); this._defaultTexture = undefined; this._incrementallyLoadTextures = defaultValue( options.incrementallyLoadTextures, true ); this._asynchronous = defaultValue(options.asynchronous, true); /** * Determines whether the model casts or receives shadows from light sources. * * @type {ShadowMode} * * @default ShadowMode.ENABLED */ this.shadows = defaultValue(options.shadows, ShadowMode$1.ENABLED); this._shadows = this.shadows; /** * A color that blends with the model's rendered color. * * @type {Color} * * @default Color.WHITE */ this.color = Color.clone(defaultValue(options.color, Color.WHITE)); this._colorPreviousAlpha = 1.0; /** * Defines how the color blends with the model. * * @type {ColorBlendMode} * * @default ColorBlendMode.HIGHLIGHT */ this.colorBlendMode = defaultValue( options.colorBlendMode, ColorBlendMode$1.HIGHLIGHT ); /** * Value used to determine the color strength when the colorBlendMode is MIX. * A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with * any value in-between resulting in a mix of the two. * * @type {Number} * * @default 0.5 */ this.colorBlendAmount = defaultValue(options.colorBlendAmount, 0.5); this._colorShadingEnabled = false; this._clippingPlanes = undefined; this.clippingPlanes = options.clippingPlanes; // Used for checking if shaders need to be regenerated due to clipping plane changes. this._clippingPlanesState = 0; // If defined, use this matrix to transform miscellaneous properties like // clipping planes and IBL instead of the modelMatrix. This is so that when // models are part of a tileset these properties get transformed relative to // a common reference (such as the root). this.referenceMatrix = undefined; /** * Whether to cull back-facing geometry. When true, back face culling is * determined by the material's doubleSided property; when false, back face * culling is disabled. Back faces are not culled if {@link Model#color} is * translucent or {@link Model#silhouetteSize} is greater than 0.0. * * @type {Boolean} * * @default true */ this.backFaceCulling = defaultValue(options.backFaceCulling, true); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds * to one draw command. A glTF mesh has an array of primitives, often of length one. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this._debugShowBoundingVolume = false; /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the model in wireframe. *

* * @type {Boolean} * * @default false */ this.debugWireframe = defaultValue(options.debugWireframe, false); this._debugWireframe = false; this._distanceDisplayCondition = options.distanceDisplayCondition; // Undocumented options this._addBatchIdToGeneratedShaders = options.addBatchIdToGeneratedShaders; this._precreatedAttributes = options.precreatedAttributes; this._vertexShaderLoaded = options.vertexShaderLoaded; this._fragmentShaderLoaded = options.fragmentShaderLoaded; this._uniformMapLoaded = options.uniformMapLoaded; this._pickIdLoaded = options.pickIdLoaded; this._ignoreCommands = defaultValue(options.ignoreCommands, false); this._requestType = options.requestType; this._upAxis = defaultValue(options.upAxis, Axis$1.Y); this._gltfForwardAxis = Axis$1.Z; this._forwardAxis = options.forwardAxis; /** * @private * @readonly */ this.cull = defaultValue(options.cull, true); /** * @private * @readonly */ this.opaquePass = defaultValue(options.opaquePass, Pass$1.OPAQUE); this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale this._clippingPlanesMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from reference matrix and the current view matrix this._iblReferenceFrameMatrix = Matrix3.clone(Matrix3.IDENTITY); // Derived from reference matrix and the current view matrix this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins this._boundingSphere = undefined; this._scaledBoundingSphere = new BoundingSphere(); this._state = ModelState.NEEDS_LOAD; this._loadResources = undefined; this._mode = undefined; this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform this._dirty = false; // true when the model was transformed this frame this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal this._runtime = { animations: undefined, articulationsByName: undefined, articulationsByStageKey: undefined, stagesByKey: undefined, rootNodes: undefined, nodes: undefined, // Indexed with the node's index nodesByName: undefined, // Indexed with name property in the node skinnedNodes: undefined, meshesByName: undefined, // Indexed with the name property in the mesh materialsByName: undefined, // Indexed with the name property in the material materialsById: undefined, // Indexed with the material's index }; this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation this._extensionsUsed = undefined; // Cached used glTF extensions this._extensionsRequired = undefined; // Cached required glTF extensions this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes this._programPrimitives = {}; this._rendererResources = { // Cached between models with the same url/cache-key buffers: {}, vertexArrays: {}, programs: {}, sourceShaders: {}, silhouettePrograms: {}, textures: {}, samplers: {}, renderStates: {}, }; this._cachedRendererResources = undefined; this._loadRendererResourcesFromCache = false; this._dequantizeInShader = defaultValue(options.dequantizeInShader, true); this._decodedData = {}; this._cachedGeometryByteLength = 0; this._cachedTexturesByteLength = 0; this._geometryByteLength = 0; this._texturesByteLength = 0; this._trianglesLength = 0; // Hold references for shader reconstruction. // Hold these separately because _cachedGltf may get released (this.releaseGltfJson) this._sourceTechniques = {}; this._sourcePrograms = {}; this._quantizedVertexShaders = {}; this._nodeCommands = []; this._pickIds = []; // CESIUM_RTC extension this._rtcCenter = undefined; // reference to either 3D or 2D this._rtcCenterEye = undefined; // in eye coordinates this._rtcCenter3D = undefined; // in world coordinates this._rtcCenter2D = undefined; // in projected world coordinates this._sourceVersion = undefined; this._sourceKHRTechniquesWebGL = undefined; this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0); Cartesian2.clone( options.imageBasedLightingFactor, this._imageBasedLightingFactor ); this._lightColor = Cartesian3.clone(options.lightColor); this._luminanceAtZenith = undefined; this.luminanceAtZenith = defaultValue(options.luminanceAtZenith, 0.2); this._sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients; this._specularEnvironmentMaps = options.specularEnvironmentMaps; this._shouldUpdateSpecularMapAtlas = true; this._specularEnvironmentMapAtlas = undefined; this._useDefaultSphericalHarmonics = false; this._useDefaultSpecularMaps = false; this._shouldRegenerateShaders = false; } Object.defineProperties(Model.prototype, { /** * The object for the glTF JSON, including properties with default values omitted * from the JSON provided to this model. * * @memberof Model.prototype * * @type {Object} * @readonly * * @default undefined */ gltf: { get: function () { return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined; }, }, /** * When true, the glTF JSON is not stored with the model once the model is * loaded (when {@link Model#ready} is true). This saves memory when * geometry, textures, and animations are embedded in the .gltf file. * This is especially useful for cases like 3D buildings, where each .gltf model is unique * and caching the glTF JSON is not effective. * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @default false * * @private */ releaseGltfJson: { get: function () { return this._releaseGltfJson; }, }, /** * The key identifying this model in the model cache for glTF JSON, renderer resources, and animations. * Caching saves memory and improves loading speed when several models with the same url are created. *

* This key is automatically generated when the model is created with {@link Model.fromGltf}. If the model * is created directly from glTF JSON using the {@link Model} constructor, this key can be manually * provided; otherwise, the model will not be changed. *

* * @memberof Model.prototype * * @type {String} * @readonly * * @private */ cacheKey: { get: function () { return this._cacheKey; }, }, /** * The base path that paths in the glTF JSON are relative to. The base * path is the same path as the path containing the .gltf file * minus the .gltf file, when binary, image, and shader files are * in the same directory as the .gltf. When this is '', * the app's base path is used. * * @memberof Model.prototype * * @type {String} * @readonly * * @default '' */ basePath: { get: function () { return this._resource.url; }, }, /** * The model's bounding sphere in its local coordinate system. This does not take into * account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}. * * @memberof Model.prototype * * @type {BoundingSphere} * @readonly * * @default undefined * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. * * @example * // Center in WGS84 coordinates * var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3()); */ boundingSphere: { get: function () { //>>includeStart('debug', pragmas.debug); if (this._state !== ModelState.LOADED) { throw new DeveloperError( "The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true." ); } //>>includeEnd('debug'); var modelMatrix = this.modelMatrix; if ( this.heightReference !== HeightReference$1.NONE && this._clampedModelMatrix ) { modelMatrix = this._clampedModelMatrix; } var nonUniformScale = Matrix4.getScale( modelMatrix, boundingSphereCartesian3Scratch ); var scale = defined(this.maximumScale) ? Math.min(this.maximumScale, this.scale) : this.scale; Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale); var scaledBoundingSphere = this._scaledBoundingSphere; scaledBoundingSphere.center = Cartesian3.multiplyComponents( this._boundingSphere.center, nonUniformScale, scaledBoundingSphere.center ); scaledBoundingSphere.radius = Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius; if (defined(this._rtcCenter)) { Cartesian3.add( this._rtcCenter, scaledBoundingSphere.center, scaledBoundingSphere.center ); } return scaledBoundingSphere; }, }, /** * When true, this model is ready to render, i.e., the external binary, image, * and shader files were downloaded and the WebGL resources were created. This is set to * true right before {@link Model#readyPromise} is resolved. * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @default false */ ready: { get: function () { return this._ready; }, }, /** * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image, * and shader files were downloaded and the WebGL resources were created. *

* This promise is resolved at the end of the frame before the first frame the model is rendered in. *

* * @memberof Model.prototype * @type {Promise.} * @readonly * * @example * // Play all animations at half-speed when the model is ready to render * Cesium.when(model.readyPromise).then(function(model) { * model.activeAnimations.addAll({ * multiplier : 0.5 * }); * }).otherwise(function(error){ * window.alert(error); * }); * * @see Model#ready */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * Determines if model WebGL resource creation will be spread out over several frames or * block until completion once all glTF files are loaded. * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @default true */ asynchronous: { get: function () { return this._asynchronous; }, }, /** * When true, each glTF mesh and primitive is pickable with {@link Scene#pick}. When false, GPU memory is saved. * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @default true */ allowPicking: { get: function () { return this._allowPicking; }, }, /** * Determine if textures may continue to stream in after the model is loaded. * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @default true */ incrementallyLoadTextures: { get: function () { return this._incrementallyLoadTextures; }, }, /** * Return the number of pending texture loads. * * @memberof Model.prototype * * @type {Number} * @readonly */ pendingTextureLoads: { get: function () { return defined(this._loadResources) ? this._loadResources.pendingTextureLoads : 0; }, }, /** * Returns true if the model was transformed this frame * * @memberof Model.prototype * * @type {Boolean} * @readonly * * @private */ dirty: { get: function () { return this._dirty; }, }, /** * Gets or sets the condition specifying at what distance from the camera that this model will be displayed. * @memberof Model.prototype * @type {DistanceDisplayCondition} * @default undefined */ distanceDisplayCondition: { get: function () { return this._distanceDisplayCondition; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError("far must be greater than near"); } //>>includeEnd('debug'); this._distanceDisplayCondition = DistanceDisplayCondition.clone( value, this._distanceDisplayCondition ); }, }, extensionsUsed: { get: function () { if (!defined(this._extensionsUsed)) { this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf); } return this._extensionsUsed; }, }, extensionsRequired: { get: function () { if (!defined(this._extensionsRequired)) { this._extensionsRequired = ModelUtility.getRequiredExtensions( this.gltf ); } return this._extensionsRequired; }, }, /** * Gets the model's up-axis. * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up. * * @memberof Model.prototype * * @type {Number} * @default Axis.Y * @readonly * * @private */ upAxis: { get: function () { return this._upAxis; }, }, /** * Gets the model's forward axis. * By default, glTF 2.0 models are z-forward according to the glTF spec, however older * glTF (1.0, 0.8) models used x-forward. Note that only Axis.X and Axis.Z are supported. * * @memberof Model.prototype * * @type {Number} * @default Axis.Z * @readonly * * @private */ forwardAxis: { get: function () { if (defined(this._forwardAxis)) { return this._forwardAxis; } return this._gltfForwardAxis; }, }, /** * Gets the model's triangle count. * * @private */ trianglesLength: { get: function () { return this._trianglesLength; }, }, /** * Gets the model's geometry memory in bytes. This includes all vertex and index buffers. * * @private */ geometryByteLength: { get: function () { return this._geometryByteLength; }, }, /** * Gets the model's texture memory in bytes. * * @private */ texturesByteLength: { get: function () { return this._texturesByteLength; }, }, /** * Gets the model's cached geometry memory in bytes. This includes all vertex and index buffers. * * @private */ cachedGeometryByteLength: { get: function () { return this._cachedGeometryByteLength; }, }, /** * Gets the model's cached texture memory in bytes. * * @private */ cachedTexturesByteLength: { get: function () { return this._cachedTexturesByteLength; }, }, /** * The {@link ClippingPlaneCollection} used to selectively disable rendering the model. * * @memberof Model.prototype * * @type {ClippingPlaneCollection} */ clippingPlanes: { get: function () { return this._clippingPlanes; }, set: function (value) { if (value === this._clippingPlanes) { return; } // Handle destroying, checking of unknown, checking for existing ownership ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes"); }, }, /** * @private */ pickIds: { get: function () { return this._pickIds; }, }, /** * Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final * diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources. * * @memberof Model.prototype * * @type {Cartesian2} * @default Cartesian2(1.0, 1.0) */ imageBasedLightingFactor: { get: function () { return this._imageBasedLightingFactor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("imageBasedLightingFactor", value); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.x", value.x, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.x", value.x, 1.0 ); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.y", value.y, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.y", value.y, 1.0 ); //>>includeEnd('debug'); var imageBasedLightingFactor = this._imageBasedLightingFactor; if ( value === imageBasedLightingFactor || Cartesian2.equals(value, imageBasedLightingFactor) ) { return; } this._shouldRegenerateShaders = this._shouldRegenerateShaders || (this._imageBasedLightingFactor.x > 0.0 && value.x === 0.0) || (this._imageBasedLightingFactor.x === 0.0 && value.x > 0.0); this._shouldRegenerateShaders = this._shouldRegenerateShaders || (this._imageBasedLightingFactor.y > 0.0 && value.y === 0.0) || (this._imageBasedLightingFactor.y === 0.0 && value.y > 0.0); Cartesian2.clone(value, this._imageBasedLightingFactor); }, }, /** * The light color when shading the model. When undefined the scene's light color is used instead. *

* For example, disabling additional light sources by setting model.imageBasedLightingFactor = new Cesium.Cartesian2(0.0, 0.0) will make the * model much darker. Here, increasing the intensity of the light source will make the model brighter. *

* * @memberof Model.prototype * * @type {Cartesian3} * @default undefined */ lightColor: { get: function () { return this._lightColor; }, set: function (value) { var lightColor = this._lightColor; if (value === lightColor || Cartesian3.equals(value, lightColor)) { return; } this._shouldRegenerateShaders = this._shouldRegenerateShaders || (defined(lightColor) && !defined(value)) || (defined(value) && !defined(lightColor)); this._lightColor = Cartesian3.clone(value, lightColor); }, }, /** * The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. * This is used when {@link Model#specularEnvironmentMaps} and {@link Model#sphericalHarmonicCoefficients} are not defined. * * @memberof Model.prototype * * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo} * @type {Number} * @default 0.2 */ luminanceAtZenith: { get: function () { return this._luminanceAtZenith; }, set: function (value) { var lum = this._luminanceAtZenith; if (value === lum) { return; } this._shouldRegenerateShaders = this._shouldRegenerateShaders || (defined(lum) && !defined(value)) || (defined(value) && !defined(lum)); this._luminanceAtZenith = value; }, }, /** * The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When undefined, a diffuse irradiance * computed from the atmosphere color is used. *

* There are nine Cartesian3 coefficients. * The order of the coefficients is: L00, L1-1, L10, L11, L2-2, L2-1, L20, L21, L22 *

* * These values can be obtained by preprocessing the environment map using the cmgen tool of * {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be * supplied to {@link Model#specularEnvironmentMaps}. * * @memberof Model.prototype * * @type {Cartesian3[]} * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo} * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps} */ sphericalHarmonicCoefficients: { get: function () { return this._sphericalHarmonicCoefficients; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && (!Array.isArray(value) || value.length !== 9)) { throw new DeveloperError( "sphericalHarmonicCoefficients must be an array of 9 Cartesian3 values." ); } //>>includeEnd('debug'); if (value === this._sphericalHarmonicCoefficients) { return; } this._sphericalHarmonicCoefficients = value; this._shouldRegenerateShaders = true; }, }, /** * A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps. * * @memberof Model.prototype * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo} * @type {String} * @see Model#sphericalHarmonicCoefficients */ specularEnvironmentMaps: { get: function () { return this._specularEnvironmentMaps; }, set: function (value) { this._shouldUpdateSpecularMapAtlas = this._shouldUpdateSpecularMapAtlas || value !== this._specularEnvironmentMaps; this._specularEnvironmentMaps = value; }, }, /** * Gets the credit that will be displayed for the model * @memberof Model.prototype * @type {Credit} */ credit: { get: function () { return this._credit; }, }, }); function silhouetteSupported(context) { return context.stencilBuffer; } function isColorShadingEnabled(model) { return ( !Color.equals(model.color, Color.WHITE) || model.colorBlendMode !== ColorBlendMode$1.HIGHLIGHT ); } function isClippingEnabled(model) { var clippingPlanes = model._clippingPlanes; return ( defined(clippingPlanes) && clippingPlanes.enabled && clippingPlanes.length !== 0 ); } /** * Determines if silhouettes are supported. * * @param {Scene} scene The scene. * @returns {Boolean} true if silhouettes are supported; otherwise, returns false */ Model.silhouetteSupported = function (scene) { return silhouetteSupported(scene.context); }; function containsGltfMagic(uint8Array) { var magic = getMagic(uint8Array); return magic === "glTF"; } /** *

* Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image, * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved. *

*

* The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the .glb extension. *

*

* Cesium supports glTF assets with the following extensions: *

    *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_binary_glTF/README.md|KHR_binary_glTF (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Khronos/KHR_materials_common/README.md|KHR_materials_common (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/WEB3D_quantized_attributes/README.md|WEB3D_quantized_attributes (glTF 1.0)} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/AGI_articulations/README.md|AGI_articulations} *
  • * {@link https://github.com/KhronosGroup/glTF/pull/1302|KHR_blend (draft)} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/README.md|KHR_materials_pbrSpecularGlossiness} *
  • * {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit/README.md|KHR_materials_unlit} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_techniques_webgl/README.md|KHR_techniques_webgl} *
  • * {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_texture_transform/README.md|KHR_texture_transform} *
  • *
*

*

* For high-precision rendering, Cesium supports the {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/1.0/Vendor/CESIUM_RTC/README.md|CESIUM_RTC} extension, which introduces the * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated * relative to a local origin. *

* * @param {Object} options Object with the following properties: * @param {Resource|String} options.url The url to the .gltf file. * @param {Resource|String} [options.basePath] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. * @param {Number} [options.maximumScale] The maximum scale for the model. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}. * @param {Boolean} [options.allowPicking=true] When true, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe. * @param {HeightReference} [options.heightReference=HeightReference.NONE] Determines how the model is drawn relative to terrain. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed. * @param {Color} [options.color=Color.WHITE] A color that blends with the model's rendered color. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the colorBlendMode is MIX. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two. * @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. * @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. * @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models. * @param {Credit|String} [options.credit] A credit for the model, which is displayed on the canvas. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if {@link Model#color} is translucent or {@link Model#silhouetteSize} is greater than 0.0. * * @returns {Model} The newly created model. * * @example * // Example 1. Create a model from a glTF asset * var model = scene.primitives.add(Cesium.Model.fromGltf({ * url : './duck/duck.gltf' * })); * * @example * // Example 2. Create model and provide all properties and events * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0); * var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin); * * var model = scene.primitives.add(Cesium.Model.fromGltf({ * url : './duck/duck.gltf', * show : true, // default * modelMatrix : modelMatrix, * scale : 2.0, // double size * minimumPixelSize : 128, // never smaller than 128 pixels * maximumScale: 20000, // never larger than 20000 * model size (overrides minimumPixelSize) * allowPicking : false, // not pickable * debugShowBoundingVolume : false, // default * debugWireframe : false * })); * * model.readyPromise.then(function(model) { * // Play all animations when the model is ready to render * model.activeAnimations.addAll(); * }); */ Model.fromGltf = function (options) { //>>includeStart('debug', pragmas.debug); if (!defined(options) || !defined(options.url)) { throw new DeveloperError("options.url is required"); } //>>includeEnd('debug'); var url = options.url; options = clone$1(options); // Create resource for the model file var modelResource = Resource.createIfNeeded(url); // Setup basePath to get dependent files var basePath = defaultValue(options.basePath, modelResource.clone()); var resource = Resource.createIfNeeded(basePath); // If no cache key is provided, use a GUID. // Check using a URI to GUID dictionary that we have not already added this model. var cacheKey = defaultValue( options.cacheKey, uriToGuid[getAbsoluteUri(modelResource.url)] ); if (!defined(cacheKey)) { cacheKey = createGuid(); uriToGuid[getAbsoluteUri(modelResource.url)] = cacheKey; } if (defined(options.basePath) && !defined(options.cacheKey)) { cacheKey += resource.url; } options.cacheKey = cacheKey; options.basePath = resource; var model = new Model(options); var cachedGltf = gltfCache[cacheKey]; if (!defined(cachedGltf)) { cachedGltf = new CachedGltf({ ready: false, }); cachedGltf.count = 1; cachedGltf.modelsToLoad.push(model); setCachedGltf(model, cachedGltf); gltfCache[cacheKey] = cachedGltf; // Add Accept header if we need it if (!defined(modelResource.headers.Accept)) { modelResource.headers.Accept = defaultModelAccept; } modelResource .fetchArrayBuffer() .then(function (arrayBuffer) { var array = new Uint8Array(arrayBuffer); if (containsGltfMagic(array)) { // Load binary glTF var parsedGltf = parseGlb(array); cachedGltf.makeReady(parsedGltf); } else { // Load text (JSON) glTF var json = getJsonFromTypedArray(array); cachedGltf.makeReady(json); } var resourceCredits = model._resourceCredits; var credits = modelResource.credits; if (defined(credits)) { var length = credits.length; for (var i = 0; i < length; i++) { resourceCredits.push(credits[i]); } } }) .otherwise( ModelUtility.getFailedLoadFunction(model, "model", modelResource.url) ); } else if (!cachedGltf.ready) { // Cache hit but the fetchArrayBuffer() or fetchText() request is still pending ++cachedGltf.count; cachedGltf.modelsToLoad.push(model); } // else if the cached glTF is defined and ready, the // model constructor will pick it up using the cache key. return model; }; /** * For the unit tests to verify model caching. * * @private */ Model._gltfCache = gltfCache; function getRuntime(model, runtimeName, name) { //>>includeStart('debug', pragmas.debug); if (model._state !== ModelState.LOADED) { throw new DeveloperError( "The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true." ); } if (!defined(name)) { throw new DeveloperError("name is required."); } //>>includeEnd('debug'); return model._runtime[runtimeName][name]; } /** * Returns the glTF node with the given name property. This is used to * modify a node's transform for animation outside of glTF animations. * * @param {String} name The glTF name of the node. * @returns {ModelNode} The node or undefined if no node with name exists. * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. * * @example * // Apply non-uniform scale to node LOD3sp * var node = model.getNode('LOD3sp'); * node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix); */ Model.prototype.getNode = function (name) { var node = getRuntime(this, "nodesByName", name); return defined(node) ? node.publicNode : undefined; }; /** * Returns the glTF mesh with the given name property. * * @param {String} name The glTF name of the mesh. * * @returns {ModelMesh} The mesh or undefined if no mesh with name exists. * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. */ Model.prototype.getMesh = function (name) { return getRuntime(this, "meshesByName", name); }; /** * Returns the glTF material with the given name property. * * @param {String} name The glTF name of the material. * @returns {ModelMaterial} The material or undefined if no material with name exists. * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. */ Model.prototype.getMaterial = function (name) { return getRuntime(this, "materialsByName", name); }; /** * Sets the current value of an articulation stage. After setting one or multiple stage values, call * Model.applyArticulations() to cause the node matrices to be recalculated. * * @param {String} articulationStageKey The name of the articulation, a space, and the name of the stage. * @param {Number} value The numeric value of this stage of the articulation. * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. * * @see Model#applyArticulations */ Model.prototype.setArticulationStage = function (articulationStageKey, value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number("value", value); //>>includeEnd('debug'); var stage = getRuntime(this, "stagesByKey", articulationStageKey); var articulation = getRuntime( this, "articulationsByStageKey", articulationStageKey ); if (defined(stage) && defined(articulation)) { value = CesiumMath.clamp(value, stage.minimumValue, stage.maximumValue); if ( !CesiumMath.equalsEpsilon(stage.currentValue, value, articulationEpsilon) ) { stage.currentValue = value; articulation.isDirty = true; } } }; var scratchArticulationCartesian = new Cartesian3(); var scratchArticulationRotation = new Matrix3(); /** * Modifies a Matrix4 by applying a transformation for a given value of a stage. Note this is different usage * from the typical result parameter, in that the incoming value of result is * meaningful. Various stages of an articulation can be multiplied together, so their * transformations are all merged into a composite Matrix4 representing them all. * * @param {object} stage The stage of an articulation that is being evaluated. * @param {Matrix4} result The matrix to be modified. * @returns {Matrix4} A matrix transformed as requested by the articulation stage. * * @private */ function applyArticulationStageMatrix(stage, result) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("stage", stage); Check.typeOf.object("result", result); //>>includeEnd('debug'); var value = stage.currentValue; var cartesian = scratchArticulationCartesian; var rotation; switch (stage.type) { case "xRotate": rotation = Matrix3.fromRotationX( CesiumMath.toRadians(value), scratchArticulationRotation ); Matrix4.multiplyByMatrix3(result, rotation, result); break; case "yRotate": rotation = Matrix3.fromRotationY( CesiumMath.toRadians(value), scratchArticulationRotation ); Matrix4.multiplyByMatrix3(result, rotation, result); break; case "zRotate": rotation = Matrix3.fromRotationZ( CesiumMath.toRadians(value), scratchArticulationRotation ); Matrix4.multiplyByMatrix3(result, rotation, result); break; case "xTranslate": cartesian.x = value; cartesian.y = 0.0; cartesian.z = 0.0; Matrix4.multiplyByTranslation(result, cartesian, result); break; case "yTranslate": cartesian.x = 0.0; cartesian.y = value; cartesian.z = 0.0; Matrix4.multiplyByTranslation(result, cartesian, result); break; case "zTranslate": cartesian.x = 0.0; cartesian.y = 0.0; cartesian.z = value; Matrix4.multiplyByTranslation(result, cartesian, result); break; case "xScale": cartesian.x = value; cartesian.y = 1.0; cartesian.z = 1.0; Matrix4.multiplyByScale(result, cartesian, result); break; case "yScale": cartesian.x = 1.0; cartesian.y = value; cartesian.z = 1.0; Matrix4.multiplyByScale(result, cartesian, result); break; case "zScale": cartesian.x = 1.0; cartesian.y = 1.0; cartesian.z = value; Matrix4.multiplyByScale(result, cartesian, result); break; case "uniformScale": Matrix4.multiplyByUniformScale(result, value, result); break; } return result; } var scratchApplyArticulationTransform = new Matrix4(); /** * Applies any modified articulation stages to the matrix of each node that participates * in any articulation. Note that this will overwrite any nodeTransformations on participating nodes. * * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true. */ Model.prototype.applyArticulations = function () { var articulationsByName = this._runtime.articulationsByName; for (var articulationName in articulationsByName) { if (articulationsByName.hasOwnProperty(articulationName)) { var articulation = articulationsByName[articulationName]; if (articulation.isDirty) { articulation.isDirty = false; var numNodes = articulation.nodes.length; for (var n = 0; n < numNodes; ++n) { var node = articulation.nodes[n]; var transform = Matrix4.clone( node.originalMatrix, scratchApplyArticulationTransform ); var numStages = articulation.stages.length; for (var s = 0; s < numStages; ++s) { var stage = articulation.stages[s]; transform = applyArticulationStageMatrix(stage, transform); } node.matrix = transform; } } } } }; /////////////////////////////////////////////////////////////////////////// function addBuffersToLoadResources(model) { var gltf = model.gltf; var loadResources = model._loadResources; ForEach.buffer(gltf, function (buffer, id) { loadResources.buffers[id] = buffer.extras._pipeline.source; }); } function bufferLoad(model, id) { return function (arrayBuffer) { var loadResources = model._loadResources; var buffer = new Uint8Array(arrayBuffer); --loadResources.pendingBufferLoads; model.gltf.buffers[id].extras._pipeline.source = buffer; }; } function parseBufferViews(model) { var bufferViews = model.gltf.bufferViews; var vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate; // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below. ForEach.bufferView(model.gltf, function (bufferView, id) { if (bufferView.target === WebGLConstants$1.ARRAY_BUFFER) { vertexBuffersToCreate.enqueue(id); } }); var indexBuffersToCreate = model._loadResources.indexBuffersToCreate; var indexBufferIds = {}; // The Cesium Renderer requires knowing the datatype for an index buffer // at creation type, which is not part of the glTF bufferview so loop // through glTF accessors to create the bufferview's index buffer. ForEach.accessor(model.gltf, function (accessor) { var bufferViewId = accessor.bufferView; if (!defined(bufferViewId)) { return; } var bufferView = bufferViews[bufferViewId]; if ( bufferView.target === WebGLConstants$1.ELEMENT_ARRAY_BUFFER && !defined(indexBufferIds[bufferViewId]) ) { indexBufferIds[bufferViewId] = true; indexBuffersToCreate.enqueue({ id: bufferViewId, componentType: accessor.componentType, }); } }); } function parseTechniques(model) { // retain references to gltf techniques var gltf = model.gltf; if (!hasExtension(gltf, "KHR_techniques_webgl")) { return; } var sourcePrograms = model._sourcePrograms; var sourceTechniques = model._sourceTechniques; var programs = gltf.extensions.KHR_techniques_webgl.programs; ForEach.technique(gltf, function (technique, techniqueId) { sourceTechniques[techniqueId] = clone$1(technique); var programId = technique.program; if (!defined(sourcePrograms[programId])) { sourcePrograms[programId] = clone$1(programs[programId]); } }); } function shaderLoad(model, type, id) { return function (source) { var loadResources = model._loadResources; loadResources.shaders[id] = { source: source, type: type, bufferView: undefined, }; --loadResources.pendingShaderLoads; model._rendererResources.sourceShaders[id] = source; }; } function parseShaders(model) { var gltf = model.gltf; var buffers = gltf.buffers; var bufferViews = gltf.bufferViews; var sourceShaders = model._rendererResources.sourceShaders; ForEach.shader(gltf, function (shader, id) { // Shader references either uri (external or base64-encoded) or bufferView if (defined(shader.bufferView)) { var bufferViewId = shader.bufferView; var bufferView = bufferViews[bufferViewId]; var bufferId = bufferView.buffer; var buffer = buffers[bufferId]; var source = getStringFromTypedArray( buffer.extras._pipeline.source, bufferView.byteOffset, bufferView.byteLength ); sourceShaders[id] = source; } else if (defined(shader.extras._pipeline.source)) { sourceShaders[id] = shader.extras._pipeline.source; } else { ++model._loadResources.pendingShaderLoads; var shaderResource = model._resource.getDerivedResource({ url: shader.uri, }); shaderResource .fetchText() .then(shaderLoad(model, shader.type, id)) .otherwise( ModelUtility.getFailedLoadFunction( model, "shader", shaderResource.url ) ); } }); } function parsePrograms(model) { var sourceTechniques = model._sourceTechniques; for (var techniqueId in sourceTechniques) { if (sourceTechniques.hasOwnProperty(techniqueId)) { var technique = sourceTechniques[techniqueId]; model._loadResources.programsToCreate.enqueue({ programId: technique.program, techniqueId: techniqueId, }); } } } function parseArticulations(model) { var articulationsByName = {}; var articulationsByStageKey = {}; var runtimeStagesByKey = {}; model._runtime.articulationsByName = articulationsByName; model._runtime.articulationsByStageKey = articulationsByStageKey; model._runtime.stagesByKey = runtimeStagesByKey; var gltf = model.gltf; if ( !hasExtension(gltf, "AGI_articulations") || !defined(gltf.extensions) || !defined(gltf.extensions.AGI_articulations) ) { return; } var gltfArticulations = gltf.extensions.AGI_articulations.articulations; if (!defined(gltfArticulations)) { return; } var numArticulations = gltfArticulations.length; for (var i = 0; i < numArticulations; ++i) { var articulation = clone$1(gltfArticulations[i]); articulation.nodes = []; articulation.isDirty = true; articulationsByName[articulation.name] = articulation; var numStages = articulation.stages.length; for (var s = 0; s < numStages; ++s) { var stage = articulation.stages[s]; stage.currentValue = stage.initialValue; var stageKey = articulation.name + " " + stage.name; articulationsByStageKey[stageKey] = articulation; runtimeStagesByKey[stageKey] = stage; } } } function imageLoad(model, textureId) { return function (image) { var loadResources = model._loadResources; --loadResources.pendingTextureLoads; loadResources.texturesToCreate.enqueue({ id: textureId, image: image, bufferView: image.bufferView, width: image.width, height: image.height, internalFormat: image.internalFormat, }); }; } var ktxRegex$1 = /(^data:image\/ktx)|(\.ktx$)/i; var crnRegex$1 = /(^data:image\/crn)|(\.crn$)/i; function parseTextures(model, context, supportsWebP) { var gltf = model.gltf; var images = gltf.images; var uri; ForEach.texture(gltf, function (texture, id) { var imageId = texture.source; if ( defined(texture.extensions) && defined(texture.extensions.EXT_texture_webp) && supportsWebP ) { imageId = texture.extensions.EXT_texture_webp.source; } var gltfImage = images[imageId]; var extras = gltfImage.extras; var bufferViewId = gltfImage.bufferView; var mimeType = gltfImage.mimeType; uri = gltfImage.uri; // First check for a compressed texture if (defined(extras) && defined(extras.compressedImage3DTiles)) { var crunch = extras.compressedImage3DTiles.crunch; var s3tc = extras.compressedImage3DTiles.s3tc; var pvrtc = extras.compressedImage3DTiles.pvrtc1; var etc1 = extras.compressedImage3DTiles.etc1; if (context.s3tc && defined(crunch)) { mimeType = crunch.mimeType; if (defined(crunch.bufferView)) { bufferViewId = crunch.bufferView; } else { uri = crunch.uri; } } else if (context.s3tc && defined(s3tc)) { mimeType = s3tc.mimeType; if (defined(s3tc.bufferView)) { bufferViewId = s3tc.bufferView; } else { uri = s3tc.uri; } } else if (context.pvrtc && defined(pvrtc)) { mimeType = pvrtc.mimeType; if (defined(pvrtc.bufferView)) { bufferViewId = pvrtc.bufferView; } else { uri = pvrtc.uri; } } else if (context.etc1 && defined(etc1)) { mimeType = etc1.mimeType; if (defined(etc1.bufferView)) { bufferViewId = etc1.bufferView; } else { uri = etc1.uri; } } } // Image references either uri (external or base64-encoded) or bufferView if (defined(bufferViewId)) { model._loadResources.texturesToCreateFromBufferView.enqueue({ id: id, image: undefined, bufferView: bufferViewId, mimeType: mimeType, }); } else { ++model._loadResources.pendingTextureLoads; var imageResource = model._resource.getDerivedResource({ url: uri, }); var promise; if (ktxRegex$1.test(uri)) { promise = loadKTX(imageResource); } else if (crnRegex$1.test(uri)) { promise = loadCRN(imageResource); } else { promise = imageResource.fetchImage(); } promise .then(imageLoad(model, id)) .otherwise( ModelUtility.getFailedLoadFunction(model, "image", imageResource.url) ); } }); } var scratchArticulationStageInitialTransform = new Matrix4(); function parseNodes(model) { var runtimeNodes = {}; var runtimeNodesByName = {}; var skinnedNodes = []; var skinnedNodesIds = model._loadResources.skinnedNodesIds; var articulationsByName = model._runtime.articulationsByName; ForEach.node(model.gltf, function (node, id) { var runtimeNode = { // Animation targets matrix: undefined, translation: undefined, rotation: undefined, scale: undefined, // Per-node show inherited from parent computedShow: true, // Computed transforms transformToRoot: new Matrix4(), computedMatrix: new Matrix4(), dirtyNumber: 0, // The frame this node was made dirty by an animation; for graph traversal // Rendering commands: [], // empty for transform, light, and camera nodes // Skinned node inverseBindMatrices: undefined, // undefined when node is not skinned bindShapeMatrix: undefined, // undefined when node is not skinned or identity joints: [], // empty when node is not skinned computedJointMatrices: [], // empty when node is not skinned // Joint node jointName: node.jointName, // undefined when node is not a joint weights: [], // Graph pointers children: [], // empty for leaf nodes parents: [], // empty for root nodes // Publicly-accessible ModelNode instance to modify animation targets publicNode: undefined, }; runtimeNode.publicNode = new ModelNode( model, node, runtimeNode, id, ModelUtility.getTransform(node) ); runtimeNodes[id] = runtimeNode; runtimeNodesByName[node.name] = runtimeNode; if (defined(node.skin)) { skinnedNodesIds.push(id); skinnedNodes.push(runtimeNode); } if ( defined(node.extensions) && defined(node.extensions.AGI_articulations) ) { var articulationName = node.extensions.AGI_articulations.articulationName; if (defined(articulationName)) { var transform = Matrix4.clone( runtimeNode.publicNode.originalMatrix, scratchArticulationStageInitialTransform ); var articulation = articulationsByName[articulationName]; articulation.nodes.push(runtimeNode.publicNode); var numStages = articulation.stages.length; for (var s = 0; s < numStages; ++s) { var stage = articulation.stages[s]; transform = applyArticulationStageMatrix(stage, transform); } runtimeNode.publicNode.matrix = transform; } } }); model._runtime.nodes = runtimeNodes; model._runtime.nodesByName = runtimeNodesByName; model._runtime.skinnedNodes = skinnedNodes; } function parseMaterials(model) { var gltf = model.gltf; var techniques = model._sourceTechniques; var runtimeMaterialsByName = {}; var runtimeMaterialsById = {}; var uniformMaps = model._uniformMaps; ForEach.material(gltf, function (material, materialId) { // Allocated now so ModelMaterial can keep a reference to it. uniformMaps[materialId] = { uniformMap: undefined, values: undefined, jointMatrixUniformName: undefined, morphWeightsUniformName: undefined, }; var modelMaterial = new ModelMaterial(model, material, materialId); if ( defined(material.extensions) && defined(material.extensions.KHR_techniques_webgl) ) { var techniqueId = material.extensions.KHR_techniques_webgl.technique; modelMaterial._technique = techniqueId; modelMaterial._program = techniques[techniqueId].program; ForEach.materialValue(material, function (value, uniformName) { if (!defined(modelMaterial._values)) { modelMaterial._values = {}; } modelMaterial._values[uniformName] = clone$1(value); }); } runtimeMaterialsByName[material.name] = modelMaterial; runtimeMaterialsById[materialId] = modelMaterial; }); model._runtime.materialsByName = runtimeMaterialsByName; model._runtime.materialsById = runtimeMaterialsById; } function parseMeshes(model) { var runtimeMeshesByName = {}; var runtimeMaterialsById = model._runtime.materialsById; ForEach.mesh(model.gltf, function (mesh, meshId) { runtimeMeshesByName[mesh.name] = new ModelMesh( mesh, runtimeMaterialsById, meshId ); if ( defined(model.extensionsUsed.WEB3D_quantized_attributes) || model._dequantizeInShader ) { // Cache primitives according to their program ForEach.meshPrimitive(mesh, function (primitive, primitiveId) { var programId = getProgramForPrimitive(model, primitive); var programPrimitives = model._programPrimitives[programId]; if (!defined(programPrimitives)) { programPrimitives = {}; model._programPrimitives[programId] = programPrimitives; } programPrimitives[meshId + ".primitive." + primitiveId] = primitive; }); } }); model._runtime.meshesByName = runtimeMeshesByName; } /////////////////////////////////////////////////////////////////////////// var CreateVertexBufferJob = function () { this.id = undefined; this.model = undefined; this.context = undefined; }; CreateVertexBufferJob.prototype.set = function (id, model, context) { this.id = id; this.model = model; this.context = context; }; CreateVertexBufferJob.prototype.execute = function () { createVertexBuffer$1(this.id, this.model, this.context); }; /////////////////////////////////////////////////////////////////////////// function createVertexBuffer$1(bufferViewId, model, context) { var loadResources = model._loadResources; var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; // Use bufferView created at runtime if (!defined(bufferView)) { bufferView = loadResources.createdBufferViews[bufferViewId]; } var vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: loadResources.getBuffer(bufferView), usage: BufferUsage$1.STATIC_DRAW, }); vertexBuffer.vertexArrayDestroyable = false; model._rendererResources.buffers[bufferViewId] = vertexBuffer; model._geometryByteLength += vertexBuffer.sizeInBytes; } /////////////////////////////////////////////////////////////////////////// var CreateIndexBufferJob = function () { this.id = undefined; this.componentType = undefined; this.model = undefined; this.context = undefined; }; CreateIndexBufferJob.prototype.set = function ( id, componentType, model, context ) { this.id = id; this.componentType = componentType; this.model = model; this.context = context; }; CreateIndexBufferJob.prototype.execute = function () { createIndexBuffer(this.id, this.componentType, this.model, this.context); }; /////////////////////////////////////////////////////////////////////////// function createIndexBuffer(bufferViewId, componentType, model, context) { var loadResources = model._loadResources; var bufferViews = model.gltf.bufferViews; var bufferView = bufferViews[bufferViewId]; // Use bufferView created at runtime if (!defined(bufferView)) { bufferView = loadResources.createdBufferViews[bufferViewId]; } var indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: loadResources.getBuffer(bufferView), usage: BufferUsage$1.STATIC_DRAW, indexDatatype: componentType, }); indexBuffer.vertexArrayDestroyable = false; model._rendererResources.buffers[bufferViewId] = indexBuffer; model._geometryByteLength += indexBuffer.sizeInBytes; } var scratchVertexBufferJob = new CreateVertexBufferJob(); var scratchIndexBufferJob = new CreateIndexBufferJob(); function createBuffers(model, frameState) { var loadResources = model._loadResources; if (loadResources.pendingBufferLoads !== 0) { return; } var context = frameState.context; var vertexBuffersToCreate = loadResources.vertexBuffersToCreate; var indexBuffersToCreate = loadResources.indexBuffersToCreate; var i; if (model.asynchronous) { while (vertexBuffersToCreate.length > 0) { scratchVertexBufferJob.set(vertexBuffersToCreate.peek(), model, context); if ( !frameState.jobScheduler.execute(scratchVertexBufferJob, JobType$1.BUFFER) ) { break; } vertexBuffersToCreate.dequeue(); } while (indexBuffersToCreate.length > 0) { i = indexBuffersToCreate.peek(); scratchIndexBufferJob.set(i.id, i.componentType, model, context); if ( !frameState.jobScheduler.execute(scratchIndexBufferJob, JobType$1.BUFFER) ) { break; } indexBuffersToCreate.dequeue(); } } else { while (vertexBuffersToCreate.length > 0) { createVertexBuffer$1(vertexBuffersToCreate.dequeue(), model, context); } while (indexBuffersToCreate.length > 0) { i = indexBuffersToCreate.dequeue(); createIndexBuffer(i.id, i.componentType, model, context); } } } function getProgramForPrimitive(model, primitive) { var material = model._runtime.materialsById[primitive.material]; if (!defined(material)) { return; } return material._program; } function modifyShaderForQuantizedAttributes(shader, programName, model) { var primitive; var primitives = model._programPrimitives[programName]; // If no primitives were cached for this program, there's no need to modify the shader if (!defined(primitives)) { return shader; } var primitiveId; for (primitiveId in primitives) { if (primitives.hasOwnProperty(primitiveId)) { primitive = primitives[primitiveId]; if (getProgramForPrimitive(model, primitive) === programName) { break; } } } // This is not needed after the program is processed, free the memory model._programPrimitives[programName] = undefined; var result; if (model.extensionsUsed.WEB3D_quantized_attributes) { result = ModelUtility.modifyShaderForQuantizedAttributes( model.gltf, primitive, shader ); model._quantizedUniforms[programName] = result.uniforms; } else { var decodedData = model._decodedData[primitiveId]; if (defined(decodedData)) { result = ModelUtility.modifyShaderForDracoQuantizedAttributes( model.gltf, primitive, shader, decodedData.attributes ); } else { return shader; } } return result.shader; } function modifyShaderForColor(shader) { shader = ShaderSource.replaceMain(shader, "gltf_blend_main"); shader += "uniform vec4 gltf_color; \n" + "uniform float gltf_colorBlend; \n" + "void main() \n" + "{ \n" + " gltf_blend_main(); \n" + " gl_FragColor.rgb = mix(gl_FragColor.rgb, gltf_color.rgb, gltf_colorBlend); \n" + " float highlight = ceil(gltf_colorBlend); \n" + " gl_FragColor.rgb *= mix(gltf_color.rgb, vec3(1.0), highlight); \n" + " gl_FragColor.a *= gltf_color.a; \n" + "} \n"; return shader; } function modifyShader(shader, programName, callback) { if (defined(callback)) { shader = callback(shader, programName); } return shader; } var CreateProgramJob = function () { this.programToCreate = undefined; this.model = undefined; this.context = undefined; }; CreateProgramJob.prototype.set = function (programToCreate, model, context) { this.programToCreate = programToCreate; this.model = model; this.context = context; }; CreateProgramJob.prototype.execute = function () { createProgram(this.programToCreate, this.model, this.context); }; /////////////////////////////////////////////////////////////////////////// // When building programs for the first time, do not include modifiers for clipping planes and color // since this is the version of the program that will be cached for use with other Models. function createProgram(programToCreate, model, context) { var programId = programToCreate.programId; var techniqueId = programToCreate.techniqueId; var program = model._sourcePrograms[programId]; var shaders = model._rendererResources.sourceShaders; var vs = shaders[program.vertexShader]; var fs = shaders[program.fragmentShader]; var quantizedVertexShaders = model._quantizedVertexShaders; if ( model.extensionsUsed.WEB3D_quantized_attributes || model._dequantizeInShader ) { var quantizedVS = quantizedVertexShaders[programId]; if (!defined(quantizedVS)) { quantizedVS = modifyShaderForQuantizedAttributes(vs, programId, model); quantizedVertexShaders[programId] = quantizedVS; } vs = quantizedVS; } var drawVS = modifyShader(vs, programId, model._vertexShaderLoaded); var drawFS = modifyShader(fs, programId, model._fragmentShaderLoaded); if (!defined(model._uniformMapLoaded)) { drawFS = "uniform vec4 czm_pickColor;\n" + drawFS; } var useIBL = model._imageBasedLightingFactor.x > 0.0 || model._imageBasedLightingFactor.y > 0.0; if (useIBL) { drawFS = "#define USE_IBL_LIGHTING \n\n" + drawFS; } if (defined(model._lightColor)) { drawFS = "#define USE_CUSTOM_LIGHT_COLOR \n\n" + drawFS; } if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) { drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main"); drawFS = drawFS + "\n" + "void main() { \n" + " non_gamma_corrected_main(); \n" + " gl_FragColor = czm_gammaCorrect(gl_FragColor); \n" + "} \n"; } if (OctahedralProjectedCubeMap.isSupported(context)) { var usesSH = defined(model._sphericalHarmonicCoefficients) || model._useDefaultSphericalHarmonics; var usesSM = (defined(model._specularEnvironmentMapAtlas) && model._specularEnvironmentMapAtlas.ready) || model._useDefaultSpecularMaps; var addMatrix = usesSH || usesSM || useIBL; if (addMatrix) { drawFS = "uniform mat3 gltf_iblReferenceFrameMatrix; \n" + drawFS; } if (defined(model._sphericalHarmonicCoefficients)) { drawFS = "#define DIFFUSE_IBL \n" + "#define CUSTOM_SPHERICAL_HARMONICS \n" + "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n" + drawFS; } else if (model._useDefaultSphericalHarmonics) { drawFS = "#define DIFFUSE_IBL \n" + drawFS; } if ( defined(model._specularEnvironmentMapAtlas) && model._specularEnvironmentMapAtlas.ready ) { drawFS = "#define SPECULAR_IBL \n" + "#define CUSTOM_SPECULAR_IBL \n" + "uniform sampler2D gltf_specularMap; \n" + "uniform vec2 gltf_specularMapSize; \n" + "uniform float gltf_maxSpecularLOD; \n" + drawFS; } else if (model._useDefaultSpecularMaps) { drawFS = "#define SPECULAR_IBL \n" + drawFS; } } if (defined(model._luminanceAtZenith)) { drawFS = "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n" + drawFS; } createAttributesAndProgram( programId, techniqueId, drawFS, drawVS, model, context ); } function recreateProgram(programToCreate, model, context) { var programId = programToCreate.programId; var techniqueId = programToCreate.techniqueId; var program = model._sourcePrograms[programId]; var shaders = model._rendererResources.sourceShaders; var quantizedVertexShaders = model._quantizedVertexShaders; var clippingPlaneCollection = model.clippingPlanes; var addClippingPlaneCode = isClippingEnabled(model); var vs = shaders[program.vertexShader]; var fs = shaders[program.fragmentShader]; if ( model.extensionsUsed.WEB3D_quantized_attributes || model._dequantizeInShader ) { vs = quantizedVertexShaders[programId]; } var finalFS = fs; if (isColorShadingEnabled(model)) { finalFS = Model._modifyShaderForColor(finalFS); } if (addClippingPlaneCode) { finalFS = modifyShaderForClippingPlanes( finalFS, clippingPlaneCollection, context ); } var drawVS = modifyShader(vs, programId, model._vertexShaderLoaded); var drawFS = modifyShader(finalFS, programId, model._fragmentShaderLoaded); if (!defined(model._uniformMapLoaded)) { drawFS = "uniform vec4 czm_pickColor;\n" + drawFS; } var useIBL = model._imageBasedLightingFactor.x > 0.0 || model._imageBasedLightingFactor.y > 0.0; if (useIBL) { drawFS = "#define USE_IBL_LIGHTING \n\n" + drawFS; } if (defined(model._lightColor)) { drawFS = "#define USE_CUSTOM_LIGHT_COLOR \n\n" + drawFS; } if (model._sourceVersion !== "2.0" || model._sourceKHRTechniquesWebGL) { drawFS = ShaderSource.replaceMain(drawFS, "non_gamma_corrected_main"); drawFS = drawFS + "\n" + "void main() { \n" + " non_gamma_corrected_main(); \n" + " gl_FragColor = czm_gammaCorrect(gl_FragColor); \n" + "} \n"; } if (OctahedralProjectedCubeMap.isSupported(context)) { var usesSH = defined(model._sphericalHarmonicCoefficients) || model._useDefaultSphericalHarmonics; var usesSM = (defined(model._specularEnvironmentMapAtlas) && model._specularEnvironmentMapAtlas.ready) || model._useDefaultSpecularMaps; var addMatrix = usesSH || usesSM || useIBL; if (addMatrix) { drawFS = "uniform mat3 gltf_iblReferenceFrameMatrix; \n" + drawFS; } if (defined(model._sphericalHarmonicCoefficients)) { drawFS = "#define DIFFUSE_IBL \n" + "#define CUSTOM_SPHERICAL_HARMONICS \n" + "uniform vec3 gltf_sphericalHarmonicCoefficients[9]; \n" + drawFS; } else if (model._useDefaultSphericalHarmonics) { drawFS = "#define DIFFUSE_IBL \n" + drawFS; } if ( defined(model._specularEnvironmentMapAtlas) && model._specularEnvironmentMapAtlas.ready ) { drawFS = "#define SPECULAR_IBL \n" + "#define CUSTOM_SPECULAR_IBL \n" + "uniform sampler2D gltf_specularMap; \n" + "uniform vec2 gltf_specularMapSize; \n" + "uniform float gltf_maxSpecularLOD; \n" + drawFS; } else if (model._useDefaultSpecularMaps) { drawFS = "#define SPECULAR_IBL \n" + drawFS; } } if (defined(model._luminanceAtZenith)) { drawFS = "#define USE_SUN_LUMINANCE \n" + "uniform float gltf_luminanceAtZenith;\n" + drawFS; } createAttributesAndProgram( programId, techniqueId, drawFS, drawVS, model, context ); } function createAttributesAndProgram( programId, techniqueId, drawFS, drawVS, model, context ) { var technique = model._sourceTechniques[techniqueId]; var attributeLocations = ModelUtility.createAttributeLocations( technique, model._precreatedAttributes ); model._rendererResources.programs[programId] = ShaderProgram.fromCache({ context: context, vertexShaderSource: drawVS, fragmentShaderSource: drawFS, attributeLocations: attributeLocations, }); } var scratchCreateProgramJob = new CreateProgramJob(); function createPrograms(model, frameState) { var loadResources = model._loadResources; var programsToCreate = loadResources.programsToCreate; if (loadResources.pendingShaderLoads !== 0) { return; } // PERFORMANCE_IDEA: this could be more fine-grained by looking // at the shader's bufferView's to determine the buffer dependencies. if (loadResources.pendingBufferLoads !== 0) { return; } var context = frameState.context; if (model.asynchronous) { while (programsToCreate.length > 0) { scratchCreateProgramJob.set(programsToCreate.peek(), model, context); if ( !frameState.jobScheduler.execute( scratchCreateProgramJob, JobType$1.PROGRAM ) ) { break; } programsToCreate.dequeue(); } } else { // Create all loaded programs this frame while (programsToCreate.length > 0) { createProgram(programsToCreate.dequeue(), model, context); } } } function getOnImageCreatedFromTypedArray(loadResources, gltfTexture) { return function (image) { loadResources.texturesToCreate.enqueue({ id: gltfTexture.id, image: image, bufferView: undefined, }); --loadResources.pendingBufferViewToImage; }; } function loadTexturesFromBufferViews(model) { var loadResources = model._loadResources; if (loadResources.pendingBufferLoads !== 0) { return; } while (loadResources.texturesToCreateFromBufferView.length > 0) { var gltfTexture = loadResources.texturesToCreateFromBufferView.dequeue(); var gltf = model.gltf; var bufferView = gltf.bufferViews[gltfTexture.bufferView]; gltf.textures[gltfTexture.id].source; var onerror = ModelUtility.getFailedLoadFunction( model, "image", "id: " + gltfTexture.id + ", bufferView: " + gltfTexture.bufferView ); if (gltfTexture.mimeType === "image/ktx") { loadKTX(loadResources.getBuffer(bufferView)) .then(imageLoad(model, gltfTexture.id)) .otherwise(onerror); ++model._loadResources.pendingTextureLoads; } else if (gltfTexture.mimeType === "image/crn") { loadCRN(loadResources.getBuffer(bufferView)) .then(imageLoad(model, gltfTexture.id)) .otherwise(onerror); ++model._loadResources.pendingTextureLoads; } else { var onload = getOnImageCreatedFromTypedArray(loadResources, gltfTexture); loadImageFromTypedArray({ uint8Array: loadResources.getBuffer(bufferView), format: gltfTexture.mimeType, flipY: false, }) .then(onload) .otherwise(onerror); ++loadResources.pendingBufferViewToImage; } } } function createSamplers(model) { var loadResources = model._loadResources; if (loadResources.createSamplers) { loadResources.createSamplers = false; var rendererSamplers = model._rendererResources.samplers; ForEach.sampler(model.gltf, function (sampler, samplerId) { rendererSamplers[samplerId] = new Sampler({ wrapS: sampler.wrapS, wrapT: sampler.wrapT, minificationFilter: sampler.minFilter, magnificationFilter: sampler.magFilter, }); }); } } /////////////////////////////////////////////////////////////////////////// var CreateTextureJob = function () { this.gltfTexture = undefined; this.model = undefined; this.context = undefined; }; CreateTextureJob.prototype.set = function (gltfTexture, model, context) { this.gltfTexture = gltfTexture; this.model = model; this.context = context; }; CreateTextureJob.prototype.execute = function () { createTexture(this.gltfTexture, this.model, this.context); }; /////////////////////////////////////////////////////////////////////////// function createTexture(gltfTexture, model, context) { var textures = model.gltf.textures; var texture = textures[gltfTexture.id]; var rendererSamplers = model._rendererResources.samplers; var sampler = rendererSamplers[texture.sampler]; if (!defined(sampler)) { sampler = new Sampler({ wrapS: TextureWrap$1.REPEAT, wrapT: TextureWrap$1.REPEAT, }); } var usesTextureTransform = false; var materials = model.gltf.materials; var materialsLength = materials.length; for (var i = 0; i < materialsLength; ++i) { var material = materials[i]; if ( defined(material.extensions) && defined(material.extensions.KHR_techniques_webgl) ) { var values = material.extensions.KHR_techniques_webgl.values; for (var valueName in values) { if ( values.hasOwnProperty(valueName) && valueName.indexOf("Texture") !== -1 ) { var value = values[valueName]; if ( value.index === gltfTexture.id && defined(value.extensions) && defined(value.extensions.KHR_texture_transform) ) { usesTextureTransform = true; break; } } } } if (usesTextureTransform) { break; } } var wrapS = sampler.wrapS; var wrapT = sampler.wrapT; var minFilter = sampler.minificationFilter; if ( usesTextureTransform && minFilter !== TextureMinificationFilter$1.LINEAR && minFilter !== TextureMinificationFilter$1.NEAREST ) { if ( minFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST || minFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_LINEAR ) { minFilter = TextureMinificationFilter$1.NEAREST; } else { minFilter = TextureMinificationFilter$1.LINEAR; } sampler = new Sampler({ wrapS: sampler.wrapS, wrapT: sampler.wrapT, textureMinificationFilter: minFilter, textureMagnificationFilter: sampler.magnificationFilter, }); } var internalFormat = gltfTexture.internalFormat; var mipmap = !( defined(internalFormat) && PixelFormat$1.isCompressedFormat(internalFormat) ) && (minFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_NEAREST || minFilter === TextureMinificationFilter$1.NEAREST_MIPMAP_LINEAR || minFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_NEAREST || minFilter === TextureMinificationFilter$1.LINEAR_MIPMAP_LINEAR); var requiresNpot = mipmap || wrapS === TextureWrap$1.REPEAT || wrapS === TextureWrap$1.MIRRORED_REPEAT || wrapT === TextureWrap$1.REPEAT || wrapT === TextureWrap$1.MIRRORED_REPEAT; var tx; var source = gltfTexture.image; if (defined(internalFormat)) { tx = new Texture({ context: context, source: { arrayBufferView: gltfTexture.bufferView, }, width: gltfTexture.width, height: gltfTexture.height, pixelFormat: internalFormat, sampler: sampler, }); } else if (defined(source)) { var npot = !CesiumMath.isPowerOfTwo(source.width) || !CesiumMath.isPowerOfTwo(source.height); if (requiresNpot && npot) { // WebGL requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes. var canvas = document.createElement("canvas"); canvas.width = CesiumMath.nextPowerOfTwo(source.width); canvas.height = CesiumMath.nextPowerOfTwo(source.height); var canvasContext = canvas.getContext("2d"); canvasContext.drawImage( source, 0, 0, source.width, source.height, 0, 0, canvas.width, canvas.height ); source = canvas; } tx = new Texture({ context: context, source: source, pixelFormat: texture.internalFormat, pixelDatatype: texture.type, sampler: sampler, flipY: false, }); // GLTF_SPEC: Support TEXTURE_CUBE_MAP. https://github.com/KhronosGroup/glTF/issues/40 if (mipmap) { tx.generateMipmap(); } } if (defined(tx)) { model._rendererResources.textures[gltfTexture.id] = tx; model._texturesByteLength += tx.sizeInBytes; } } var scratchCreateTextureJob = new CreateTextureJob(); function createTextures$2(model, frameState) { var context = frameState.context; var texturesToCreate = model._loadResources.texturesToCreate; if (model.asynchronous) { while (texturesToCreate.length > 0) { scratchCreateTextureJob.set(texturesToCreate.peek(), model, context); if ( !frameState.jobScheduler.execute( scratchCreateTextureJob, JobType$1.TEXTURE ) ) { break; } texturesToCreate.dequeue(); } } else { // Create all loaded textures this frame while (texturesToCreate.length > 0) { createTexture(texturesToCreate.dequeue(), model, context); } } } function getAttributeLocations$1(model, primitive) { var techniques = model._sourceTechniques; // Retrieve the compiled shader program to assign index values to attributes var attributeLocations = {}; var location; var index; var material = model._runtime.materialsById[primitive.material]; if (!defined(material)) { return attributeLocations; } var technique = techniques[material._technique]; if (!defined(technique)) { return attributeLocations; } var attributes = technique.attributes; var program = model._rendererResources.programs[technique.program]; var programAttributeLocations = program._attributeLocations; for (location in programAttributeLocations) { if (programAttributeLocations.hasOwnProperty(location)) { var attribute = attributes[location]; if (defined(attribute)) { index = programAttributeLocations[location]; attributeLocations[attribute.semantic] = index; } } } // Add pre-created attributes. var precreatedAttributes = model._precreatedAttributes; if (defined(precreatedAttributes)) { for (location in precreatedAttributes) { if (precreatedAttributes.hasOwnProperty(location)) { index = programAttributeLocations[location]; attributeLocations[location] = index; } } } return attributeLocations; } function createJoints(model, runtimeSkins) { var gltf = model.gltf; var skins = gltf.skins; var nodes = gltf.nodes; var runtimeNodes = model._runtime.nodes; var skinnedNodesIds = model._loadResources.skinnedNodesIds; var length = skinnedNodesIds.length; for (var j = 0; j < length; ++j) { var id = skinnedNodesIds[j]; var skinnedNode = runtimeNodes[id]; var node = nodes[id]; var runtimeSkin = runtimeSkins[node.skin]; skinnedNode.inverseBindMatrices = runtimeSkin.inverseBindMatrices; skinnedNode.bindShapeMatrix = runtimeSkin.bindShapeMatrix; var gltfJoints = skins[node.skin].joints; var jointsLength = gltfJoints.length; for (var i = 0; i < jointsLength; ++i) { var nodeId = gltfJoints[i]; var jointNode = runtimeNodes[nodeId]; skinnedNode.joints.push(jointNode); } } } function createSkins(model) { var loadResources = model._loadResources; if (loadResources.pendingBufferLoads !== 0) { return; } if (!loadResources.createSkins) { return; } loadResources.createSkins = false; var gltf = model.gltf; var accessors = gltf.accessors; var runtimeSkins = {}; ForEach.skin(gltf, function (skin, id) { var accessor = accessors[skin.inverseBindMatrices]; var bindShapeMatrix; if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) { bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix); } runtimeSkins[id] = { inverseBindMatrices: ModelAnimationCache.getSkinInverseBindMatrices( model, accessor ), bindShapeMatrix: bindShapeMatrix, // not used when undefined }; }); createJoints(model, runtimeSkins); } function getChannelEvaluator(model, runtimeNode, targetPath, spline) { return function (localAnimationTime) { if (defined(spline)) { localAnimationTime = model.clampAnimations ? spline.clampTime(localAnimationTime) : spline.wrapTime(localAnimationTime); runtimeNode[targetPath] = spline.evaluate( localAnimationTime, runtimeNode[targetPath] ); runtimeNode.dirtyNumber = model._maxDirtyNumber; } }; } function createRuntimeAnimations(model) { var loadResources = model._loadResources; if (!loadResources.finishedPendingBufferLoads()) { return; } if (!loadResources.createRuntimeAnimations) { return; } loadResources.createRuntimeAnimations = false; model._runtime.animations = []; var runtimeNodes = model._runtime.nodes; var accessors = model.gltf.accessors; ForEach.animation(model.gltf, function (animation, i) { var channels = animation.channels; var samplers = animation.samplers; // Find start and stop time for the entire animation var startTime = Number.MAX_VALUE; var stopTime = -Number.MAX_VALUE; var channelsLength = channels.length; var channelEvaluators = new Array(channelsLength); for (var j = 0; j < channelsLength; ++j) { var channel = channels[j]; var target = channel.target; var path = target.path; var sampler = samplers[channel.sampler]; var input = ModelAnimationCache.getAnimationParameterValues( model, accessors[sampler.input] ); var output = ModelAnimationCache.getAnimationParameterValues( model, accessors[sampler.output] ); startTime = Math.min(startTime, input[0]); stopTime = Math.max(stopTime, input[input.length - 1]); var spline = ModelAnimationCache.getAnimationSpline( model, i, animation, channel.sampler, sampler, input, path, output ); channelEvaluators[j] = getChannelEvaluator( model, runtimeNodes[target.node], target.path, spline ); } model._runtime.animations[i] = { name: animation.name, startTime: startTime, stopTime: stopTime, channelEvaluators: channelEvaluators, }; }); } function createVertexArrays$1(model, context) { var loadResources = model._loadResources; if ( !loadResources.finishedBuffersCreation() || !loadResources.finishedProgramCreation() || !loadResources.createVertexArrays ) { return; } loadResources.createVertexArrays = false; var rendererBuffers = model._rendererResources.buffers; var rendererVertexArrays = model._rendererResources.vertexArrays; var gltf = model.gltf; var accessors = gltf.accessors; ForEach.mesh(gltf, function (mesh, meshId) { ForEach.meshPrimitive(mesh, function (primitive, primitiveId) { var attributes = []; var attributeLocation; var attributeLocations = getAttributeLocations$1(model, primitive); var decodedData = model._decodedData[meshId + ".primitive." + primitiveId]; ForEach.meshPrimitiveAttribute(primitive, function ( accessorId, attributeName ) { // Skip if the attribute is not used by the material, e.g., because the asset // was exported with an attribute that wasn't used and the asset wasn't optimized. attributeLocation = attributeLocations[attributeName]; if (defined(attributeLocation)) { // Use attributes of previously decoded draco geometry if (defined(decodedData)) { var decodedAttributes = decodedData.attributes; if (decodedAttributes.hasOwnProperty(attributeName)) { var decodedAttribute = decodedAttributes[attributeName]; attributes.push({ index: attributeLocation, vertexBuffer: rendererBuffers[decodedAttribute.bufferView], componentsPerAttribute: decodedAttribute.componentsPerAttribute, componentDatatype: decodedAttribute.componentDatatype, normalize: decodedAttribute.normalized, offsetInBytes: decodedAttribute.byteOffset, strideInBytes: decodedAttribute.byteStride, }); return; } } var a = accessors[accessorId]; var normalize = defined(a.normalized) && a.normalized; attributes.push({ index: attributeLocation, vertexBuffer: rendererBuffers[a.bufferView], componentsPerAttribute: numberOfComponentsForType(a.type), componentDatatype: a.componentType, normalize: normalize, offsetInBytes: a.byteOffset, strideInBytes: getAccessorByteStride(gltf, a), }); } }); // Add pre-created attributes var attribute; var attributeName; var precreatedAttributes = model._precreatedAttributes; if (defined(precreatedAttributes)) { for (attributeName in precreatedAttributes) { if (precreatedAttributes.hasOwnProperty(attributeName)) { attributeLocation = attributeLocations[attributeName]; if (defined(attributeLocation)) { attribute = precreatedAttributes[attributeName]; attribute.index = attributeLocation; attributes.push(attribute); } } } } var indexBuffer; if (defined(primitive.indices)) { var accessor = accessors[primitive.indices]; var bufferView = accessor.bufferView; // Use buffer of previously decoded draco geometry if (defined(decodedData)) { bufferView = decodedData.bufferView; } indexBuffer = rendererBuffers[bufferView]; } rendererVertexArrays[ meshId + ".primitive." + primitiveId ] = new VertexArray({ context: context, attributes: attributes, indexBuffer: indexBuffer, }); }); }); } function createRenderStates$2(model) { var loadResources = model._loadResources; if (loadResources.createRenderStates) { loadResources.createRenderStates = false; ForEach.material(model.gltf, function (material, materialId) { createRenderStateForMaterial(model, material, materialId); }); } } function createRenderStateForMaterial(model, material, materialId) { var rendererRenderStates = model._rendererResources.renderStates; var blendEquationSeparate = [ WebGLConstants$1.FUNC_ADD, WebGLConstants$1.FUNC_ADD, ]; var blendFuncSeparate = [ WebGLConstants$1.ONE, WebGLConstants$1.ONE_MINUS_SRC_ALPHA, WebGLConstants$1.ONE, WebGLConstants$1.ONE_MINUS_SRC_ALPHA, ]; if (defined(material.extensions) && defined(material.extensions.KHR_blend)) { blendEquationSeparate = material.extensions.KHR_blend.blendEquation; blendFuncSeparate = material.extensions.KHR_blend.blendFactors; } var enableCulling = !material.doubleSided; var blendingEnabled = material.alphaMode === "BLEND"; rendererRenderStates[materialId] = RenderState.fromCache({ cull: { enabled: enableCulling, }, depthTest: { enabled: true, func: DepthFunction$1.LESS_OR_EQUAL, }, depthMask: !blendingEnabled, blending: { enabled: blendingEnabled, equationRgb: blendEquationSeparate[0], equationAlpha: blendEquationSeparate[1], functionSourceRgb: blendFuncSeparate[0], functionDestinationRgb: blendFuncSeparate[1], functionSourceAlpha: blendFuncSeparate[2], functionDestinationAlpha: blendFuncSeparate[3], }, }); } /////////////////////////////////////////////////////////////////////////// var gltfUniformsFromNode = { MODEL: function (uniformState, model, runtimeNode) { return function () { return runtimeNode.computedMatrix; }; }, VIEW: function (uniformState, model, runtimeNode) { return function () { return uniformState.view; }; }, PROJECTION: function (uniformState, model, runtimeNode) { return function () { return uniformState.projection; }; }, MODELVIEW: function (uniformState, model, runtimeNode) { var mv = new Matrix4(); return function () { return Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mv ); }; }, CESIUM_RTC_MODELVIEW: function (uniformState, model, runtimeNode) { // CESIUM_RTC extension var mvRtc = new Matrix4(); return function () { Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mvRtc ); return Matrix4.setTranslation(mvRtc, model._rtcCenterEye, mvRtc); }; }, MODELVIEWPROJECTION: function (uniformState, model, runtimeNode) { var mvp = new Matrix4(); return function () { Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mvp ); return Matrix4.multiply(uniformState._projection, mvp, mvp); }; }, MODELINVERSE: function (uniformState, model, runtimeNode) { var mInverse = new Matrix4(); return function () { return Matrix4.inverse(runtimeNode.computedMatrix, mInverse); }; }, VIEWINVERSE: function (uniformState, model) { return function () { return uniformState.inverseView; }; }, PROJECTIONINVERSE: function (uniformState, model, runtimeNode) { return function () { return uniformState.inverseProjection; }; }, MODELVIEWINVERSE: function (uniformState, model, runtimeNode) { var mv = new Matrix4(); var mvInverse = new Matrix4(); return function () { Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mv ); return Matrix4.inverse(mv, mvInverse); }; }, MODELVIEWPROJECTIONINVERSE: function (uniformState, model, runtimeNode) { var mvp = new Matrix4(); var mvpInverse = new Matrix4(); return function () { Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mvp ); Matrix4.multiply(uniformState._projection, mvp, mvp); return Matrix4.inverse(mvp, mvpInverse); }; }, MODELINVERSETRANSPOSE: function (uniformState, model, runtimeNode) { var mInverse = new Matrix4(); var mInverseTranspose = new Matrix3(); return function () { Matrix4.inverse(runtimeNode.computedMatrix, mInverse); Matrix4.getMatrix3(mInverse, mInverseTranspose); return Matrix3.transpose(mInverseTranspose, mInverseTranspose); }; }, MODELVIEWINVERSETRANSPOSE: function (uniformState, model, runtimeNode) { var mv = new Matrix4(); var mvInverse = new Matrix4(); var mvInverseTranspose = new Matrix3(); return function () { Matrix4.multiplyTransformation( uniformState.view, runtimeNode.computedMatrix, mv ); Matrix4.inverse(mv, mvInverse); Matrix4.getMatrix3(mvInverse, mvInverseTranspose); return Matrix3.transpose(mvInverseTranspose, mvInverseTranspose); }; }, VIEWPORT: function (uniformState, model, runtimeNode) { return function () { return uniformState.viewportCartesian4; }; }, }; function getUniformFunctionFromSource(source, model, semantic, uniformState) { var runtimeNode = model._runtime.nodes[source]; return gltfUniformsFromNode[semantic](uniformState, model, runtimeNode); } function createUniformsForMaterial( model, material, technique, instanceValues, context, textures, defaultTexture ) { var uniformMap = {}; var uniformValues = {}; var jointMatrixUniformName; var morphWeightsUniformName; ForEach.techniqueUniform(technique, function (uniform, uniformName) { // GLTF_SPEC: This does not take into account uniform arrays, // indicated by uniforms with a count property. // // https://github.com/KhronosGroup/glTF/issues/258 // GLTF_SPEC: In this implementation, material parameters with a // semantic or targeted via a source (for animation) are not // targetable for material animations. Is this too strict? // // https://github.com/KhronosGroup/glTF/issues/142 var uv; if (defined(instanceValues) && defined(instanceValues[uniformName])) { // Parameter overrides by the instance technique uv = ModelUtility.createUniformFunction( uniform.type, instanceValues[uniformName], textures, defaultTexture ); uniformMap[uniformName] = uv.func; uniformValues[uniformName] = uv; } else if (defined(uniform.node)) { uniformMap[uniformName] = getUniformFunctionFromSource( uniform.node, model, uniform.semantic, context.uniformState ); } else if (defined(uniform.semantic)) { if (uniform.semantic === "JOINTMATRIX") { jointMatrixUniformName = uniformName; } else if (uniform.semantic === "MORPHWEIGHTS") { morphWeightsUniformName = uniformName; } else if (uniform.semantic === "ALPHACUTOFF") { // The material's alphaCutoff value uses a uniform with semantic ALPHACUTOFF. // A uniform with this semantic will ignore the instance or default values. var alphaMode = material.alphaMode; if (defined(alphaMode) && alphaMode === "MASK") { var alphaCutoffValue = defaultValue(material.alphaCutoff, 0.5); uv = ModelUtility.createUniformFunction( uniform.type, alphaCutoffValue, textures, defaultTexture ); uniformMap[uniformName] = uv.func; uniformValues[uniformName] = uv; } } else { // Map glTF semantic to Cesium automatic uniform uniformMap[uniformName] = ModelUtility.getGltfSemanticUniforms()[ uniform.semantic ](context.uniformState, model); } } else if (defined(uniform.value)) { // Technique value that isn't overridden by a material var uv2 = ModelUtility.createUniformFunction( uniform.type, uniform.value, textures, defaultTexture ); uniformMap[uniformName] = uv2.func; uniformValues[uniformName] = uv2; } }); return { map: uniformMap, values: uniformValues, jointMatrixUniformName: jointMatrixUniformName, morphWeightsUniformName: morphWeightsUniformName, }; } function createUniformMaps(model, context) { var loadResources = model._loadResources; if (!loadResources.finishedProgramCreation()) { return; } if (!loadResources.createUniformMaps) { return; } loadResources.createUniformMaps = false; var gltf = model.gltf; var techniques = model._sourceTechniques; var uniformMaps = model._uniformMaps; var textures = model._rendererResources.textures; var defaultTexture = model._defaultTexture; ForEach.material(gltf, function (material, materialId) { var modelMaterial = model._runtime.materialsById[materialId]; var technique = techniques[modelMaterial._technique]; var instanceValues = modelMaterial._values; var uniforms = createUniformsForMaterial( model, material, technique, instanceValues, context, textures, defaultTexture ); var u = uniformMaps[materialId]; u.uniformMap = uniforms.map; // uniform name -> function for the renderer u.values = uniforms.values; // material parameter name -> ModelMaterial for modifying the parameter at runtime u.jointMatrixUniformName = uniforms.jointMatrixUniformName; u.morphWeightsUniformName = uniforms.morphWeightsUniformName; if (defined(technique.attributes.a_outlineCoordinates)) { var outlineTexture = ModelOutlineLoader.createTexture(model, context); u.uniformMap.u_outlineTexture = function () { return outlineTexture; }; } }); } function createUniformsForDracoQuantizedAttributes(decodedData) { return ModelUtility.createUniformsForDracoQuantizedAttributes( decodedData.attributes ); } function createUniformsForQuantizedAttributes(model, primitive) { var programId = getProgramForPrimitive(model, primitive); var quantizedUniforms = model._quantizedUniforms[programId]; return ModelUtility.createUniformsForQuantizedAttributes( model.gltf, primitive, quantizedUniforms ); } function createPickColorFunction$1(color) { return function () { return color; }; } function createJointMatricesFunction(runtimeNode) { return function () { return runtimeNode.computedJointMatrices; }; } function createMorphWeightsFunction(runtimeNode) { return function () { return runtimeNode.weights; }; } function createSilhouetteColorFunction(model) { return function () { return model.silhouetteColor; }; } function createSilhouetteSizeFunction(model) { return function () { return model.silhouetteSize; }; } function createColorFunction(model) { return function () { return model.color; }; } function createClippingPlanesMatrixFunction(model) { return function () { return model._clippingPlanesMatrix; }; } function createIBLReferenceFrameMatrixFunction(model) { return function () { return model._iblReferenceFrameMatrix; }; } function createClippingPlanesFunction(model) { return function () { var clippingPlanes = model.clippingPlanes; return !defined(clippingPlanes) || !clippingPlanes.enabled ? model._defaultTexture : clippingPlanes.texture; }; } function createClippingPlanesEdgeStyleFunction(model) { return function () { var clippingPlanes = model.clippingPlanes; if (!defined(clippingPlanes)) { return Color.WHITE.withAlpha(0.0); } var style = Color.clone(clippingPlanes.edgeColor); style.alpha = clippingPlanes.edgeWidth; return style; }; } function createColorBlendFunction(model) { return function () { return ColorBlendMode$1.getColorBlend( model.colorBlendMode, model.colorBlendAmount ); }; } function createIBLFactorFunction(model) { return function () { return model._imageBasedLightingFactor; }; } function createLightColorFunction(model) { return function () { return model._lightColor; }; } function createLuminanceAtZenithFunction(model) { return function () { return model.luminanceAtZenith; }; } function createSphericalHarmonicCoefficientsFunction(model) { return function () { return model._sphericalHarmonicCoefficients; }; } function createSpecularEnvironmentMapFunction(model) { return function () { return model._specularEnvironmentMapAtlas.texture; }; } function createSpecularEnvironmentMapSizeFunction(model) { return function () { return model._specularEnvironmentMapAtlas.texture.dimensions; }; } function createSpecularEnvironmentMapLOD(model) { return function () { return model._specularEnvironmentMapAtlas.maximumMipmapLevel; }; } //【hongguo】 田洪果 倾斜模型编辑修改 function createModelEditor_b3dmOffset(model) { return function () { return model._modelEditor.b3dmOffset || new Cartesian3(0.0, 0.0, 0.0); }; } //压平纹理 function createModelEditor_u_polygonTexture(model, context) { return function () { return model._modelEditor.fbo && model._modelEditor.fbo._colorTextures[0] || context._defaultTexture; }; } //压平外包矩形 function createModelEditor_u_polygonBounds(model) { return function () { return model._modelEditor.polygonBounds || new Cartesian4(0.0, 0.0, 0.0, 0.0); // return model._modelEditor.polygonBounds || []; }; } //压平高度 function createModelEditor_u_heightVar(model) { return function () { return new Cartesian2(model._modelEditor.heightVar[0], model._modelEditor.heightVar[1]); }; } function createModelEditor_IsYaPing(model) { return function () { return new Cartesian4(model._modelEditor.IsYaPing[0], model._modelEditor.IsYaPing[1], model._modelEditor.IsYaPing[2], model._modelEditor.IsYaPing[3]); }; } function createModelEditor_editVar(model) { return function () { return new Cartesian4(model._modelEditor.editVar[0], model._modelEditor.editVar[1], model._modelEditor.editVar[2], model._modelEditor.editVar[3]); }; } function createModelEditor_floodVar(model) { return function () { return new Cartesian4(model._modelEditor.floodVar[0], model._modelEditor.floodVar[1], model._modelEditor.floodVar[2], model._modelEditor.floodVar[3]); }; } function createModelEditor_floodColor(model) { return function () { return new Cartesian4(model._modelEditor.floodColor[0], model._modelEditor.floodColor[1], model._modelEditor.floodColor[2], model._modelEditor.floodColor[3]); }; } //【hongguo】 田洪果 倾斜模型编辑修改 function triangleCountFromPrimitiveIndices(primitive, indicesCount) { switch (primitive.mode) { case PrimitiveType$1.TRIANGLES: return indicesCount / 3; case PrimitiveType$1.TRIANGLE_STRIP: case PrimitiveType$1.TRIANGLE_FAN: return Math.max(indicesCount - 2, 0); default: return 0; } } function createCommand$2(model, gltfNode, runtimeNode, context, scene3DOnly) { var nodeCommands = model._nodeCommands; var pickIds = model._pickIds; var allowPicking = model.allowPicking; var runtimeMeshesByName = model._runtime.meshesByName; var resources = model._rendererResources; var rendererVertexArrays = resources.vertexArrays; var rendererPrograms = resources.programs; var rendererRenderStates = resources.renderStates; var uniformMaps = model._uniformMaps; var gltf = model.gltf; var accessors = gltf.accessors; var gltfMeshes = gltf.meshes; var id = gltfNode.mesh; var mesh = gltfMeshes[id]; var primitives = mesh.primitives; var length = primitives.length; // The glTF node hierarchy is a DAG so a node can have more than one // parent, so a node may already have commands. If so, append more // since they will have a different model matrix. for (var i = 0; i < length; ++i) { var primitive = primitives[i]; var ix = accessors[primitive.indices]; var material = model._runtime.materialsById[primitive.material]; var programId = material._program; var decodedData = model._decodedData[id + ".primitive." + i]; var boundingSphere; var positionAccessor = primitive.attributes.POSITION; if (defined(positionAccessor)) { var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor); boundingSphere = BoundingSphere.fromCornerPoints( Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max) ); } var vertexArray = rendererVertexArrays[id + ".primitive." + i]; var offset; var count; // Use indices of the previously decoded Draco geometry. if (defined(decodedData)) { count = decodedData.numberOfIndices; offset = 0; } else if (defined(ix)) { count = ix.count; offset = ix.byteOffset / IndexDatatype$1.getSizeInBytes(ix.componentType); // glTF has offset in bytes. Cesium has offsets in indices } else { var positions = accessors[primitive.attributes.POSITION]; count = positions.count; offset = 0; } // Update model triangle count using number of indices model._trianglesLength += triangleCountFromPrimitiveIndices( primitive, count ); var um = uniformMaps[primitive.material]; var uniformMap = um.uniformMap; if (defined(um.jointMatrixUniformName)) { var jointUniformMap = {}; jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction( runtimeNode ); uniformMap = combine$2(uniformMap, jointUniformMap); } if (defined(um.morphWeightsUniformName)) { var morphWeightsUniformMap = {}; morphWeightsUniformMap[ um.morphWeightsUniformName ] = createMorphWeightsFunction(runtimeNode); uniformMap = combine$2(uniformMap, morphWeightsUniformMap); } uniformMap = combine$2(uniformMap, { gltf_color: createColorFunction(model), gltf_colorBlend: createColorBlendFunction(model), gltf_clippingPlanes: createClippingPlanesFunction(model), gltf_clippingPlanesEdgeStyle: createClippingPlanesEdgeStyleFunction( model ), gltf_clippingPlanesMatrix: createClippingPlanesMatrixFunction(model), gltf_iblReferenceFrameMatrix: createIBLReferenceFrameMatrixFunction( model ), gltf_iblFactor: createIBLFactorFunction(model), gltf_lightColor: createLightColorFunction(model), gltf_sphericalHarmonicCoefficients: createSphericalHarmonicCoefficientsFunction( model ), gltf_specularMap: createSpecularEnvironmentMapFunction(model), gltf_specularMapSize: createSpecularEnvironmentMapSizeFunction(model), gltf_maxSpecularLOD: createSpecularEnvironmentMapLOD(model), gltf_luminanceAtZenith: createLuminanceAtZenithFunction(model), //【hongguo】 田洪果 倾斜模型编辑 b3dmOffset :createModelEditor_b3dmOffset(model), //有些模型会有偏移,此变量用来调整,一个模型只需调整一次 u_polygonTexture:createModelEditor_u_polygonTexture(model,context),//压平纹理 u_polygonBounds:createModelEditor_u_polygonBounds(model),//压平外包矩形 u_heightVar:createModelEditor_u_heightVar(model), //压平高度 IsYaPing:createModelEditor_IsYaPing(model), editVar:createModelEditor_editVar(model), floodVar:createModelEditor_floodVar(model), floodColor:createModelEditor_floodColor(model) //【hongguo】 田洪果 倾斜模型编辑 }); // Allow callback to modify the uniformMap if (defined(model._uniformMapLoaded)) { uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode); } // Add uniforms for decoding quantized attributes if used var quantizedUniformMap = {}; if (model.extensionsUsed.WEB3D_quantized_attributes) { quantizedUniformMap = createUniformsForQuantizedAttributes( model, primitive ); } else if (model._dequantizeInShader && defined(decodedData)) { quantizedUniformMap = createUniformsForDracoQuantizedAttributes( decodedData ); } uniformMap = combine$2(uniformMap, quantizedUniformMap); var rs = rendererRenderStates[primitive.material]; var isTranslucent = rs.blending.enabled; var owner = model._pickObject; if (!defined(owner)) { owner = { primitive: model, id: model.id, node: runtimeNode.publicNode, mesh: runtimeMeshesByName[mesh.name], }; } var castShadows = ShadowMode$1.castShadows(model._shadows); var receiveShadows = ShadowMode$1.receiveShadows(model._shadows); var pickId; if (allowPicking && !defined(model._uniformMapLoaded)) { pickId = context.createPickId(owner); pickIds.push(pickId); var pickUniforms = { czm_pickColor: createPickColorFunction$1(pickId.color), }; uniformMap = combine$2(uniformMap, pickUniforms); } if (allowPicking) { if (defined(model._pickIdLoaded) && defined(model._uniformMapLoaded)) { pickId = model._pickIdLoaded(); } else { pickId = "czm_pickColor"; } } var command = new DrawCommand({ boundingVolume: new BoundingSphere(), // updated in update() cull: model.cull, modelMatrix: new Matrix4(), // computed in update() primitiveType: primitive.mode, vertexArray: vertexArray, count: count, offset: offset, shaderProgram: rendererPrograms[programId], castShadows: castShadows, receiveShadows: receiveShadows, uniformMap: uniformMap, renderState: rs, owner: owner, pass: isTranslucent ? Pass$1.TRANSLUCENT : model.opaquePass, pickId: pickId, }); var command2D; if (!scene3DOnly) { command2D = DrawCommand.shallowClone(command); command2D.boundingVolume = new BoundingSphere(); // updated in update() command2D.modelMatrix = new Matrix4(); // updated in update() } var nodeCommand = { show: true, boundingSphere: boundingSphere, command: command, command2D: command2D, // Generated on demand when silhouette size is greater than 0.0 and silhouette alpha is greater than 0.0 silhouetteModelCommand: undefined, silhouetteModelCommand2D: undefined, silhouetteColorCommand: undefined, silhouetteColorCommand2D: undefined, // Generated on demand when color alpha is less than 1.0 translucentCommand: undefined, translucentCommand2D: undefined, // Generated on demand when back face culling is false disableCullingCommand: undefined, disableCullingCommand2D: undefined, // For updating node commands on shader reconstruction programId: programId, }; runtimeNode.commands.push(nodeCommand); nodeCommands.push(nodeCommand); } } function createRuntimeNodes(model, context, scene3DOnly) { var loadResources = model._loadResources; if (!loadResources.finishedEverythingButTextureCreation()) { return; } if (!loadResources.createRuntimeNodes) { return; } loadResources.createRuntimeNodes = false; var rootNodes = []; var runtimeNodes = model._runtime.nodes; var gltf = model.gltf; var nodes = gltf.nodes; var scene = gltf.scenes[gltf.scene]; var sceneNodes = scene.nodes; var length = sceneNodes.length; var stack = []; var seen = {}; for (var i = 0; i < length; ++i) { stack.push({ parentRuntimeNode: undefined, gltfNode: nodes[sceneNodes[i]], id: sceneNodes[i], }); while (stack.length > 0) { var n = stack.pop(); seen[n.id] = true; var parentRuntimeNode = n.parentRuntimeNode; var gltfNode = n.gltfNode; // Node hierarchy is a DAG so a node can have more than one parent so it may already exist var runtimeNode = runtimeNodes[n.id]; if (runtimeNode.parents.length === 0) { if (defined(gltfNode.matrix)) { runtimeNode.matrix = Matrix4.fromColumnMajorArray(gltfNode.matrix); } else { // TRS converted to Cesium types var rotation = gltfNode.rotation; runtimeNode.translation = Cartesian3.fromArray(gltfNode.translation); runtimeNode.rotation = Quaternion.unpack(rotation); runtimeNode.scale = Cartesian3.fromArray(gltfNode.scale); } } if (defined(parentRuntimeNode)) { parentRuntimeNode.children.push(runtimeNode); runtimeNode.parents.push(parentRuntimeNode); } else { rootNodes.push(runtimeNode); } if (defined(gltfNode.mesh)) { createCommand$2(model, gltfNode, runtimeNode, context, scene3DOnly); } var children = gltfNode.children; if (defined(children)) { var childrenLength = children.length; for (var j = 0; j < childrenLength; j++) { var childId = children[j]; if (!seen[childId]) { stack.push({ parentRuntimeNode: runtimeNode, gltfNode: nodes[childId], id: children[j], }); } } } } } model._runtime.rootNodes = rootNodes; model._runtime.nodes = runtimeNodes; } function getGeometryByteLength(buffers) { var memory = 0; for (var id in buffers) { if (buffers.hasOwnProperty(id)) { memory += buffers[id].sizeInBytes; } } return memory; } function getTexturesByteLength(textures) { var memory = 0; for (var id in textures) { if (textures.hasOwnProperty(id)) { memory += textures[id].sizeInBytes; } } return memory; } function createResources$5(model, frameState) { var context = frameState.context; var scene3DOnly = frameState.scene3DOnly; var quantizedVertexShaders = model._quantizedVertexShaders; var techniques = model._sourceTechniques; var programs = model._sourcePrograms; var resources = model._rendererResources; var shaders = resources.sourceShaders; if (model._loadRendererResourcesFromCache) { shaders = resources.sourceShaders = model._cachedRendererResources.sourceShaders; } for (var techniqueId in techniques) { if (techniques.hasOwnProperty(techniqueId)) { var programId = techniques[techniqueId].program; var program = programs[programId]; var shader = shaders[program.vertexShader]; ModelUtility.checkSupportedGlExtensions(program.glExtensions, context); if ( model.extensionsUsed.WEB3D_quantized_attributes || model._dequantizeInShader ) { var quantizedVS = quantizedVertexShaders[programId]; if (!defined(quantizedVS)) { quantizedVS = modifyShaderForQuantizedAttributes( shader, programId, model ); quantizedVertexShaders[programId] = quantizedVS; } shader = quantizedVS; } shader = modifyShader(shader, programId, model._vertexShaderLoaded); } } if (model._loadRendererResourcesFromCache) { var cachedResources = model._cachedRendererResources; resources.buffers = cachedResources.buffers; resources.vertexArrays = cachedResources.vertexArrays; resources.programs = cachedResources.programs; resources.silhouettePrograms = cachedResources.silhouettePrograms; resources.textures = cachedResources.textures; resources.samplers = cachedResources.samplers; resources.renderStates = cachedResources.renderStates; // Vertex arrays are unique to this model, create instead of using the cache. if (defined(model._precreatedAttributes)) { createVertexArrays$1(model, context); } model._cachedGeometryByteLength += getGeometryByteLength( cachedResources.buffers ); model._cachedTexturesByteLength += getTexturesByteLength( cachedResources.textures ); } else { createBuffers(model, frameState); // using glTF bufferViews createPrograms(model, frameState); createSamplers(model); loadTexturesFromBufferViews(model); createTextures$2(model, frameState); } createSkins(model); createRuntimeAnimations(model); if (!model._loadRendererResourcesFromCache) { createVertexArrays$1(model, context); // using glTF meshes createRenderStates$2(model); // using glTF materials/techniques/states // Long-term, we might not cache render states if they could change // due to an animation, e.g., a uniform going from opaque to transparent. // Could use copy-on-write if it is worth it. Probably overkill. } createUniformMaps(model, context); // using glTF materials/techniques createRuntimeNodes(model, context, scene3DOnly); // using glTF scene } /////////////////////////////////////////////////////////////////////////// function getNodeMatrix(node, result) { var publicNode = node.publicNode; var publicMatrix = publicNode.matrix; if (publicNode.useMatrix && defined(publicMatrix)) { // Public matrix overrides original glTF matrix and glTF animations Matrix4.clone(publicMatrix, result); } else if (defined(node.matrix)) { Matrix4.clone(node.matrix, result); } else { Matrix4.fromTranslationQuaternionRotationScale( node.translation, node.rotation, node.scale, result ); // Keep matrix returned by the node in-sync if the node is targeted by an animation. Only TRS nodes can be targeted. publicNode.setMatrix(result); } } var scratchNodeStack = []; var scratchComputedTranslation$1 = new Cartesian4(); var scratchComputedMatrixIn2D = new Matrix4(); function updateNodeHierarchyModelMatrix( model, modelTransformChanged, justLoaded, projection ) { var maxDirtyNumber = model._maxDirtyNumber; var rootNodes = model._runtime.rootNodes; var length = rootNodes.length; var nodeStack = scratchNodeStack; var computedModelMatrix = model._computedModelMatrix; if (model._mode !== SceneMode$1.SCENE3D && !model._ignoreCommands) { var translation = Matrix4.getColumn( computedModelMatrix, 3, scratchComputedTranslation$1 ); if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { computedModelMatrix = Transforms.basisTo2D( projection, computedModelMatrix, scratchComputedMatrixIn2D ); model._rtcCenter = model._rtcCenter3D; } else { var center = model.boundingSphere.center; var to2D = Transforms.wgs84To2DModelMatrix( projection, center, scratchComputedMatrixIn2D ); computedModelMatrix = Matrix4.multiply( to2D, computedModelMatrix, scratchComputedMatrixIn2D ); if (defined(model._rtcCenter)) { Matrix4.setTranslation( computedModelMatrix, Cartesian4.UNIT_W, computedModelMatrix ); model._rtcCenter = model._rtcCenter2D; } } } for (var i = 0; i < length; ++i) { var n = rootNodes[i]; getNodeMatrix(n, n.transformToRoot); nodeStack.push(n); while (nodeStack.length > 0) { n = nodeStack.pop(); var transformToRoot = n.transformToRoot; var commands = n.commands; if ( n.dirtyNumber === maxDirtyNumber || modelTransformChanged || justLoaded ) { var nodeMatrix = Matrix4.multiplyTransformation( computedModelMatrix, transformToRoot, n.computedMatrix ); var commandsLength = commands.length; if (commandsLength > 0) { // Node has meshes, which has primitives. Update their commands. for (var j = 0; j < commandsLength; ++j) { var primitiveCommand = commands[j]; var command = primitiveCommand.command; Matrix4.clone(nodeMatrix, command.modelMatrix); // PERFORMANCE_IDEA: Can use transformWithoutScale if no node up to the root has scale (including animation) BoundingSphere.transform( primitiveCommand.boundingSphere, command.modelMatrix, command.boundingVolume ); if (defined(model._rtcCenter)) { Cartesian3.add( model._rtcCenter, command.boundingVolume.center, command.boundingVolume.center ); } // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it // will be clipped by the viewport. We create a second command that translates the model // model matrix to the opposite side of the map so the part that was clipped in one viewport // is drawn in the other. command = primitiveCommand.command2D; if (defined(command) && model._mode === SceneMode$1.SCENE2D) { Matrix4.clone(nodeMatrix, command.modelMatrix); command.modelMatrix[13] -= CesiumMath.sign(command.modelMatrix[13]) * 2.0 * CesiumMath.PI * projection.ellipsoid.maximumRadius; BoundingSphere.transform( primitiveCommand.boundingSphere, command.modelMatrix, command.boundingVolume ); } } } } var children = n.children; if (defined(children)) { var childrenLength = children.length; for (var k = 0; k < childrenLength; ++k) { var child = children[k]; // A node's transform needs to be updated if // - It was targeted for animation this frame, or // - Any of its ancestors were targeted for animation this frame // PERFORMANCE_IDEA: if a child has multiple parents and only one of the parents // is dirty, all the subtrees for each child instance will be dirty; we probably // won't see this in the wild often. child.dirtyNumber = Math.max(child.dirtyNumber, n.dirtyNumber); if (child.dirtyNumber === maxDirtyNumber || justLoaded) { // Don't check for modelTransformChanged since if only the model's model matrix changed, // we do not need to rebuild the local transform-to-root, only the final // [model's-model-matrix][transform-to-root] above. getNodeMatrix(child, child.transformToRoot); Matrix4.multiplyTransformation( transformToRoot, child.transformToRoot, child.transformToRoot ); } nodeStack.push(child); } } } } ++model._maxDirtyNumber; } var scratchObjectSpace = new Matrix4(); function applySkins(model) { var skinnedNodes = model._runtime.skinnedNodes; var length = skinnedNodes.length; for (var i = 0; i < length; ++i) { var node = skinnedNodes[i]; scratchObjectSpace = Matrix4.inverseTransformation( node.transformToRoot, scratchObjectSpace ); var computedJointMatrices = node.computedJointMatrices; var joints = node.joints; var bindShapeMatrix = node.bindShapeMatrix; var inverseBindMatrices = node.inverseBindMatrices; var inverseBindMatricesLength = inverseBindMatrices.length; for (var m = 0; m < inverseBindMatricesLength; ++m) { // [joint-matrix] = [node-to-root^-1][joint-to-root][inverse-bind][bind-shape] if (!defined(computedJointMatrices[m])) { computedJointMatrices[m] = new Matrix4(); } computedJointMatrices[m] = Matrix4.multiplyTransformation( scratchObjectSpace, joints[m].transformToRoot, computedJointMatrices[m] ); computedJointMatrices[m] = Matrix4.multiplyTransformation( computedJointMatrices[m], inverseBindMatrices[m], computedJointMatrices[m] ); if (defined(bindShapeMatrix)) { // NOTE: bindShapeMatrix is glTF 1.0 only, removed in glTF 2.0. computedJointMatrices[m] = Matrix4.multiplyTransformation( computedJointMatrices[m], bindShapeMatrix, computedJointMatrices[m] ); } } } } function updatePerNodeShow(model) { // Totally not worth it, but we could optimize this: // http://help.agi.com/AGIComponents/html/BlogDeletionInBoundingVolumeHierarchies.htm var rootNodes = model._runtime.rootNodes; var length = rootNodes.length; var nodeStack = scratchNodeStack; for (var i = 0; i < length; ++i) { var n = rootNodes[i]; n.computedShow = n.publicNode.show; nodeStack.push(n); while (nodeStack.length > 0) { n = nodeStack.pop(); var show = n.computedShow; var nodeCommands = n.commands; var nodeCommandsLength = nodeCommands.length; for (var j = 0; j < nodeCommandsLength; ++j) { nodeCommands[j].show = show; } // if commandsLength is zero, the node has a light or camera var children = n.children; if (defined(children)) { var childrenLength = children.length; for (var k = 0; k < childrenLength; ++k) { var child = children[k]; // Parent needs to be shown for child to be shown. child.computedShow = show && child.publicNode.show; nodeStack.push(child); } } } } } function updatePickIds(model, context) { var id = model.id; if (model._id !== id) { model._id = id; var pickIds = model._pickIds; var length = pickIds.length; for (var i = 0; i < length; ++i) { pickIds[i].object.id = id; } } } function updateWireframe$1(model) { if (model._debugWireframe !== model.debugWireframe) { model._debugWireframe = model.debugWireframe; // This assumes the original primitive was TRIANGLES and that the triangles // are connected for the wireframe to look perfect. var primitiveType = model.debugWireframe ? PrimitiveType$1.LINES : PrimitiveType$1.TRIANGLES; var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; ++i) { nodeCommands[i].command.primitiveType = primitiveType; } } } function updateShowBoundingVolume$1(model) { if (model.debugShowBoundingVolume !== model._debugShowBoundingVolume) { model._debugShowBoundingVolume = model.debugShowBoundingVolume; var debugShowBoundingVolume = model.debugShowBoundingVolume; var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; ++i) { nodeCommands[i].command.debugShowBoundingVolume = debugShowBoundingVolume; } } } function updateShadows$1(model) { if (model.shadows !== model._shadows) { model._shadows = model.shadows; var castShadows = ShadowMode$1.castShadows(model.shadows); var receiveShadows = ShadowMode$1.receiveShadows(model.shadows); var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; i++) { var nodeCommand = nodeCommands[i]; nodeCommand.command.castShadows = castShadows; nodeCommand.command.receiveShadows = receiveShadows; } } } function getTranslucentRenderState$1(renderState) { var rs = clone$1(renderState, true); rs.cull.enabled = false; rs.depthTest.enabled = true; rs.depthMask = false; rs.blending = BlendingState$1.ALPHA_BLEND; return RenderState.fromCache(rs); } function deriveTranslucentCommand(command) { var translucentCommand = DrawCommand.shallowClone(command); translucentCommand.pass = Pass$1.TRANSLUCENT; translucentCommand.renderState = getTranslucentRenderState$1( command.renderState ); return translucentCommand; } function updateColor(model, frameState, forceDerive) { // Generate translucent commands when the blend color has an alpha in the range (0.0, 1.0) exclusive var scene3DOnly = frameState.scene3DOnly; var alpha = model.color.alpha; if (alpha > 0.0 && alpha < 1.0) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; if ( length > 0 && (!defined(nodeCommands[0].translucentCommand) || forceDerive) ) { for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; var command = nodeCommand.command; nodeCommand.translucentCommand = deriveTranslucentCommand(command); if (!scene3DOnly) { var command2D = nodeCommand.command2D; nodeCommand.translucentCommand2D = deriveTranslucentCommand( command2D ); } } } } } function getDisableCullingRenderState$1(renderState) { var rs = clone$1(renderState, true); rs.cull.enabled = false; return RenderState.fromCache(rs); } function deriveDisableCullingCommand(command) { var disableCullingCommand = DrawCommand.shallowClone(command); disableCullingCommand.renderState = getDisableCullingRenderState$1( command.renderState ); return disableCullingCommand; } function updateBackFaceCulling$1(model, frameState, forceDerive) { var scene3DOnly = frameState.scene3DOnly; var backFaceCulling = model.backFaceCulling; if (!backFaceCulling) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; if ( length > 0 && (!defined(nodeCommands[0].disableCullingCommand) || forceDerive) ) { for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; var command = nodeCommand.command; nodeCommand.disableCullingCommand = deriveDisableCullingCommand( command ); if (!scene3DOnly) { var command2D = nodeCommand.command2D; nodeCommand.disableCullingCommand2D = deriveDisableCullingCommand( command2D ); } } } } } function getProgramId(model, program) { var programs = model._rendererResources.programs; for (var id in programs) { if (programs.hasOwnProperty(id)) { if (programs[id] === program) { return id; } } } } function createSilhouetteProgram(model, program, frameState) { var vs = program.vertexShaderSource.sources[0]; var attributeLocations = program._attributeLocations; var normalAttributeName = model._normalAttributeName; // Modified from http://forum.unity3d.com/threads/toon-outline-but-with-diffuse-surface.24668/ vs = ShaderSource.replaceMain(vs, "gltf_silhouette_main"); vs += "uniform float gltf_silhouetteSize; \n" + "void main() \n" + "{ \n" + " gltf_silhouette_main(); \n" + " vec3 n = normalize(czm_normal3D * " + normalAttributeName + "); \n" + " n.x *= czm_projection[0][0]; \n" + " n.y *= czm_projection[1][1]; \n" + " vec4 clip = gl_Position; \n" + " clip.xy += n.xy * clip.w * gltf_silhouetteSize * czm_pixelRatio / czm_viewport.z; \n" + " gl_Position = clip; \n" + "}"; var fs = "uniform vec4 gltf_silhouetteColor; \n" + "void main() \n" + "{ \n" + " gl_FragColor = czm_gammaCorrect(gltf_silhouetteColor); \n" + "}"; return ShaderProgram.fromCache({ context: frameState.context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); } function hasSilhouette(model, frameState) { return ( silhouetteSupported(frameState.context) && model.silhouetteSize > 0.0 && model.silhouetteColor.alpha > 0.0 && defined(model._normalAttributeName) ); } function hasTranslucentCommands(model) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; var command = nodeCommand.command; if (command.pass === Pass$1.TRANSLUCENT) { return true; } } return false; } function isTranslucent(model) { return model.color.alpha > 0.0 && model.color.alpha < 1.0; } function isInvisible(model) { return model.color.alpha === 0.0; } function alphaDirty(currAlpha, prevAlpha) { // Returns whether the alpha state has changed between invisible, translucent, or opaque return ( Math.floor(currAlpha) !== Math.floor(prevAlpha) || Math.ceil(currAlpha) !== Math.ceil(prevAlpha) ); } var silhouettesLength = 0; function createSilhouetteCommands(model, frameState) { // Wrap around after exceeding the 8-bit stencil limit. // The reference is unique to each model until this point. var stencilReference = ++silhouettesLength % 255; // If the model is translucent the silhouette needs to be in the translucent pass. // Otherwise the silhouette would be rendered before the model. var silhouetteTranslucent = hasTranslucentCommands(model) || isTranslucent(model) || model.silhouetteColor.alpha < 1.0; var silhouettePrograms = model._rendererResources.silhouettePrograms; var scene3DOnly = frameState.scene3DOnly; var nodeCommands = model._nodeCommands; var length = nodeCommands.length; for (var i = 0; i < length; ++i) { var nodeCommand = nodeCommands[i]; var command = nodeCommand.command; // Create model command var modelCommand = isTranslucent(model) ? nodeCommand.translucentCommand : command; var silhouetteModelCommand = DrawCommand.shallowClone(modelCommand); var renderState = clone$1(modelCommand.renderState); // Write the reference value into the stencil buffer. renderState.stencilTest = { enabled: true, frontFunction: WebGLConstants$1.ALWAYS, backFunction: WebGLConstants$1.ALWAYS, reference: stencilReference, mask: ~0, frontOperation: { fail: WebGLConstants$1.KEEP, zFail: WebGLConstants$1.KEEP, zPass: WebGLConstants$1.REPLACE, }, backOperation: { fail: WebGLConstants$1.KEEP, zFail: WebGLConstants$1.KEEP, zPass: WebGLConstants$1.REPLACE, }, }; if (isInvisible(model)) { // When the model is invisible disable color and depth writes but still write into the stencil buffer renderState.colorMask = { red: false, green: false, blue: false, alpha: false, }; renderState.depthMask = false; } renderState = RenderState.fromCache(renderState); silhouetteModelCommand.renderState = renderState; nodeCommand.silhouetteModelCommand = silhouetteModelCommand; // Create color command var silhouetteColorCommand = DrawCommand.shallowClone(command); renderState = clone$1(command.renderState, true); renderState.depthTest.enabled = true; renderState.cull.enabled = false; if (silhouetteTranslucent) { silhouetteColorCommand.pass = Pass$1.TRANSLUCENT; renderState.depthMask = false; renderState.blending = BlendingState$1.ALPHA_BLEND; } // Only render silhouette if the value in the stencil buffer equals the reference renderState.stencilTest = { enabled: true, frontFunction: WebGLConstants$1.NOTEQUAL, backFunction: WebGLConstants$1.NOTEQUAL, reference: stencilReference, mask: ~0, frontOperation: { fail: WebGLConstants$1.KEEP, zFail: WebGLConstants$1.KEEP, zPass: WebGLConstants$1.KEEP, }, backOperation: { fail: WebGLConstants$1.KEEP, zFail: WebGLConstants$1.KEEP, zPass: WebGLConstants$1.KEEP, }, }; renderState = RenderState.fromCache(renderState); // If the silhouette program has already been cached use it var program = command.shaderProgram; var id = getProgramId(model, program); var silhouetteProgram = silhouettePrograms[id]; if (!defined(silhouetteProgram)) { silhouetteProgram = createSilhouetteProgram(model, program, frameState); silhouettePrograms[id] = silhouetteProgram; } var silhouetteUniformMap = combine$2(command.uniformMap, { gltf_silhouetteColor: createSilhouetteColorFunction(model), gltf_silhouetteSize: createSilhouetteSizeFunction(model), }); silhouetteColorCommand.renderState = renderState; silhouetteColorCommand.shaderProgram = silhouetteProgram; silhouetteColorCommand.uniformMap = silhouetteUniformMap; silhouetteColorCommand.castShadows = false; silhouetteColorCommand.receiveShadows = false; nodeCommand.silhouetteColorCommand = silhouetteColorCommand; if (!scene3DOnly) { var command2D = nodeCommand.command2D; var silhouetteModelCommand2D = DrawCommand.shallowClone( silhouetteModelCommand ); silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume; silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix; nodeCommand.silhouetteModelCommand2D = silhouetteModelCommand2D; var silhouetteColorCommand2D = DrawCommand.shallowClone( silhouetteColorCommand ); silhouetteModelCommand2D.boundingVolume = command2D.boundingVolume; silhouetteModelCommand2D.modelMatrix = command2D.modelMatrix; nodeCommand.silhouetteColorCommand2D = silhouetteColorCommand2D; } } } function modifyShaderForClippingPlanes( shader, clippingPlaneCollection, context ) { shader = ShaderSource.replaceMain(shader, "gltf_clip_main"); shader += Model._getClippingFunction(clippingPlaneCollection, context) + "\n"; shader += "uniform highp sampler2D gltf_clippingPlanes; \n" + "uniform mat4 gltf_clippingPlanesMatrix; \n" + "uniform vec4 gltf_clippingPlanesEdgeStyle; \n" + "void main() \n" + "{ \n" + " gltf_clip_main(); \n" + getClipAndStyleCode( "gltf_clippingPlanes", "gltf_clippingPlanesMatrix", "gltf_clippingPlanesEdgeStyle" ) + "} \n"; return shader; } function updateSilhouette(model, frameState, force) { // Generate silhouette commands when the silhouette size is greater than 0.0 and the alpha is greater than 0.0 // There are two silhouette commands: // 1. silhouetteModelCommand : render model normally while enabling stencil mask // 2. silhouetteColorCommand : render enlarged model with a solid color while enabling stencil tests if (!hasSilhouette(model, frameState)) { return; } var nodeCommands = model._nodeCommands; var dirty = nodeCommands.length > 0 && (alphaDirty(model.color.alpha, model._colorPreviousAlpha) || alphaDirty( model.silhouetteColor.alpha, model._silhouetteColorPreviousAlpha ) || !defined(nodeCommands[0].silhouetteModelCommand)); model._colorPreviousAlpha = model.color.alpha; model._silhouetteColorPreviousAlpha = model.silhouetteColor.alpha; if (dirty || force) { createSilhouetteCommands(model, frameState); } } function updateClippingPlanes$1(model, frameState) { var clippingPlanes = model._clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.owner === model) { if (clippingPlanes.enabled) { clippingPlanes.update(frameState); } } } var scratchBoundingSphere$1 = new BoundingSphere(); function scaleInPixels(positionWC, radius, frameState) { scratchBoundingSphere$1.center = positionWC; scratchBoundingSphere$1.radius = radius; return frameState.camera.getPixelSize( scratchBoundingSphere$1, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight ); } var scratchPosition$6 = new Cartesian3(); var scratchCartographic$c = new Cartographic(); function getScale(model, frameState) { var scale = model.scale; if (model.minimumPixelSize !== 0.0) { // Compute size of bounding sphere in pixels var context = frameState.context; var maxPixelSize = Math.max( context.drawingBufferWidth, context.drawingBufferHeight ); var m = defined(model._clampedModelMatrix) ? model._clampedModelMatrix : model.modelMatrix; scratchPosition$6.x = m[12]; scratchPosition$6.y = m[13]; scratchPosition$6.z = m[14]; if (defined(model._rtcCenter)) { Cartesian3.add(model._rtcCenter, scratchPosition$6, scratchPosition$6); } if (model._mode !== SceneMode$1.SCENE3D) { var projection = frameState.mapProjection; var cartographic = projection.ellipsoid.cartesianToCartographic( scratchPosition$6, scratchCartographic$c ); projection.project(cartographic, scratchPosition$6); Cartesian3.fromElements( scratchPosition$6.z, scratchPosition$6.x, scratchPosition$6.y, scratchPosition$6 ); } var radius = model.boundingSphere.radius; var metersPerPixel = scaleInPixels(scratchPosition$6, radius, frameState); // metersPerPixel is always > 0.0 var pixelsPerMeter = 1.0 / metersPerPixel; var diameterInPixels = Math.min( pixelsPerMeter * (2.0 * radius), maxPixelSize ); // Maintain model's minimum pixel size if (diameterInPixels < model.minimumPixelSize) { scale = (model.minimumPixelSize * metersPerPixel) / (2.0 * model._initialRadius); } } return defined(model.maximumScale) ? Math.min(model.maximumScale, scale) : scale; } function releaseCachedGltf(model) { if ( defined(model._cacheKey) && defined(model._cachedGltf) && --model._cachedGltf.count === 0 ) { delete gltfCache[model._cacheKey]; } model._cachedGltf = undefined; } /////////////////////////////////////////////////////////////////////////// function CachedRendererResources(context, cacheKey) { this.buffers = undefined; this.vertexArrays = undefined; this.programs = undefined; this.sourceShaders = undefined; this.silhouettePrograms = undefined; this.textures = undefined; this.samplers = undefined; this.renderStates = undefined; this.ready = false; this.context = context; this.cacheKey = cacheKey; this.count = 0; } function destroy(property) { for (var name in property) { if (property.hasOwnProperty(name)) { property[name].destroy(); } } } function destroyCachedRendererResources(resources) { destroy(resources.buffers); destroy(resources.vertexArrays); destroy(resources.programs); destroy(resources.silhouettePrograms); destroy(resources.textures); } CachedRendererResources.prototype.release = function () { if (--this.count === 0) { if (defined(this.cacheKey)) { // Remove if this was cached delete this.context.cache.modelRendererResourceCache[this.cacheKey]; } destroyCachedRendererResources(this); return destroyObject(this); } return undefined; }; /////////////////////////////////////////////////////////////////////////// function getUpdateHeightCallback(model, ellipsoid, cartoPosition) { return function (clampedPosition) { if (model.heightReference === HeightReference$1.RELATIVE_TO_GROUND) { var clampedCart = ellipsoid.cartesianToCartographic( clampedPosition, scratchCartographic$c ); clampedCart.height += cartoPosition.height; ellipsoid.cartographicToCartesian(clampedCart, clampedPosition); } var clampedModelMatrix = model._clampedModelMatrix; // Modify clamped model matrix to use new height Matrix4.clone(model.modelMatrix, clampedModelMatrix); clampedModelMatrix[12] = clampedPosition.x; clampedModelMatrix[13] = clampedPosition.y; clampedModelMatrix[14] = clampedPosition.z; model._heightChanged = true; }; } function updateClamping(model) { if (defined(model._removeUpdateHeightCallback)) { model._removeUpdateHeightCallback(); model._removeUpdateHeightCallback = undefined; } var scene = model._scene; if ( !defined(scene) || !defined(scene.globe) || model.heightReference === HeightReference$1.NONE ) { //>>includeStart('debug', pragmas.debug); if (model.heightReference !== HeightReference$1.NONE) { throw new DeveloperError( "Height reference is not supported without a scene and globe." ); } //>>includeEnd('debug'); model._clampedModelMatrix = undefined; return; } var globe = scene.globe; var ellipsoid = globe.ellipsoid; // Compute cartographic position so we don't recompute every update var modelMatrix = model.modelMatrix; scratchPosition$6.x = modelMatrix[12]; scratchPosition$6.y = modelMatrix[13]; scratchPosition$6.z = modelMatrix[14]; var cartoPosition = ellipsoid.cartesianToCartographic(scratchPosition$6); if (!defined(model._clampedModelMatrix)) { model._clampedModelMatrix = Matrix4.clone(modelMatrix, new Matrix4()); } // Install callback to handle updating of terrain tiles var surface = globe._surface; model._removeUpdateHeightCallback = surface.updateHeight( cartoPosition, getUpdateHeightCallback(model, ellipsoid, cartoPosition) ); // Set the correct height now var height = globe.getHeight(cartoPosition); if (defined(height)) { // Get callback with cartoPosition being the non-clamped position var cb = getUpdateHeightCallback(model, ellipsoid, cartoPosition); // Compute the clamped cartesian and call updateHeight callback Cartographic.clone(cartoPosition, scratchCartographic$c); scratchCartographic$c.height = height; ellipsoid.cartographicToCartesian(scratchCartographic$c, scratchPosition$6); cb(scratchPosition$6); } } var scratchDisplayConditionCartesian = new Cartesian3(); var scratchDistanceDisplayConditionCartographic = new Cartographic(); function distanceDisplayConditionVisible(model, frameState) { var distance2; var ddc = model.distanceDisplayCondition; var nearSquared = ddc.near * ddc.near; var farSquared = ddc.far * ddc.far; if (frameState.mode === SceneMode$1.SCENE2D) { var frustum2DWidth = frameState.camera.frustum.right - frameState.camera.frustum.left; distance2 = frustum2DWidth * 0.5; distance2 = distance2 * distance2; } else { // Distance to center of primitive's reference frame var position = Matrix4.getTranslation( model.modelMatrix, scratchDisplayConditionCartesian ); if (frameState.mode === SceneMode$1.COLUMBUS_VIEW) { var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var cartographic = ellipsoid.cartesianToCartographic( position, scratchDistanceDisplayConditionCartographic ); position = projection.project(cartographic, position); Cartesian3.fromElements(position.z, position.x, position.y, position); } distance2 = Cartesian3.distanceSquared( position, frameState.camera.positionWC ); } return distance2 >= nearSquared && distance2 <= farSquared; } var scratchClippingPlanesMatrix$2 = new Matrix4(); var scratchIBLReferenceFrameMatrix4 = new Matrix4(); var scratchIBLReferenceFrameMatrix3 = new Matrix3(); /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {RuntimeError} Failed to load external reference. */ Model.prototype.update = function (frameState) { if (frameState.mode === SceneMode$1.MORPHING) { return; } if (!FeatureDetection.supportsWebP.initialized) { FeatureDetection.supportsWebP.initialize(); return; } var supportsWebP = FeatureDetection.supportsWebP(); var context = frameState.context; this._defaultTexture = context.defaultTexture; if (this._state === ModelState.NEEDS_LOAD && defined(this.gltf)) { // Use renderer resources from cache instead of loading/creating them? var cachedRendererResources; var cacheKey = this.cacheKey; if (defined(cacheKey)) { // cache key given? this model will pull from or contribute to context level cache context.cache.modelRendererResourceCache = defaultValue( context.cache.modelRendererResourceCache, {} ); var modelCaches = context.cache.modelRendererResourceCache; cachedRendererResources = modelCaches[this.cacheKey]; if (defined(cachedRendererResources)) { if (!cachedRendererResources.ready) { // Cached resources for the model are not loaded yet. We'll // try again every frame until they are. return; } ++cachedRendererResources.count; this._loadRendererResourcesFromCache = true; } else { cachedRendererResources = new CachedRendererResources( context, cacheKey ); cachedRendererResources.count = 1; modelCaches[this.cacheKey] = cachedRendererResources; } this._cachedRendererResources = cachedRendererResources; } else { // cache key not given? this model doesn't care about context level cache at all. Cache is here to simplify freeing on destroy. cachedRendererResources = new CachedRendererResources(context); cachedRendererResources.count = 1; this._cachedRendererResources = cachedRendererResources; } this._state = ModelState.LOADING; if (this._state !== ModelState.FAILED) { var extensions = this.gltf.extensions; if (defined(extensions) && defined(extensions.CESIUM_RTC)) { var center = Cartesian3.fromArray(extensions.CESIUM_RTC.center); if (!Cartesian3.equals(center, Cartesian3.ZERO)) { this._rtcCenter3D = center; var projection = frameState.mapProjection; var ellipsoid = projection.ellipsoid; var cartographic = ellipsoid.cartesianToCartographic( this._rtcCenter3D ); var projectedCart = projection.project(cartographic); Cartesian3.fromElements( projectedCart.z, projectedCart.x, projectedCart.y, projectedCart ); this._rtcCenter2D = projectedCart; this._rtcCenterEye = new Cartesian3(); this._rtcCenter = this._rtcCenter3D; } } addPipelineExtras(this.gltf); this._loadResources = new ModelLoadResources(); if (!this._loadRendererResourcesFromCache) { // Buffers are required to updateVersion ModelUtility.parseBuffers(this, bufferLoad); } } } var loadResources = this._loadResources; var incrementallyLoadTextures = this._incrementallyLoadTextures; var justLoaded = false; if (this._state === ModelState.LOADING) { // Transition from LOADING -> LOADED once resources are downloaded and created. // Textures may continue to stream in while in the LOADED state. if (loadResources.pendingBufferLoads === 0) { if (!loadResources.initialized) { frameState.brdfLutGenerator.update(frameState); ModelUtility.checkSupportedExtensions( this.extensionsRequired, supportsWebP ); ModelUtility.updateForwardAxis(this); // glTF pipeline updates, not needed if loading from cache if (!defined(this.gltf.extras.sourceVersion)) { var gltf = this.gltf; // Add the original version so it remains cached gltf.extras.sourceVersion = ModelUtility.getAssetVersion(gltf); gltf.extras.sourceKHRTechniquesWebGL = defined( ModelUtility.getUsedExtensions(gltf).KHR_techniques_webgl ); this._sourceVersion = gltf.extras.sourceVersion; this._sourceKHRTechniquesWebGL = gltf.extras.sourceKHRTechniquesWebGL; updateVersion(gltf); addDefaults(gltf); var options = { addBatchIdToGeneratedShaders: this._addBatchIdToGeneratedShaders, modelEditor:this._modelEditor,//【hongguo】 田洪果 倾斜模型编辑修改 buildStyle: this._buildStyle //【hongguo】 田洪果 建筑物特效 }; processModelMaterialsCommon(gltf, options); processPbrMaterials(gltf, options); } this._sourceVersion = this.gltf.extras.sourceVersion; this._sourceKHRTechniquesWebGL = this.gltf.extras.sourceKHRTechniquesWebGL; // Skip dequantizing in the shader if not encoded this._dequantizeInShader = this._dequantizeInShader && DracoLoader.hasExtension(this); // We do this after to make sure that the ids don't change addBuffersToLoadResources(this); parseArticulations(this); parseTechniques(this); if (!this._loadRendererResourcesFromCache) { parseBufferViews(this); parseShaders(this); parsePrograms(this); parseTextures(this, context, supportsWebP); } parseMaterials(this); parseMeshes(this); parseNodes(this); // Start draco decoding DracoLoader.parse(this, context); loadResources.initialized = true; } if (!loadResources.finishedDecoding()) { DracoLoader.decodeModel(this, context).otherwise( ModelUtility.getFailedLoadFunction(this, "model", this.basePath) ); } if (loadResources.finishedDecoding() && !loadResources.resourcesParsed) { this._boundingSphere = ModelUtility.computeBoundingSphere(this); this._initialRadius = this._boundingSphere.radius; DracoLoader.cacheDataForModel(this); loadResources.resourcesParsed = true; } if ( loadResources.resourcesParsed && loadResources.pendingShaderLoads === 0 ) { ModelOutlineLoader.outlinePrimitives(this); createResources$5(this, frameState); } } if ( loadResources.finished() || (incrementallyLoadTextures && loadResources.finishedEverythingButTextureCreation()) ) { this._state = ModelState.LOADED; justLoaded = true; } } // Incrementally stream textures. if (defined(loadResources) && this._state === ModelState.LOADED) { if (incrementallyLoadTextures && !justLoaded) { createResources$5(this, frameState); } if (loadResources.finished()) { this._loadResources = undefined; // Clear CPU memory since WebGL resources were created. var resources = this._rendererResources; var cachedResources = this._cachedRendererResources; cachedResources.buffers = resources.buffers; cachedResources.vertexArrays = resources.vertexArrays; cachedResources.programs = resources.programs; cachedResources.sourceShaders = resources.sourceShaders; cachedResources.silhouettePrograms = resources.silhouettePrograms; cachedResources.textures = resources.textures; cachedResources.samplers = resources.samplers; cachedResources.renderStates = resources.renderStates; cachedResources.ready = true; // The normal attribute name is required for silhouettes, so get it before the gltf JSON is released this._normalAttributeName = ModelUtility.getAttributeOrUniformBySemantic( this.gltf, "NORMAL" ); // Vertex arrays are unique to this model, do not store in cache. if (defined(this._precreatedAttributes)) { cachedResources.vertexArrays = {}; } if (this.releaseGltfJson) { releaseCachedGltf(this); } } } var iblSupported = OctahedralProjectedCubeMap.isSupported(context); if (this._shouldUpdateSpecularMapAtlas && iblSupported) { this._shouldUpdateSpecularMapAtlas = false; this._specularEnvironmentMapAtlas = this._specularEnvironmentMapAtlas && this._specularEnvironmentMapAtlas.destroy(); this._specularEnvironmentMapAtlas = undefined; if (defined(this._specularEnvironmentMaps)) { this._specularEnvironmentMapAtlas = new OctahedralProjectedCubeMap( this._specularEnvironmentMaps ); var that = this; this._specularEnvironmentMapAtlas.readyPromise .then(function () { that._shouldRegenerateShaders = true; }) .otherwise(function (error) { console.error("Error loading specularEnvironmentMaps: " + error); }); } // Regenerate shaders to not use an environment map. Will be set to true again if there was a new environment map and it is ready. this._shouldRegenerateShaders = true; } if (defined(this._specularEnvironmentMapAtlas)) { this._specularEnvironmentMapAtlas.update(frameState); } var recompileWithDefaultAtlas = !defined(this._specularEnvironmentMapAtlas) && defined(frameState.specularEnvironmentMaps) && !this._useDefaultSpecularMaps; var recompileWithoutDefaultAtlas = !defined(frameState.specularEnvironmentMaps) && this._useDefaultSpecularMaps; var recompileWithDefaultSHCoeffs = !defined(this._sphericalHarmonicCoefficients) && defined(frameState.sphericalHarmonicCoefficients) && !this._useDefaultSphericalHarmonics; var recompileWithoutDefaultSHCoeffs = !defined(frameState.sphericalHarmonicCoefficients) && this._useDefaultSphericalHarmonics; this._shouldRegenerateShaders = this._shouldRegenerateShaders || recompileWithDefaultAtlas || recompileWithoutDefaultAtlas || recompileWithDefaultSHCoeffs || recompileWithoutDefaultSHCoeffs; this._useDefaultSpecularMaps = !defined(this._specularEnvironmentMapAtlas) && defined(frameState.specularEnvironmentMaps); this._useDefaultSphericalHarmonics = !defined(this._sphericalHarmonicCoefficients) && defined(frameState.sphericalHarmonicCoefficients); var silhouette = hasSilhouette(this, frameState); var translucent = isTranslucent(this); var invisible = isInvisible(this); var backFaceCulling = this.backFaceCulling; var displayConditionPassed = defined(this.distanceDisplayCondition) ? distanceDisplayConditionVisible(this, frameState) : true; var show = this.show && displayConditionPassed && this.scale !== 0.0 && (!invisible || silhouette); if ((show && this._state === ModelState.LOADED) || justLoaded) { var animated = this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty; this._cesiumAnimationsDirty = false; this._dirty = false; var modelMatrix = this.modelMatrix; var modeChanged = frameState.mode !== this._mode; this._mode = frameState.mode; // Model's model matrix needs to be updated var modelTransformChanged = !Matrix4.equals(this._modelMatrix, modelMatrix) || this._scale !== this.scale || this._minimumPixelSize !== this.minimumPixelSize || this.minimumPixelSize !== 0.0 || // Minimum pixel size changed or is enabled this._maximumScale !== this.maximumScale || this._heightReference !== this.heightReference || this._heightChanged || modeChanged; if (modelTransformChanged || justLoaded) { Matrix4.clone(modelMatrix, this._modelMatrix); updateClamping(this); if (defined(this._clampedModelMatrix)) { modelMatrix = this._clampedModelMatrix; } this._scale = this.scale; this._minimumPixelSize = this.minimumPixelSize; this._maximumScale = this.maximumScale; this._heightReference = this.heightReference; this._heightChanged = false; var scale = getScale(this, frameState); var computedModelMatrix = this._computedModelMatrix; Matrix4.multiplyByUniformScale(modelMatrix, scale, computedModelMatrix); if (this._upAxis === Axis$1.Y) { Matrix4.multiplyTransformation( computedModelMatrix, Axis$1.Y_UP_TO_Z_UP, computedModelMatrix ); } else if (this._upAxis === Axis$1.X) { Matrix4.multiplyTransformation( computedModelMatrix, Axis$1.X_UP_TO_Z_UP, computedModelMatrix ); } if (this.forwardAxis === Axis$1.Z) { // glTF 2.0 has a Z-forward convention that must be adapted here to X-forward. Matrix4.multiplyTransformation( computedModelMatrix, Axis$1.Z_UP_TO_X_UP, computedModelMatrix ); } } // Update modelMatrix throughout the graph as needed if (animated || modelTransformChanged || justLoaded) { updateNodeHierarchyModelMatrix( this, modelTransformChanged, justLoaded, frameState.mapProjection ); this._dirty = true; if (animated || justLoaded) { // Apply skins if animation changed any node transforms applySkins(this); } } if (this._perNodeShowDirty) { this._perNodeShowDirty = false; updatePerNodeShow(this); } updatePickIds(this); updateWireframe$1(this); updateShowBoundingVolume$1(this); updateShadows$1(this); updateClippingPlanes$1(this, frameState); // Regenerate shaders if ClippingPlaneCollection state changed or it was removed var clippingPlanes = this._clippingPlanes; var currentClippingPlanesState = 0; var useClippingPlanes = defined(clippingPlanes) && clippingPlanes.enabled && clippingPlanes.length > 0; // If defined, use the reference matrix to transform miscellaneous properties like // clipping planes and IBL instead of the modelMatrix. This is so that when // models are part of a tileset these properties get transformed relative to // a common reference (such as the root). var referenceMatrix = defaultValue(this.referenceMatrix, modelMatrix); if (useClippingPlanes) { var clippingPlanesMatrix = scratchClippingPlanesMatrix$2; clippingPlanesMatrix = Matrix4.multiply( context.uniformState.view3D, referenceMatrix, clippingPlanesMatrix ); clippingPlanesMatrix = Matrix4.multiply( clippingPlanesMatrix, clippingPlanes.modelMatrix, clippingPlanesMatrix ); this._clippingPlanesMatrix = Matrix4.inverseTranspose( clippingPlanesMatrix, this._clippingPlanesMatrix ); currentClippingPlanesState = clippingPlanes.clippingPlanesState; } var usesSH = defined(this._sphericalHarmonicCoefficients) || this._useDefaultSphericalHarmonics; var usesSM = (defined(this._specularEnvironmentMapAtlas) && this._specularEnvironmentMapAtlas.ready) || this._useDefaultSpecularMaps; if (usesSH || usesSM) { var iblReferenceFrameMatrix3 = scratchIBLReferenceFrameMatrix3; var iblReferenceFrameMatrix4 = scratchIBLReferenceFrameMatrix4; iblReferenceFrameMatrix4 = Matrix4.multiply( context.uniformState.view3D, referenceMatrix, iblReferenceFrameMatrix4 ); iblReferenceFrameMatrix3 = Matrix4.getMatrix3( iblReferenceFrameMatrix4, iblReferenceFrameMatrix3 ); iblReferenceFrameMatrix3 = Matrix3.getRotation( iblReferenceFrameMatrix3, iblReferenceFrameMatrix3 ); this._iblReferenceFrameMatrix = Matrix3.transpose( iblReferenceFrameMatrix3, this._iblReferenceFrameMatrix ); } var shouldRegenerateShaders = this._shouldRegenerateShaders; shouldRegenerateShaders = shouldRegenerateShaders || this._clippingPlanesState !== currentClippingPlanesState; this._clippingPlanesState = currentClippingPlanesState; // Regenerate shaders if color shading changed from last update var currentlyColorShadingEnabled = isColorShadingEnabled(this); if (currentlyColorShadingEnabled !== this._colorShadingEnabled) { this._colorShadingEnabled = currentlyColorShadingEnabled; shouldRegenerateShaders = true; } if (shouldRegenerateShaders) { regenerateShaders(this, frameState); } else { updateColor(this, frameState, false); updateBackFaceCulling$1(this, frameState, false); updateSilhouette(this, frameState, false); } } if (justLoaded) { // Called after modelMatrix update. var model = this; frameState.afterRender.push(function () { model._ready = true; model._readyPromise.resolve(model); }); return; } // We don't check show at the top of the function since we // want to be able to progressively load models when they are not shown, // and then have them visible immediately when show is set to true. if (show && !this._ignoreCommands) { // PERFORMANCE_IDEA: This is terrible var commandList = frameState.commandList; var passes = frameState.passes; var nodeCommands = this._nodeCommands; var length = nodeCommands.length; var i; var nc; var idl2D = frameState.mapProjection.ellipsoid.maximumRadius * CesiumMath.PI; var boundingVolume; if (passes.render || (passes.pick && this.allowPicking)) { for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { var command = nc.command; if (silhouette) { command = nc.silhouetteModelCommand; } else if (translucent) { command = nc.translucentCommand; } else if (!backFaceCulling) { command = nc.disableCullingCommand; } commandList.push(command); boundingVolume = nc.command.boundingVolume; if ( frameState.mode === SceneMode$1.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D) ) { var command2D = nc.command2D; if (silhouette) { command2D = nc.silhouetteModelCommand2D; } else if (translucent) { command2D = nc.translucentCommand2D; } else if (!backFaceCulling) { command2D = nc.disableCullingCommand2D; } commandList.push(command2D); } } } if (silhouette && !passes.pick) { // Render second silhouette pass for (i = 0; i < length; ++i) { nc = nodeCommands[i]; if (nc.show) { commandList.push(nc.silhouetteColorCommand); boundingVolume = nc.command.boundingVolume; if ( frameState.mode === SceneMode$1.SCENE2D && (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D) ) { commandList.push(nc.silhouetteColorCommand2D); } } } } } } var credit = this._credit; if (defined(credit)) { frameState.creditDisplay.addCredit(credit); } var resourceCredits = this._resourceCredits; var creditCount = resourceCredits.length; for (var c = 0; c < creditCount; c++) { frameState.creditDisplay.addCredit(resourceCredits[c]); } }; function destroyIfNotCached(rendererResources, cachedRendererResources) { if (rendererResources.programs !== cachedRendererResources.programs) { destroy(rendererResources.programs); } if ( rendererResources.silhouettePrograms !== cachedRendererResources.silhouettePrograms ) { destroy(rendererResources.silhouettePrograms); } } // Run from update iff: // - everything is loaded // - clipping planes state change OR color state set // Run this from destructor after removing color state and clipping plane state function regenerateShaders(model, frameState) { // In regards to _cachedRendererResources: // Fair to assume that this is data that should just never get modified due to clipping planes or model color. // So if clipping planes or model color active: // - delink _rendererResources.*programs and create new dictionaries. // - do NOT destroy any programs - might be used by copies of the model or by might be needed in the future if clipping planes/model color is deactivated // If clipping planes and model color inactive: // - destroy _rendererResources.*programs // - relink _rendererResources.*programs to _cachedRendererResources // In both cases, need to mark commands as dirty, re-run derived commands (elsewhere) var rendererResources = model._rendererResources; var cachedRendererResources = model._cachedRendererResources; destroyIfNotCached(rendererResources, cachedRendererResources); var programId; if ( isClippingEnabled(model) || isColorShadingEnabled(model) || model._shouldRegenerateShaders ) { model._shouldRegenerateShaders = false; rendererResources.programs = {}; rendererResources.silhouettePrograms = {}; var visitedPrograms = {}; var techniques = model._sourceTechniques; var technique; for (var techniqueId in techniques) { if (techniques.hasOwnProperty(techniqueId)) { technique = techniques[techniqueId]; programId = technique.program; if (!visitedPrograms[programId]) { visitedPrograms[programId] = true; recreateProgram({ programId: programId, techniqueId: techniqueId, }, model, frameState.context ); } } } } else { rendererResources.programs = cachedRendererResources.programs; rendererResources.silhouettePrograms = cachedRendererResources.silhouettePrograms; } // Fix all the commands, marking them as dirty so everything that derives will re-derive var rendererPrograms = rendererResources.programs; var nodeCommands = model._nodeCommands; var commandCount = nodeCommands.length; for (var i = 0; i < commandCount; ++i) { var nodeCommand = nodeCommands[i]; programId = nodeCommand.programId; var renderProgram = rendererPrograms[programId]; nodeCommand.command.shaderProgram = renderProgram; if (defined(nodeCommand.command2D)) { nodeCommand.command2D.shaderProgram = renderProgram; } } // Force update silhouette commands/shaders updateColor(model, frameState, true); updateBackFaceCulling$1(model, frameState, true); updateSilhouette(model, frameState, true); } /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see Model#destroy */ Model.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * model = model && model.destroy(); * * @see Model#isDestroyed */ Model.prototype.destroy = function () { // Vertex arrays are unique to this model, destroy here. if (defined(this._precreatedAttributes)) { destroy(this._rendererResources.vertexArrays); } if (defined(this._removeUpdateHeightCallback)) { this._removeUpdateHeightCallback(); this._removeUpdateHeightCallback = undefined; } if (defined(this._terrainProviderChangedCallback)) { this._terrainProviderChangedCallback(); this._terrainProviderChangedCallback = undefined; } // Shaders modified for clipping and for color don't get cached, so destroy these manually if (defined(this._cachedRendererResources)) { destroyIfNotCached(this._rendererResources, this._cachedRendererResources); } this._rendererResources = undefined; this._cachedRendererResources = this._cachedRendererResources && this._cachedRendererResources.release(); DracoLoader.destroyCachedDataForModel(this); var pickIds = this._pickIds; var length = pickIds.length; for (var i = 0; i < length; ++i) { pickIds[i].destroy(); } releaseCachedGltf(this); this._quantizedVertexShaders = undefined; // Only destroy the ClippingPlaneCollection if this is the owner - if this model is part of a Cesium3DTileset, // _clippingPlanes references a ClippingPlaneCollection that this model does not own. var clippingPlaneCollection = this._clippingPlanes; if ( defined(clippingPlaneCollection) && !clippingPlaneCollection.isDestroyed() && clippingPlaneCollection.owner === this ) { clippingPlaneCollection.destroy(); } this._clippingPlanes = undefined; this._specularEnvironmentMapAtlas = this._specularEnvironmentMapAtlas && this._specularEnvironmentMapAtlas.destroy(); return destroyObject(this); }; // exposed for testing Model._getClippingFunction = getClippingFunction; Model._modifyShaderForColor = modifyShaderForColor; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Batched3DModel|Batched 3D Model} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Batched3DModel3DTileContent * @constructor * * @private */ function Batched3DModel3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._model = undefined; this._batchTable = undefined; this._features = undefined; // Populate from gltf when available this._batchIdAttributeName = undefined; this._diffuseAttributeOrUniformName = {}; this._rtcCenterTransform = undefined; this._contentModelMatrix = undefined; this.featurePropertiesDirty = false; initialize$7(this, arrayBuffer, byteOffset); } // This can be overridden for testing purposes Batched3DModel3DTileContent._deprecationWarning = deprecationWarning; Object.defineProperties(Batched3DModel3DTileContent.prototype, { featuresLength: { get: function () { return this._batchTable.featuresLength; }, }, pointsLength: { get: function () { return 0; }, }, trianglesLength: { get: function () { return this._model.trianglesLength; }, }, geometryByteLength: { get: function () { return this._model.geometryByteLength; }, }, texturesByteLength: { get: function () { return this._model.texturesByteLength; }, }, batchTableByteLength: { get: function () { return this._batchTable.memorySizeInBytes; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._model.readyPromise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return this._batchTable; }, }, }); var sizeOfUint32$5 = Uint32Array.BYTES_PER_ELEMENT; function getBatchIdAttributeName(gltf) { var batchIdAttributeName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "_BATCHID" ); if (!defined(batchIdAttributeName)) { batchIdAttributeName = ModelUtility.getAttributeOrUniformBySemantic( gltf, "BATCHID" ); if (defined(batchIdAttributeName)) { Batched3DModel3DTileContent._deprecationWarning( "b3dm-legacy-batchid", "The glTF in this b3dm uses the semantic `BATCHID`. Application-specific semantics should be prefixed with an underscore: `_BATCHID`." ); } } return batchIdAttributeName; } function getVertexShaderCallback$1(content) { return function (vs, programId) { var batchTable = content._batchTable; var handleTranslucent = !defined(content._tileset.classificationType); var gltf = content._model.gltf; if (defined(gltf)) { content._batchIdAttributeName = getBatchIdAttributeName(gltf); content._diffuseAttributeOrUniformName[ programId ] = ModelUtility.getDiffuseAttributeOrUniform(gltf, programId); } var callback = batchTable.getVertexShaderCallback( handleTranslucent, content._batchIdAttributeName, content._diffuseAttributeOrUniformName[programId] ); return defined(callback) ? callback(vs) : vs; }; } function getFragmentShaderCallback$1(content) { return function (fs, programId) { var batchTable = content._batchTable; var handleTranslucent = !defined(content._tileset.classificationType); var gltf = content._model.gltf; if (defined(gltf)) { content._diffuseAttributeOrUniformName[ programId ] = ModelUtility.getDiffuseAttributeOrUniform(gltf, programId); } var callback = batchTable.getFragmentShaderCallback( handleTranslucent, content._diffuseAttributeOrUniformName[programId] ); return defined(callback) ? callback(fs) : fs; }; } function getPickIdCallback$1(content) { return function () { return content._batchTable.getPickId(); }; } function getClassificationFragmentShaderCallback(content) { return function (fs) { var batchTable = content._batchTable; var callback = batchTable.getClassificationFragmentShaderCallback(); return defined(callback) ? callback(fs) : fs; }; } function createColorChangedCallback$2(content) { return function (batchId, color) { content._model.updateCommands(batchId, color); }; } function initialize$7(content, arrayBuffer, byteOffset) { var tileset = content._tileset; var tile = content._tile; var resource = content._resource; var byteStart = defaultValue(byteOffset, 0); byteOffset = byteStart; var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32$5; // Skip magic var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Batched 3D Model version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32$5; var byteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$5; var featureTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$5; var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$5; var batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$5; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$5; var batchLength; // Legacy header #1: [batchLength] [batchTableByteLength] // Legacy header #2: [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength] // Current header: [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] // If the header is in the first legacy format 'batchTableJsonByteLength' will be the start of the JSON string (a quotation mark) or the glTF magic. // Accordingly its first byte will be either 0x22 or 0x67, and so the minimum uint32 expected is 0x22000000 = 570425344 = 570MB. It is unlikely that the feature table JSON will exceed this length. // The check for the second legacy format is similar, except it checks 'batchTableBinaryByteLength' instead if (batchTableJsonByteLength >= 570425344) { // First legacy check byteOffset -= sizeOfUint32$5 * 2; batchLength = featureTableJsonByteLength; batchTableJsonByteLength = featureTableBinaryByteLength; batchTableBinaryByteLength = 0; featureTableJsonByteLength = 0; featureTableBinaryByteLength = 0; Batched3DModel3DTileContent._deprecationWarning( "b3dm-legacy-header", "This b3dm header is using the legacy format [batchLength] [batchTableByteLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Batched3DModel." ); } else if (batchTableBinaryByteLength >= 570425344) { // Second legacy check byteOffset -= sizeOfUint32$5; batchLength = batchTableJsonByteLength; batchTableJsonByteLength = featureTableJsonByteLength; batchTableBinaryByteLength = featureTableBinaryByteLength; featureTableJsonByteLength = 0; featureTableBinaryByteLength = 0; Batched3DModel3DTileContent._deprecationWarning( "b3dm-legacy-header", "This b3dm header is using the legacy format [batchTableJsonByteLength] [batchTableBinaryByteLength] [batchLength]. The new format is [featureTableJsonByteLength] [featureTableBinaryByteLength] [batchTableJsonByteLength] [batchTableBinaryByteLength] from https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Batched3DModel." ); } var featureTableJson; if (featureTableJsonByteLength === 0) { featureTableJson = { BATCH_LENGTH: defaultValue(batchLength, 0), }; } else { featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJsonByteLength ); byteOffset += featureTableJsonByteLength; } var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; var featureTable = new Cesium3DTileFeatureTable( featureTableJson, featureTableBinary ); batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); featureTable.featuresLength = batchLength; var batchTableJson; var batchTableBinary; if (batchTableJsonByteLength > 0) { // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the // arraybuffer/string compressed in memory and then decompress it when it is first accessed. // // We could also make another request for it, but that would make the property set/get // API async, and would double the number of numbers in some cases. batchTableJson = getJsonFromTypedArray( uint8Array, byteOffset, batchTableJsonByteLength ); byteOffset += batchTableJsonByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array( arrayBuffer, byteOffset, batchTableBinaryByteLength ); // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); byteOffset += batchTableBinaryByteLength; } } var colorChangedCallback; if (defined(tileset.classificationType)) { colorChangedCallback = createColorChangedCallback$2(content); } var batchTable = new Cesium3DTileBatchTable( content, batchLength, batchTableJson, batchTableBinary, colorChangedCallback ); content._batchTable = batchTable; var gltfByteLength = byteStart + byteLength - byteOffset; if (gltfByteLength === 0) { throw new RuntimeError("glTF byte length must be greater than 0."); } var gltfView; if (byteOffset % 4 === 0) { gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); } else { // Create a copy of the glb so that it is 4-byte aligned Batched3DModel3DTileContent._deprecationWarning( "b3dm-glb-unaligned", "The embedded glb is not aligned to a 4-byte boundary." ); gltfView = new Uint8Array( uint8Array.subarray(byteOffset, byteOffset + gltfByteLength) ); } var pickObject = { content: content, primitive: tileset, }; content._rtcCenterTransform = Matrix4.IDENTITY; var rtcCenter = featureTable.getGlobalProperty( "RTC_CENTER", ComponentDatatype$1.FLOAT, 3 ); if (defined(rtcCenter)) { content._rtcCenterTransform = Matrix4.fromTranslation( Cartesian3.fromArray(rtcCenter) ); } content._contentModelMatrix = Matrix4.multiply( tile.computedTransform, content._rtcCenterTransform, new Matrix4() ); if (!defined(tileset.classificationType)) { // PERFORMANCE_IDEA: patch the shader on demand, e.g., the first time show/color changes. // The pick shader still needs to be patched. content._model = new Model({ gltf: gltfView, cull: false, // The model is already culled by 3D Tiles releaseGltfJson: true, // Models are unique and will not benefit from caching so save memory opaquePass: Pass$1.CESIUM_3D_TILE, // Draw opaque portions of the model during the 3D Tiles pass basePath: resource, requestType: RequestType$1.TILES3D, modelMatrix: content._contentModelMatrix, upAxis: tileset._gltfUpAxis, forwardAxis: Axis$1.X, shadows: tileset.shadows, debugWireframe: tileset.debugWireframe, incrementallyLoadTextures: false, vertexShaderLoaded: getVertexShaderCallback$1(content), fragmentShaderLoaded: getFragmentShaderCallback$1(content), uniformMapLoaded: batchTable.getUniformMapCallback(), pickIdLoaded: getPickIdCallback$1(content), addBatchIdToGeneratedShaders: batchLength > 0, // If the batch table has values in it, generated shaders will need a batchId attribute pickObject: pickObject, imageBasedLightingFactor: tileset.imageBasedLightingFactor, lightColor: tileset.lightColor, luminanceAtZenith: tileset.luminanceAtZenith, sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, specularEnvironmentMaps: tileset.specularEnvironmentMaps, backFaceCulling: tileset.backFaceCulling, buildStyle: tileset.buildStyle, //【hongguo】 田洪果 建筑物特效 modelEditor:tileset.modelEditor,//【hongguo】 田洪果 倾斜模型编辑修改 }); content._model.readyPromise.then(function (model) { model.activeAnimations.addAll({ loop: ModelAnimationLoop$1.REPEAT, }); }); } else { // This transcodes glTF to an internal representation for geometry so we can take advantage of the re-batching of vector data. // For a list of limitations on the input glTF, see the documentation for classificationType of Cesium3DTileset. content._model = new ClassificationModel({ gltf: gltfView, cull: false, // The model is already culled by 3D Tiles basePath: resource, requestType: RequestType$1.TILES3D, modelMatrix: content._contentModelMatrix, upAxis: tileset._gltfUpAxis, forwardAxis: Axis$1.X, debugWireframe: tileset.debugWireframe, vertexShaderLoaded: getVertexShaderCallback$1(content), classificationShaderLoaded: getClassificationFragmentShaderCallback( content ), uniformMapLoaded: batchTable.getUniformMapCallback(), pickIdLoaded: getPickIdCallback$1(content), classificationType: tileset._classificationType, batchTable: batchTable, }); } } function createFeatures$4(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && featuresLength > 0) { var features = new Array(featuresLength); for (var i = 0; i < featuresLength; ++i) { features[i] = new Cesium3DTileFeature(content, i); } content._features = features; } } Batched3DModel3DTileContent.prototype.hasProperty = function (batchId, name) { return this._batchTable.hasProperty(batchId, name); }; Batched3DModel3DTileContent.prototype.getFeature = function (batchId) { //>>includeStart('debug', pragmas.debug); var featuresLength = this.featuresLength; if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + (featuresLength - 1) + ")." ); } //>>includeEnd('debug'); createFeatures$4(this); return this._features[batchId]; }; Batched3DModel3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) { color = enabled ? color : Color.WHITE; if (this.featuresLength === 0) { this._model.color = color; } else { this._batchTable.setAllColor(color); } }; Batched3DModel3DTileContent.prototype.applyStyle = function (style) { if (this.featuresLength === 0) { var hasColorStyle = defined(style) && defined(style.color); var hasShowStyle = defined(style) && defined(style.show); this._model.color = hasColorStyle ? style.color.evaluateColor(undefined, this._model.color) : Color.clone(Color.WHITE, this._model.color); this._model.show = hasShowStyle ? style.show.evaluate(undefined) : true; } else { this._batchTable.applyStyle(style); } }; Batched3DModel3DTileContent.prototype.update = function (tileset, frameState) { var commandStart = frameState.commandList.length; // In the PROCESSING state we may be calling update() to move forward // the content's resource loading. In the READY state, it will // actually generate commands. this._batchTable.update(tileset, frameState); this._contentModelMatrix = Matrix4.multiply( this._tile.computedTransform, this._rtcCenterTransform, this._contentModelMatrix ); this._model.modelMatrix = this._contentModelMatrix; this._model.shadows = this._tileset.shadows; this._model.imageBasedLightingFactor = this._tileset.imageBasedLightingFactor; this._model.lightColor = this._tileset.lightColor; this._model.luminanceAtZenith = this._tileset.luminanceAtZenith; this._model.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients; this._model.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps; this._model.backFaceCulling = this._tileset.backFaceCulling; this._model.debugWireframe = this._tileset.debugWireframe; // Update clipping planes var tilesetClippingPlanes = this._tileset.clippingPlanes; this._model.referenceMatrix = this._tileset.clippingPlanesOriginMatrix; if (defined(tilesetClippingPlanes) && this._tile.clippingPlanesDirty) { // Dereference the clipping planes from the model if they are irrelevant. // Link/Dereference directly to avoid ownership checks. // This will also trigger synchronous shader regeneration to remove or add the clipping plane and color blending code. this._model._clippingPlanes = tilesetClippingPlanes.enabled && this._tile._isClipped ? tilesetClippingPlanes : undefined; } // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection. if ( defined(tilesetClippingPlanes) && defined(this._model._clippingPlanes) && this._model._clippingPlanes !== tilesetClippingPlanes ) { this._model._clippingPlanes = tilesetClippingPlanes; } this._model.update(frameState); // If any commands were pushed, add derived commands var commandEnd = frameState.commandList.length; if ( commandStart < commandEnd && (frameState.passes.render || frameState.passes.pick) && !defined(tileset.classificationType) ) { this._batchTable.addDerivedCommands(frameState, commandStart); } }; Batched3DModel3DTileContent.prototype.isDestroyed = function () { return false; }; Batched3DModel3DTileContent.prototype.destroy = function () { this._model = this._model && this._model.destroy(); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Composite|Composite} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Composite3DTileContent * @constructor * * @private */ function Composite3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset, factory ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._contents = []; this._readyPromise = when.defer(); initialize$6(this, arrayBuffer, byteOffset, factory); } Object.defineProperties(Composite3DTileContent.prototype, { featurePropertiesDirty: { get: function () { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { if (contents[i].featurePropertiesDirty) { return true; } } return false; }, set: function (value) { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { contents[i].featurePropertiesDirty = value; } }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call featuresLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ featuresLength: { get: function () { return 0; }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call pointsLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ pointsLength: { get: function () { return 0; }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call trianglesLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ trianglesLength: { get: function () { return 0; }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call geometryByteLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ geometryByteLength: { get: function () { return 0; }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call texturesByteLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ texturesByteLength: { get: function () { return 0; }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns 0. Instead call batchTableByteLength for a tile in the composite. * @memberof Composite3DTileContent.prototype */ batchTableByteLength: { get: function () { return 0; }, }, innerContents: { get: function () { return this._contents; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns undefined. Instead call batchTable for a tile in the composite. * @memberof Composite3DTileContent.prototype */ batchTable: { get: function () { return undefined; }, }, }); var sizeOfUint32$4 = Uint32Array.BYTES_PER_ELEMENT; function initialize$6(content, arrayBuffer, byteOffset, factory) { byteOffset = defaultValue(byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32$4; // Skip magic var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Composite Tile version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32$4; // Skip byteLength byteOffset += sizeOfUint32$4; var tilesLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$4; var contentPromises = []; for (var i = 0; i < tilesLength; ++i) { var tileType = getMagic(uint8Array, byteOffset); // Tile byte length is stored after magic and version var tileByteLength = view.getUint32(byteOffset + sizeOfUint32$4 * 2, true); var contentFactory = factory[tileType]; if (defined(contentFactory)) { var innerContent = contentFactory( content._tileset, content._tile, content._resource, arrayBuffer, byteOffset ); content._contents.push(innerContent); contentPromises.push(innerContent.readyPromise); } else { throw new RuntimeError( "Unknown tile content type, " + tileType + ", inside Composite tile" ); } byteOffset += tileByteLength; } when .all(contentPromises) .then(function () { content._readyPromise.resolve(content); }) .otherwise(function (error) { content._readyPromise.reject(error); }); } /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns false. Instead call hasProperty for a tile in the composite. */ Composite3DTileContent.prototype.hasProperty = function (batchId, name) { return false; }; /** * Part of the {@link Cesium3DTileContent} interface. Composite3DTileContent * always returns undefined. Instead call getFeature for a tile in the composite. */ Composite3DTileContent.prototype.getFeature = function (batchId) { return undefined; }; Composite3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { contents[i].applyDebugSettings(enabled, color); } }; Composite3DTileContent.prototype.applyStyle = function (style) { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { contents[i].applyStyle(style); } }; Composite3DTileContent.prototype.update = function (tileset, frameState) { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { contents[i].update(tileset, frameState); } }; Composite3DTileContent.prototype.isDestroyed = function () { return false; }; Composite3DTileContent.prototype.destroy = function () { var contents = this._contents; var length = contents.length; for (var i = 0; i < length; ++i) { contents[i].destroy(); } return destroyObject(this); }; /** * Creates a batch of box, cylinder, ellipsoid and/or sphere geometries intersecting terrain or 3D Tiles. * * @alias Vector3DTileGeometry * @constructor * * @param {Object} options An object with following properties: * @param {Float32Array} [options.boxes] The boxes in the tile. * @param {Uint16Array} [options.boxBatchIds] The batch ids for each box. * @param {Float32Array} [options.cylinders] The cylinders in the tile. * @param {Uint16Array} [options.cylinderBatchIds] The batch ids for each cylinder. * @param {Float32Array} [options.ellipsoids] The ellipsoids in the tile. * @param {Uint16Array} [options.ellipsoidBatchIds] The batch ids for each ellipsoid. * @param {Float32Array} [options.spheres] The spheres in the tile. * @param {Uint16Array} [options.sphereBatchIds] The batch ids for each sphere. * @param {Cartesian3} options.center The RTC center of all geometries. * @param {Matrix4} options.modelMatrix The model matrix of all geometries. Applied after the individual geometry model matrices. * @param {Cesium3DTileBatchTable} options.batchTable The batch table. * @param {BoundingSphere} options.boundingVolume The bounding volume containing all of the geometry in the tile. * * @private */ function Vector3DTileGeometry(options) { // these will all be released after the primitive is created this._boxes = options.boxes; this._boxBatchIds = options.boxBatchIds; this._cylinders = options.cylinders; this._cylinderBatchIds = options.cylinderBatchIds; this._ellipsoids = options.ellipsoids; this._ellipsoidBatchIds = options.ellipsoidBatchIds; this._spheres = options.spheres; this._sphereBatchIds = options.sphereBatchIds; this._modelMatrix = options.modelMatrix; this._batchTable = options.batchTable; this._boundingVolume = options.boundingVolume; this._center = options.center; if (!defined(this._center)) { if (defined(this._boundingVolume)) { this._center = Cartesian3.clone(this._boundingVolume.center); } else { this._center = Cartesian3.clone(Cartesian3.ZERO); } } this._boundingVolumes = undefined; this._batchedIndices = undefined; this._indices = undefined; this._indexOffsets = undefined; this._indexCounts = undefined; this._positions = undefined; this._vertexBatchIds = undefined; this._batchIds = undefined; this._batchTableColors = undefined; this._packedBuffer = undefined; this._ready = false; this._readyPromise = when.defer(); this._verticesPromise = undefined; this._primitive = undefined; /** * Draws the wireframe of the classification geometries. * @type {Boolean} * @default false */ this.debugWireframe = false; /** * Forces a re-batch instead of waiting after a number of frames have been rendered. For testing only. * @type {Boolean} * @default false */ this.forceRebatch = false; /** * What this tile will classify. * @type {ClassificationType} * @default ClassificationType.BOTH */ this.classificationType = ClassificationType$1.BOTH; } Object.defineProperties(Vector3DTileGeometry.prototype, { /** * Gets the number of triangles. * * @memberof Vector3DTileGeometry.prototype * * @type {Number} * @readonly */ trianglesLength: { get: function () { if (defined(this._primitive)) { return this._primitive.trianglesLength; } return 0; }, }, /** * Gets the geometry memory in bytes. * * @memberof Vector3DTileGeometry.prototype * * @type {Number} * @readonly */ geometryByteLength: { get: function () { if (defined(this._primitive)) { return this._primitive.geometryByteLength; } return 0; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTileGeometry.prototype * @type {Promise} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); Vector3DTileGeometry.packedBoxLength = Matrix4.packedLength + Cartesian3.packedLength; Vector3DTileGeometry.packedCylinderLength = Matrix4.packedLength + 2; Vector3DTileGeometry.packedEllipsoidLength = Matrix4.packedLength + Cartesian3.packedLength; Vector3DTileGeometry.packedSphereLength = Cartesian3.packedLength + 1; function packBuffer$3(geometries) { var packedBuffer = new Float64Array( Matrix4.packedLength + Cartesian3.packedLength ); var offset = 0; Cartesian3.pack(geometries._center, packedBuffer, offset); offset += Cartesian3.packedLength; Matrix4.pack(geometries._modelMatrix, packedBuffer, offset); return packedBuffer; } function unpackBuffer$1(geometries, packedBuffer) { var offset = 0; var indicesBytesPerElement = packedBuffer[offset++]; var numBVS = packedBuffer[offset++]; var bvs = (geometries._boundingVolumes = new Array(numBVS)); for (var i = 0; i < numBVS; ++i) { bvs[i] = BoundingSphere.unpack(packedBuffer, offset); offset += BoundingSphere.packedLength; } var numBatchedIndices = packedBuffer[offset++]; var bis = (geometries._batchedIndices = new Array(numBatchedIndices)); for (var j = 0; j < numBatchedIndices; ++j) { var color = Color.unpack(packedBuffer, offset); offset += Color.packedLength; var indexOffset = packedBuffer[offset++]; var count = packedBuffer[offset++]; var length = packedBuffer[offset++]; var batchIds = new Array(length); for (var k = 0; k < length; ++k) { batchIds[k] = packedBuffer[offset++]; } bis[j] = new Vector3DTileBatch({ color: color, offset: indexOffset, count: count, batchIds: batchIds, }); } return indicesBytesPerElement; } var createVerticesTaskProcessor$3 = new TaskProcessor( "createVectorTileGeometries", 5 ); var scratchColor$i = new Color(); function createPrimitive$1(geometries) { if (defined(geometries._primitive)) { return; } if (!defined(geometries._verticesPromise)) { var boxes = geometries._boxes; var boxBatchIds = geometries._boxBatchIds; var cylinders = geometries._cylinders; var cylinderBatchIds = geometries._cylinderBatchIds; var ellipsoids = geometries._ellipsoids; var ellipsoidBatchIds = geometries._ellipsoidBatchIds; var spheres = geometries._spheres; var sphereBatchIds = geometries._sphereBatchIds; var batchTableColors = geometries._batchTableColors; var packedBuffer = geometries._packedBuffer; if (!defined(batchTableColors)) { // Copy because they may be the views on the same buffer. var length = 0; if (defined(geometries._boxes)) { boxes = geometries._boxes = arraySlice(boxes); boxBatchIds = geometries._boxBatchIds = arraySlice(boxBatchIds); length += boxBatchIds.length; } if (defined(geometries._cylinders)) { cylinders = geometries._cylinders = arraySlice(cylinders); cylinderBatchIds = geometries._cylinderBatchIds = arraySlice( cylinderBatchIds ); length += cylinderBatchIds.length; } if (defined(geometries._ellipsoids)) { ellipsoids = geometries._ellipsoids = arraySlice(ellipsoids); ellipsoidBatchIds = geometries._ellipsoidBatchIds = arraySlice( ellipsoidBatchIds ); length += ellipsoidBatchIds.length; } if (defined(geometries._spheres)) { spheres = geometries._sphere = arraySlice(spheres); sphereBatchIds = geometries._sphereBatchIds = arraySlice( sphereBatchIds ); length += sphereBatchIds.length; } batchTableColors = geometries._batchTableColors = new Uint32Array(length); var batchTable = geometries._batchTable; for (var i = 0; i < length; ++i) { var color = batchTable.getColor(i, scratchColor$i); batchTableColors[i] = color.toRgba(); } packedBuffer = geometries._packedBuffer = packBuffer$3(geometries); } var transferrableObjects = []; if (defined(boxes)) { transferrableObjects.push(boxes.buffer, boxBatchIds.buffer); } if (defined(cylinders)) { transferrableObjects.push(cylinders.buffer, cylinderBatchIds.buffer); } if (defined(ellipsoids)) { transferrableObjects.push(ellipsoids.buffer, ellipsoidBatchIds.buffer); } if (defined(spheres)) { transferrableObjects.push(spheres.buffer, sphereBatchIds.buffer); } transferrableObjects.push(batchTableColors.buffer, packedBuffer.buffer); var parameters = { boxes: defined(boxes) ? boxes.buffer : undefined, boxBatchIds: defined(boxes) ? boxBatchIds.buffer : undefined, cylinders: defined(cylinders) ? cylinders.buffer : undefined, cylinderBatchIds: defined(cylinders) ? cylinderBatchIds.buffer : undefined, ellipsoids: defined(ellipsoids) ? ellipsoids.buffer : undefined, ellipsoidBatchIds: defined(ellipsoids) ? ellipsoidBatchIds.buffer : undefined, spheres: defined(spheres) ? spheres.buffer : undefined, sphereBatchIds: defined(spheres) ? sphereBatchIds.buffer : undefined, batchTableColors: batchTableColors.buffer, packedBuffer: packedBuffer.buffer, }; var verticesPromise = (geometries._verticesPromise = createVerticesTaskProcessor$3.scheduleTask( parameters, transferrableObjects )); if (!defined(verticesPromise)) { // Postponed return; } verticesPromise.then(function (result) { var packedBuffer = new Float64Array(result.packedBuffer); var indicesBytesPerElement = unpackBuffer$1(geometries, packedBuffer); if (indicesBytesPerElement === 2) { geometries._indices = new Uint16Array(result.indices); } else { geometries._indices = new Uint32Array(result.indices); } geometries._indexOffsets = new Uint32Array(result.indexOffsets); geometries._indexCounts = new Uint32Array(result.indexCounts); geometries._positions = new Float32Array(result.positions); geometries._vertexBatchIds = new Uint16Array(result.vertexBatchIds); geometries._batchIds = new Uint16Array(result.batchIds); geometries._ready = true; }); } if (geometries._ready && !defined(geometries._primitive)) { geometries._primitive = new Vector3DTilePrimitive({ batchTable: geometries._batchTable, positions: geometries._positions, batchIds: geometries._batchIds, vertexBatchIds: geometries._vertexBatchIds, indices: geometries._indices, indexOffsets: geometries._indexOffsets, indexCounts: geometries._indexCounts, batchedIndices: geometries._batchedIndices, boundingVolume: geometries._boundingVolume, boundingVolumes: geometries._boundingVolumes, center: geometries._center, pickObject: defaultValue(geometries._pickObject, geometries), }); geometries._boxes = undefined; geometries._boxBatchIds = undefined; geometries._cylinders = undefined; geometries._cylinderBatchIds = undefined; geometries._ellipsoids = undefined; geometries._ellipsoidBatchIds = undefined; geometries._spheres = undefined; geometries._sphereBatchIds = undefined; geometries._center = undefined; geometries._modelMatrix = undefined; geometries._batchTable = undefined; geometries._boundingVolume = undefined; geometries._boundingVolumes = undefined; geometries._batchedIndices = undefined; geometries._indices = undefined; geometries._indexOffsets = undefined; geometries._indexCounts = undefined; geometries._positions = undefined; geometries._vertexBatchIds = undefined; geometries._batchIds = undefined; geometries._batchTableColors = undefined; geometries._packedBuffer = undefined; geometries._verticesPromise = undefined; geometries._readyPromise.resolve(); } } /** * Creates features for each geometry and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed. */ Vector3DTileGeometry.prototype.createFeatures = function (content, features) { this._primitive.createFeatures(content, features); }; /** * Colors the entire tile when enabled is true. The resulting color will be (geometry batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTileGeometry.prototype.applyDebugSettings = function (enabled, color) { this._primitive.applyDebugSettings(enabled, color); }; /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTileGeometry.prototype.applyStyle = function (style, features) { this._primitive.applyStyle(style, features); }; /** * Call when updating the color of a geometry with batchId changes color. The geometries will need to be re-batched * on the next update. * * @param {Number} batchId The batch id of the geometries whose color has changed. * @param {Color} color The new polygon color. */ Vector3DTileGeometry.prototype.updateCommands = function (batchId, color) { this._primitive.updateCommands(batchId, color); }; /** * Updates the batches and queues the commands for rendering. * * @param {FrameState} frameState The current frame state. */ Vector3DTileGeometry.prototype.update = function (frameState) { createPrimitive$1(this); if (!this._ready) { return; } this._primitive.debugWireframe = this.debugWireframe; this._primitive.forceRebatch = this.forceRebatch; this._primitive.classificationType = this.classificationType; this._primitive.update(frameState); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTileGeometry.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTileGeometry.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); return destroyObject(this); }; /** *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Geometry3DTileContent * @constructor * * @private */ function Geometry3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._geometries = undefined; this._contentReadyPromise = undefined; this._readyPromise = when.defer(); this._batchTable = undefined; this._features = undefined; /** * Part of the {@link Cesium3DTileContent} interface. */ this.featurePropertiesDirty = false; initialize$5(this, arrayBuffer, byteOffset); } Object.defineProperties(Geometry3DTileContent.prototype, { featuresLength: { get: function () { return defined(this._batchTable) ? this._batchTable.featuresLength : 0; }, }, pointsLength: { get: function () { return 0; }, }, trianglesLength: { get: function () { if (defined(this._geometries)) { return this._geometries.trianglesLength; } return 0; }, }, geometryByteLength: { get: function () { if (defined(this._geometries)) { return this._geometries.geometryByteLength; } return 0; }, }, texturesByteLength: { get: function () { return 0; }, }, batchTableByteLength: { get: function () { return defined(this._batchTable) ? this._batchTable.memorySizeInBytes : 0; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return this._batchTable; }, }, }); function createColorChangedCallback$1(content) { return function (batchId, color) { if (defined(content._geometries)) { content._geometries.updateCommands(batchId, color); } }; } function getBatchIds$1(featureTableJson, featureTableBinary) { var boxBatchIds; var cylinderBatchIds; var ellipsoidBatchIds; var sphereBatchIds; var i; var numberOfBoxes = defaultValue(featureTableJson.BOXES_LENGTH, 0); var numberOfCylinders = defaultValue(featureTableJson.CYLINDERS_LENGTH, 0); var numberOfEllipsoids = defaultValue(featureTableJson.ELLIPSOIDS_LENGTH, 0); var numberOfSpheres = defaultValue(featureTableJson.SPHERES_LENGTH, 0); if (numberOfBoxes > 0 && defined(featureTableJson.BOX_BATCH_IDS)) { var boxBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.BOX_BATCH_IDS.byteOffset; boxBatchIds = new Uint16Array( featureTableBinary.buffer, boxBatchIdsByteOffset, numberOfBoxes ); } if (numberOfCylinders > 0 && defined(featureTableJson.CYLINDER_BATCH_IDS)) { var cylinderBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.CYLINDER_BATCH_IDS.byteOffset; cylinderBatchIds = new Uint16Array( featureTableBinary.buffer, cylinderBatchIdsByteOffset, numberOfCylinders ); } if (numberOfEllipsoids > 0 && defined(featureTableJson.ELLIPSOID_BATCH_IDS)) { var ellipsoidBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.ELLIPSOID_BATCH_IDS.byteOffset; ellipsoidBatchIds = new Uint16Array( featureTableBinary.buffer, ellipsoidBatchIdsByteOffset, numberOfEllipsoids ); } if (numberOfSpheres > 0 && defined(featureTableJson.SPHERE_BATCH_IDS)) { var sphereBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.SPHERE_BATCH_IDS.byteOffset; sphereBatchIds = new Uint16Array( featureTableBinary.buffer, sphereBatchIdsByteOffset, numberOfSpheres ); } var atLeastOneDefined = defined(boxBatchIds) || defined(cylinderBatchIds) || defined(ellipsoidBatchIds) || defined(sphereBatchIds); var atLeastOneUndefined = (numberOfBoxes > 0 && !defined(boxBatchIds)) || (numberOfCylinders > 0 && !defined(cylinderBatchIds)) || (numberOfEllipsoids > 0 && !defined(ellipsoidBatchIds)) || (numberOfSpheres > 0 && !defined(sphereBatchIds)); if (atLeastOneDefined && atLeastOneUndefined) { throw new RuntimeError( "If one group of batch ids is defined, then all batch ids must be defined." ); } var allUndefinedBatchIds = !defined(boxBatchIds) && !defined(cylinderBatchIds) && !defined(ellipsoidBatchIds) && !defined(sphereBatchIds); if (allUndefinedBatchIds) { var id = 0; if (!defined(boxBatchIds) && numberOfBoxes > 0) { boxBatchIds = new Uint16Array(numberOfBoxes); for (i = 0; i < numberOfBoxes; ++i) { boxBatchIds[i] = id++; } } if (!defined(cylinderBatchIds) && numberOfCylinders > 0) { cylinderBatchIds = new Uint16Array(numberOfCylinders); for (i = 0; i < numberOfCylinders; ++i) { cylinderBatchIds[i] = id++; } } if (!defined(ellipsoidBatchIds) && numberOfEllipsoids > 0) { ellipsoidBatchIds = new Uint16Array(numberOfEllipsoids); for (i = 0; i < numberOfEllipsoids; ++i) { ellipsoidBatchIds[i] = id++; } } if (!defined(sphereBatchIds) && numberOfSpheres > 0) { sphereBatchIds = new Uint16Array(numberOfSpheres); for (i = 0; i < numberOfSpheres; ++i) { sphereBatchIds[i] = id++; } } } return { boxes: boxBatchIds, cylinders: cylinderBatchIds, ellipsoids: ellipsoidBatchIds, spheres: sphereBatchIds, }; } var sizeOfUint32$3 = Uint32Array.BYTES_PER_ELEMENT; function initialize$5(content, arrayBuffer, byteOffset) { byteOffset = defaultValue(byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32$3; // Skip magic number var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Geometry tile version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32$3; var byteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$3; if (byteLength === 0) { content._readyPromise.resolve(content); return; } var featureTableJSONByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$3; if (featureTableJSONByteLength === 0) { throw new RuntimeError( "Feature table must have a byte length greater than zero" ); } var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$3; var batchTableJSONByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$3; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$3; var featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJSONByteLength ); byteOffset += featureTableJSONByteLength; var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; var batchTableJson; var batchTableBinary; if (batchTableJSONByteLength > 0) { // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the // arraybuffer/string compressed in memory and then decompress it when it is first accessed. // // We could also make another request for it, but that would make the property set/get // API async, and would double the number of numbers in some cases. batchTableJson = getJsonFromTypedArray( uint8Array, byteOffset, batchTableJSONByteLength ); byteOffset += batchTableJSONByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array( arrayBuffer, byteOffset, batchTableBinaryByteLength ); // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); } } var numberOfBoxes = defaultValue(featureTableJson.BOXES_LENGTH, 0); var numberOfCylinders = defaultValue(featureTableJson.CYLINDERS_LENGTH, 0); var numberOfEllipsoids = defaultValue(featureTableJson.ELLIPSOIDS_LENGTH, 0); var numberOfSpheres = defaultValue(featureTableJson.SPHERES_LENGTH, 0); var totalPrimitives = numberOfBoxes + numberOfCylinders + numberOfEllipsoids + numberOfSpheres; var batchTable = new Cesium3DTileBatchTable( content, totalPrimitives, batchTableJson, batchTableBinary, createColorChangedCallback$1(content) ); content._batchTable = batchTable; if (totalPrimitives === 0) { return; } var modelMatrix = content.tile.computedTransform; var center; if (defined(featureTableJson.RTC_CENTER)) { center = Cartesian3.unpack(featureTableJson.RTC_CENTER); Matrix4.multiplyByPoint(modelMatrix, center, center); } var batchIds = getBatchIds$1(featureTableJson, featureTableBinary); if ( numberOfBoxes > 0 || numberOfCylinders > 0 || numberOfEllipsoids > 0 || numberOfSpheres > 0 ) { var boxes; var cylinders; var ellipsoids; var spheres; if (numberOfBoxes > 0) { var boxesByteOffset = featureTableBinary.byteOffset + featureTableJson.BOXES.byteOffset; boxes = new Float32Array( featureTableBinary.buffer, boxesByteOffset, Vector3DTileGeometry.packedBoxLength * numberOfBoxes ); } if (numberOfCylinders > 0) { var cylindersByteOffset = featureTableBinary.byteOffset + featureTableJson.CYLINDERS.byteOffset; cylinders = new Float32Array( featureTableBinary.buffer, cylindersByteOffset, Vector3DTileGeometry.packedCylinderLength * numberOfCylinders ); } if (numberOfEllipsoids > 0) { var ellipsoidsByteOffset = featureTableBinary.byteOffset + featureTableJson.ELLIPSOIDS.byteOffset; ellipsoids = new Float32Array( featureTableBinary.buffer, ellipsoidsByteOffset, Vector3DTileGeometry.packedEllipsoidLength * numberOfEllipsoids ); } if (numberOfSpheres > 0) { var spheresByteOffset = featureTableBinary.byteOffset + featureTableJson.SPHERES.byteOffset; spheres = new Float32Array( featureTableBinary.buffer, spheresByteOffset, Vector3DTileGeometry.packedSphereLength * numberOfSpheres ); } content._geometries = new Vector3DTileGeometry({ boxes: boxes, boxBatchIds: batchIds.boxes, cylinders: cylinders, cylinderBatchIds: batchIds.cylinders, ellipsoids: ellipsoids, ellipsoidBatchIds: batchIds.ellipsoids, spheres: spheres, sphereBatchIds: batchIds.spheres, center: center, modelMatrix: modelMatrix, batchTable: batchTable, boundingVolume: content.tile.boundingVolume.boundingVolume, }); } } function createFeatures$3(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && featuresLength > 0) { var features = new Array(featuresLength); if (defined(content._geometries)) { content._geometries.createFeatures(content, features); } content._features = features; } } Geometry3DTileContent.prototype.hasProperty = function (batchId, name) { return this._batchTable.hasProperty(batchId, name); }; Geometry3DTileContent.prototype.getFeature = function (batchId) { //>>includeStart('debug', pragmas.debug); var featuresLength = this.featuresLength; if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + (featuresLength - 1) + ")." ); } //>>includeEnd('debug'); createFeatures$3(this); return this._features[batchId]; }; Geometry3DTileContent.prototype.applyDebugSettings = function (enabled, color) { if (defined(this._geometries)) { this._geometries.applyDebugSettings(enabled, color); } }; Geometry3DTileContent.prototype.applyStyle = function (style) { createFeatures$3(this); if (defined(this._geometries)) { this._geometries.applyStyle(style, this._features); } }; Geometry3DTileContent.prototype.update = function (tileset, frameState) { if (defined(this._geometries)) { this._geometries.classificationType = this._tileset.classificationType; this._geometries.debugWireframe = this._tileset.debugWireframe; this._geometries.update(frameState); } if (defined(this._batchTable) && this._geometries._ready) { this._batchTable.update(tileset, frameState); } if (!defined(this._contentReadyPromise)) { var that = this; this._contentReadyPromise = this._geometries.readyPromise.then(function () { that._readyPromise.resolve(that); }); } }; Geometry3DTileContent.prototype.isDestroyed = function () { return false; }; Geometry3DTileContent.prototype.destroy = function () { this._geometries = this._geometries && this._geometries.destroy(); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; /** * @private */ function ModelInstance(collection, modelMatrix, instanceId) { this.primitive = collection; this._modelMatrix = Matrix4.clone(modelMatrix); this._instanceId = instanceId; } Object.defineProperties(ModelInstance.prototype, { instanceId: { get: function () { return this._instanceId; }, }, model: { get: function () { return this.primitive._model; }, }, modelMatrix: { get: function () { return Matrix4.clone(this._modelMatrix); }, set: function (value) { Matrix4.clone(value, this._modelMatrix); this.primitive.expandBoundingSphere(this._modelMatrix); this.primitive._dirty = true; }, }, }); var LoadState = { NEEDS_LOAD: 0, LOADING: 1, LOADED: 2, FAILED: 3, }; /** * A 3D model instance collection. All instances reference the same underlying model, but have unique * per-instance properties like model matrix, pick id, etc. * * Instances are rendered relative-to-center and for best results instances should be positioned close to one another. * Otherwise there may be precision issues if, for example, instances are placed on opposite sides of the globe. * * @alias ModelInstanceCollection * @constructor * * @param {Object} options Object with the following properties: * @param {Object[]} [options.instances] An array of instances, where each instance contains a modelMatrix and optional batchId when options.batchTable is defined. * @param {Cesium3DTileBatchTable} [options.batchTable] The batch table of the instanced 3D Tile. * @param {Resource|String} [options.url] The url to the .gltf file. * @param {Object} [options.requestType] The request type, used for request prioritization * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer. * @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to. * @param {Boolean} [options.dynamic=false] Hint if instance model matrices will be updated frequently. * @param {Boolean} [options.show=true] Determines if the collection will be shown. * @param {Boolean} [options.allowPicking=true] When true, each instance is pickable with {@link Scene#pick}. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the collection casts or receives shadows from light sources. * @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales the diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox. * @param {Cartesian3} [options.lightColor] The light color when shading models. When undefined the scene's light color is used instead. * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. * @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the glTF material's doubleSided property; when false, back face culling is disabled. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for the collection. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the instances in wireframe. * * @exception {DeveloperError} Must specify either or , but not both. * @exception {DeveloperError} Shader program cannot be optimized for instancing. Parameters cannot have any of the following semantics: MODEL, MODELINVERSE, MODELVIEWINVERSE, MODELVIEWPROJECTIONINVERSE, MODELINVERSETRANSPOSE. * * @private */ function ModelInstanceCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if (!defined(options.gltf) && !defined(options.url)) { throw new DeveloperError("Either options.gltf or options.url is required."); } if (defined(options.gltf) && defined(options.url)) { throw new DeveloperError( "Cannot pass in both options.gltf and options.url." ); } //>>includeEnd('debug'); this.show = defaultValue(options.show, true); this._instancingSupported = false; this._dynamic = defaultValue(options.dynamic, false); this._allowPicking = defaultValue(options.allowPicking, true); this._ready = false; this._readyPromise = when.defer(); this._state = LoadState.NEEDS_LOAD; this._dirty = false; // Undocumented options this._cull = defaultValue(options.cull, true); this._opaquePass = defaultValue(options.opaquePass, Pass$1.OPAQUE); this._instances = createInstances(this, options.instances); // When the model instance collection is backed by an i3dm tile, // use its batch table resources to modify the shaders, attributes, and uniform maps. this._batchTable = options.batchTable; this._model = undefined; this._vertexBufferTypedArray = undefined; // Hold onto the vertex buffer contents when dynamic is true this._vertexBuffer = undefined; this._batchIdBuffer = undefined; this._instancedUniformsByProgram = undefined; this._drawCommands = []; this._modelCommands = undefined; this._renderStates = undefined; this._disableCullingRenderStates = undefined; this._boundingSphere = createBoundingSphere(this); this._center = Cartesian3.clone(this._boundingSphere.center); this._rtcTransform = new Matrix4(); this._rtcModelView = new Matrix4(); // Holds onto uniform this._mode = undefined; this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY); this._modelMatrix = Matrix4.clone(this.modelMatrix); // Passed on to Model this._url = Resource.createIfNeeded(options.url); this._requestType = options.requestType; this._gltf = options.gltf; this._basePath = Resource.createIfNeeded(options.basePath); this._asynchronous = options.asynchronous; this._incrementallyLoadTextures = options.incrementallyLoadTextures; this._upAxis = options.upAxis; // Undocumented option this._forwardAxis = options.forwardAxis; // Undocumented option this.shadows = defaultValue(options.shadows, ShadowMode$1.ENABLED); this._shadows = this.shadows; this._pickIdLoaded = options.pickIdLoaded; this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this._debugShowBoundingVolume = false; this.debugWireframe = defaultValue(options.debugWireframe, false); this._debugWireframe = false; this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0); Cartesian2.clone( options.imageBasedLightingFactor, this._imageBasedLightingFactor ); this.lightColor = options.lightColor; this.luminanceAtZenith = options.luminanceAtZenith; this.sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients; this.specularEnvironmentMaps = options.specularEnvironmentMaps; this.backFaceCulling = defaultValue(options.backFaceCulling, true); this._backFaceCulling = this.backFaceCulling; } Object.defineProperties(ModelInstanceCollection.prototype, { allowPicking: { get: function () { return this._allowPicking; }, }, length: { get: function () { return this._instances.length; }, }, activeAnimations: { get: function () { return this._model.activeAnimations; }, }, ready: { get: function () { return this._ready; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, imageBasedLightingFactor: { get: function () { return this._imageBasedLightingFactor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("imageBasedLightingFactor", value); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.x", value.x, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.x", value.x, 1.0 ); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.y", value.y, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.y", value.y, 1.0 ); //>>includeEnd('debug'); Cartesian2.clone(value, this._imageBasedLightingFactor); }, }, }); function createInstances(collection, instancesOptions) { instancesOptions = defaultValue(instancesOptions, []); var length = instancesOptions.length; var instances = new Array(length); for (var i = 0; i < length; ++i) { var instanceOptions = instancesOptions[i]; var modelMatrix = instanceOptions.modelMatrix; var instanceId = defaultValue(instanceOptions.batchId, i); instances[i] = new ModelInstance(collection, modelMatrix, instanceId); } return instances; } function createBoundingSphere(collection) { var instancesLength = collection.length; var points = new Array(instancesLength); for (var i = 0; i < instancesLength; ++i) { points[i] = Matrix4.getTranslation( collection._instances[i]._modelMatrix, new Cartesian3() ); } return BoundingSphere.fromPoints(points); } var scratchCartesian$7 = new Cartesian3(); var scratchMatrix$3 = new Matrix4(); ModelInstanceCollection.prototype.expandBoundingSphere = function ( instanceModelMatrix ) { var translation = Matrix4.getTranslation( instanceModelMatrix, scratchCartesian$7 ); BoundingSphere.expand( this._boundingSphere, translation, this._boundingSphere ); }; function getCheckUniformSemanticFunction( modelSemantics, supportedSemantics, programId, uniformMap ) { return function (uniform, uniformName) { var semantic = uniform.semantic; if (defined(semantic) && modelSemantics.indexOf(semantic) > -1) { if (supportedSemantics.indexOf(semantic) > -1) { uniformMap[uniformName] = semantic; } else { throw new RuntimeError( "Shader program cannot be optimized for instancing. " + 'Uniform "' + uniformName + '" in program "' + programId + '" uses unsupported semantic "' + semantic + '"' ); } } }; } function getInstancedUniforms(collection, programId) { if (defined(collection._instancedUniformsByProgram)) { return collection._instancedUniformsByProgram[programId]; } var instancedUniformsByProgram = {}; collection._instancedUniformsByProgram = instancedUniformsByProgram; // When using CESIUM_RTC_MODELVIEW the CESIUM_RTC center is ignored. Instances are always rendered relative-to-center. var modelSemantics = [ "MODEL", "MODELVIEW", "CESIUM_RTC_MODELVIEW", "MODELVIEWPROJECTION", "MODELINVERSE", "MODELVIEWINVERSE", "MODELVIEWPROJECTIONINVERSE", "MODELINVERSETRANSPOSE", "MODELVIEWINVERSETRANSPOSE", ]; var supportedSemantics = [ "MODELVIEW", "CESIUM_RTC_MODELVIEW", "MODELVIEWPROJECTION", "MODELVIEWINVERSETRANSPOSE", ]; var techniques = collection._model._sourceTechniques; for (var techniqueId in techniques) { if (techniques.hasOwnProperty(techniqueId)) { var technique = techniques[techniqueId]; var program = technique.program; // Different techniques may share the same program, skip if already processed. // This assumes techniques that share a program do not declare different semantics for the same uniforms. if (!defined(instancedUniformsByProgram[program])) { var uniformMap = {}; instancedUniformsByProgram[program] = uniformMap; ForEach.techniqueUniform( technique, getCheckUniformSemanticFunction( modelSemantics, supportedSemantics, programId, uniformMap ) ); } } } return instancedUniformsByProgram[programId]; } function getVertexShaderCallback(collection) { return function (vs, programId) { var instancedUniforms = getInstancedUniforms(collection, programId); var usesBatchTable = defined(collection._batchTable); var renamedSource = ShaderSource.replaceMain(vs, "czm_instancing_main"); var globalVarsHeader = ""; var globalVarsMain = ""; for (var uniform in instancedUniforms) { if (instancedUniforms.hasOwnProperty(uniform)) { var semantic = instancedUniforms[uniform]; var varName; if (semantic === "MODELVIEW" || semantic === "CESIUM_RTC_MODELVIEW") { varName = "czm_instanced_modelView"; } else if (semantic === "MODELVIEWPROJECTION") { varName = "czm_instanced_modelViewProjection"; globalVarsHeader += "mat4 czm_instanced_modelViewProjection;\n"; globalVarsMain += "czm_instanced_modelViewProjection = czm_projection * czm_instanced_modelView;\n"; } else if (semantic === "MODELVIEWINVERSETRANSPOSE") { varName = "czm_instanced_modelViewInverseTranspose"; globalVarsHeader += "mat3 czm_instanced_modelViewInverseTranspose;\n"; globalVarsMain += "czm_instanced_modelViewInverseTranspose = mat3(czm_instanced_modelView);\n"; } // Remove the uniform declaration var regex = new RegExp("uniform.*" + uniform + ".*"); renamedSource = renamedSource.replace(regex, ""); // Replace all occurrences of the uniform with the global variable regex = new RegExp(uniform + "\\b", "g"); renamedSource = renamedSource.replace(regex, varName); } } // czm_instanced_model is the model matrix of the instance relative to center // czm_instanced_modifiedModelView is the transform from the center to view // czm_instanced_nodeTransform is the local offset of the node within the model var uniforms = "uniform mat4 czm_instanced_modifiedModelView;\n" + "uniform mat4 czm_instanced_nodeTransform;\n"; var batchIdAttribute; var pickAttribute; var pickVarying; if (usesBatchTable) { batchIdAttribute = "attribute float a_batchId;\n"; pickAttribute = ""; pickVarying = ""; } else { batchIdAttribute = ""; pickAttribute = "attribute vec4 pickColor;\n" + "varying vec4 v_pickColor;\n"; pickVarying = " v_pickColor = pickColor;\n"; } var instancedSource = uniforms + globalVarsHeader + "mat4 czm_instanced_modelView;\n" + "attribute vec4 czm_modelMatrixRow0;\n" + "attribute vec4 czm_modelMatrixRow1;\n" + "attribute vec4 czm_modelMatrixRow2;\n" + batchIdAttribute + pickAttribute + renamedSource + "void main()\n" + "{\n" + " mat4 czm_instanced_model = mat4(czm_modelMatrixRow0.x, czm_modelMatrixRow1.x, czm_modelMatrixRow2.x, 0.0, czm_modelMatrixRow0.y, czm_modelMatrixRow1.y, czm_modelMatrixRow2.y, 0.0, czm_modelMatrixRow0.z, czm_modelMatrixRow1.z, czm_modelMatrixRow2.z, 0.0, czm_modelMatrixRow0.w, czm_modelMatrixRow1.w, czm_modelMatrixRow2.w, 1.0);\n" + " czm_instanced_modelView = czm_instanced_modifiedModelView * czm_instanced_model * czm_instanced_nodeTransform;\n" + globalVarsMain + " czm_instancing_main();\n" + pickVarying + "}\n"; if (usesBatchTable) { var gltf = collection._model.gltf; var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform( gltf, programId ); instancedSource = collection._batchTable.getVertexShaderCallback( true, "a_batchId", diffuseAttributeOrUniformName )(instancedSource); } return instancedSource; }; } function getFragmentShaderCallback(collection) { return function (fs, programId) { var batchTable = collection._batchTable; if (defined(batchTable)) { var gltf = collection._model.gltf; var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform( gltf, programId ); fs = batchTable.getFragmentShaderCallback( true, diffuseAttributeOrUniformName )(fs); } else { fs = "varying vec4 v_pickColor;\n" + fs; } return fs; }; } function createModifiedModelView(collection, context) { return function () { return Matrix4.multiply( context.uniformState.view, collection._rtcTransform, collection._rtcModelView ); }; } function createNodeTransformFunction(node) { return function () { return node.computedMatrix; }; } function getUniformMapCallback(collection, context) { return function (uniformMap, programId, node) { uniformMap = clone$1(uniformMap); uniformMap.czm_instanced_modifiedModelView = createModifiedModelView( collection, context ); uniformMap.czm_instanced_nodeTransform = createNodeTransformFunction(node); // Remove instanced uniforms from the uniform map var instancedUniforms = getInstancedUniforms(collection, programId); for (var uniform in instancedUniforms) { if (instancedUniforms.hasOwnProperty(uniform)) { delete uniformMap[uniform]; } } if (defined(collection._batchTable)) { uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap); } return uniformMap; }; } function getVertexShaderNonInstancedCallback(collection) { return function (vs, programId) { if (defined(collection._batchTable)) { var gltf = collection._model.gltf; var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform( gltf, programId ); vs = collection._batchTable.getVertexShaderCallback( true, "a_batchId", diffuseAttributeOrUniformName )(vs); // Treat a_batchId as a uniform rather than a vertex attribute vs = "uniform float a_batchId\n;" + vs; } return vs; }; } function getFragmentShaderNonInstancedCallback(collection) { return function (fs, programId) { var batchTable = collection._batchTable; if (defined(batchTable)) { var gltf = collection._model.gltf; var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform( gltf, programId ); fs = batchTable.getFragmentShaderCallback( true, diffuseAttributeOrUniformName )(fs); } else { fs = "uniform vec4 czm_pickColor;\n" + fs; } return fs; }; } function getUniformMapNonInstancedCallback(collection) { return function (uniformMap) { if (defined(collection._batchTable)) { uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap); } return uniformMap; }; } function getVertexBufferTypedArray(collection) { var instances = collection._instances; var instancesLength = collection.length; var collectionCenter = collection._center; var vertexSizeInFloats = 12; var bufferData = collection._vertexBufferTypedArray; if (!defined(bufferData)) { bufferData = new Float32Array(instancesLength * vertexSizeInFloats); } if (collection._dynamic) { // Hold onto the buffer data so we don't have to allocate new memory every frame. collection._vertexBufferTypedArray = bufferData; } for (var i = 0; i < instancesLength; ++i) { var modelMatrix = instances[i]._modelMatrix; // Instance matrix is relative to center var instanceMatrix = Matrix4.clone(modelMatrix, scratchMatrix$3); instanceMatrix[12] -= collectionCenter.x; instanceMatrix[13] -= collectionCenter.y; instanceMatrix[14] -= collectionCenter.z; var offset = i * vertexSizeInFloats; // First three rows of the model matrix bufferData[offset + 0] = instanceMatrix[0]; bufferData[offset + 1] = instanceMatrix[4]; bufferData[offset + 2] = instanceMatrix[8]; bufferData[offset + 3] = instanceMatrix[12]; bufferData[offset + 4] = instanceMatrix[1]; bufferData[offset + 5] = instanceMatrix[5]; bufferData[offset + 6] = instanceMatrix[9]; bufferData[offset + 7] = instanceMatrix[13]; bufferData[offset + 8] = instanceMatrix[2]; bufferData[offset + 9] = instanceMatrix[6]; bufferData[offset + 10] = instanceMatrix[10]; bufferData[offset + 11] = instanceMatrix[14]; } return bufferData; } function createVertexBuffer(collection, context) { var i; var instances = collection._instances; var instancesLength = collection.length; var dynamic = collection._dynamic; var usesBatchTable = defined(collection._batchTable); if (usesBatchTable) { var batchIdBufferData = new Uint16Array(instancesLength); for (i = 0; i < instancesLength; ++i) { batchIdBufferData[i] = instances[i]._instanceId; } collection._batchIdBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: batchIdBufferData, usage: BufferUsage$1.STATIC_DRAW, }); } if (!usesBatchTable) { var pickIdBuffer = new Uint8Array(instancesLength * 4); for (i = 0; i < instancesLength; ++i) { var pickId = collection._pickIds[i]; var pickColor = pickId.color; var offset = i * 4; pickIdBuffer[offset] = Color.floatToByte(pickColor.red); pickIdBuffer[offset + 1] = Color.floatToByte(pickColor.green); pickIdBuffer[offset + 2] = Color.floatToByte(pickColor.blue); pickIdBuffer[offset + 3] = Color.floatToByte(pickColor.alpha); } collection._pickIdBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: pickIdBuffer, usage: BufferUsage$1.STATIC_DRAW, }); } var vertexBufferTypedArray = getVertexBufferTypedArray(collection); collection._vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: vertexBufferTypedArray, usage: dynamic ? BufferUsage$1.STREAM_DRAW : BufferUsage$1.STATIC_DRAW, }); } function updateVertexBuffer(collection) { var vertexBufferTypedArray = getVertexBufferTypedArray(collection); collection._vertexBuffer.copyFromArrayView(vertexBufferTypedArray); } function createPickIds(collection, context) { // PERFORMANCE_IDEA: we could skip the pick buffer completely by allocating // a continuous range of pickIds and then converting the base pickId + batchId // to RGBA in the shader. The only consider is precision issues, which might // not be an issue in WebGL 2. var instances = collection._instances; var instancesLength = instances.length; var pickIds = new Array(instancesLength); for (var i = 0; i < instancesLength; ++i) { pickIds[i] = context.createPickId(instances[i]); } return pickIds; } function createModel$1(collection, context) { var instancingSupported = collection._instancingSupported; var usesBatchTable = defined(collection._batchTable); var allowPicking = collection._allowPicking; var modelOptions = { url: collection._url, requestType: collection._requestType, gltf: collection._gltf, basePath: collection._basePath, shadows: collection._shadows, cacheKey: undefined, asynchronous: collection._asynchronous, allowPicking: allowPicking, incrementallyLoadTextures: collection._incrementallyLoadTextures, upAxis: collection._upAxis, forwardAxis: collection._forwardAxis, precreatedAttributes: undefined, vertexShaderLoaded: undefined, fragmentShaderLoaded: undefined, uniformMapLoaded: undefined, pickIdLoaded: collection._pickIdLoaded, ignoreCommands: true, opaquePass: collection._opaquePass, imageBasedLightingFactor: collection.imageBasedLightingFactor, lightColor: collection.lightColor, luminanceAtZenith: collection.luminanceAtZenith, sphericalHarmonicCoefficients: collection.sphericalHarmonicCoefficients, specularEnvironmentMaps: collection.specularEnvironmentMaps, }; if (!usesBatchTable) { collection._pickIds = createPickIds(collection, context); } if (instancingSupported) { createVertexBuffer(collection, context); var vertexSizeInFloats = 12; var componentSizeInBytes = ComponentDatatype$1.getSizeInBytes( ComponentDatatype$1.FLOAT ); var instancedAttributes = { czm_modelMatrixRow0: { index: 0, // updated in Model vertexBuffer: collection._vertexBuffer, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, normalize: false, offsetInBytes: 0, strideInBytes: componentSizeInBytes * vertexSizeInFloats, instanceDivisor: 1, }, czm_modelMatrixRow1: { index: 0, // updated in Model vertexBuffer: collection._vertexBuffer, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, normalize: false, offsetInBytes: componentSizeInBytes * 4, strideInBytes: componentSizeInBytes * vertexSizeInFloats, instanceDivisor: 1, }, czm_modelMatrixRow2: { index: 0, // updated in Model vertexBuffer: collection._vertexBuffer, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, normalize: false, offsetInBytes: componentSizeInBytes * 8, strideInBytes: componentSizeInBytes * vertexSizeInFloats, instanceDivisor: 1, }, }; // When using a batch table, add a batch id attribute if (usesBatchTable) { instancedAttributes.a_batchId = { index: 0, // updated in Model vertexBuffer: collection._batchIdBuffer, componentsPerAttribute: 1, componentDatatype: ComponentDatatype$1.UNSIGNED_SHORT, normalize: false, offsetInBytes: 0, strideInBytes: 0, instanceDivisor: 1, }; } if (!usesBatchTable) { instancedAttributes.pickColor = { index: 0, // updated in Model vertexBuffer: collection._pickIdBuffer, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, normalize: true, offsetInBytes: 0, strideInBytes: 0, instanceDivisor: 1, }; } modelOptions.precreatedAttributes = instancedAttributes; modelOptions.vertexShaderLoaded = getVertexShaderCallback(collection); modelOptions.fragmentShaderLoaded = getFragmentShaderCallback(collection); modelOptions.uniformMapLoaded = getUniformMapCallback(collection, context); if (defined(collection._url)) { modelOptions.cacheKey = collection._url.getUrlComponent() + "#instanced"; } } else { modelOptions.vertexShaderLoaded = getVertexShaderNonInstancedCallback( collection ); modelOptions.fragmentShaderLoaded = getFragmentShaderNonInstancedCallback( collection ); modelOptions.uniformMapLoaded = getUniformMapNonInstancedCallback( collection); } if (defined(collection._url)) { collection._model = Model.fromGltf(modelOptions); } else { collection._model = new Model(modelOptions); } } function updateWireframe(collection, force) { if (collection._debugWireframe !== collection.debugWireframe || force) { collection._debugWireframe = collection.debugWireframe; // This assumes the original primitive was TRIANGLES and that the triangles // are connected for the wireframe to look perfect. var primitiveType = collection.debugWireframe ? PrimitiveType$1.LINES : PrimitiveType$1.TRIANGLES; var commands = collection._drawCommands; var length = commands.length; for (var i = 0; i < length; ++i) { commands[i].primitiveType = primitiveType; } } } function getDisableCullingRenderState(renderState) { var rs = clone$1(renderState, true); rs.cull.enabled = false; return RenderState.fromCache(rs); } function updateBackFaceCulling(collection, force) { if (collection._backFaceCulling !== collection.backFaceCulling || force) { collection._backFaceCulling = collection.backFaceCulling; var commands = collection._drawCommands; var length = commands.length; var i; if (!defined(collection._disableCullingRenderStates)) { collection._disableCullingRenderStates = new Array(length); collection._renderStates = new Array(length); for (i = 0; i < length; ++i) { var renderState = commands[i].renderState; var derivedRenderState = getDisableCullingRenderState(renderState); collection._disableCullingRenderStates[i] = derivedRenderState; collection._renderStates[i] = renderState; } } for (i = 0; i < length; ++i) { commands[i].renderState = collection._backFaceCulling ? collection._renderStates[i] : collection._disableCullingRenderStates[i]; } } } function updateShowBoundingVolume(collection, force) { if ( collection.debugShowBoundingVolume !== collection._debugShowBoundingVolume || force ) { collection._debugShowBoundingVolume = collection.debugShowBoundingVolume; var commands = collection._drawCommands; var length = commands.length; for (var i = 0; i < length; ++i) { commands[i].debugShowBoundingVolume = collection.debugShowBoundingVolume; } } } function createCommands$2(collection, drawCommands) { var commandsLength = drawCommands.length; var instancesLength = collection.length; var boundingSphere = collection._boundingSphere; var cull = collection._cull; for (var i = 0; i < commandsLength; ++i) { var drawCommand = DrawCommand.shallowClone(drawCommands[i]); drawCommand.instanceCount = instancesLength; drawCommand.boundingVolume = boundingSphere; drawCommand.cull = cull; if (defined(collection._batchTable)) { drawCommand.pickId = collection._batchTable.getPickId(); } else { drawCommand.pickId = "v_pickColor"; } collection._drawCommands.push(drawCommand); } } function createBatchIdFunction(batchId) { return function () { return batchId; }; } function createPickColorFunction(color) { return function () { return color; }; } function createCommandsNonInstanced(collection, drawCommands) { // When instancing is disabled, create commands for every instance. var instances = collection._instances; var commandsLength = drawCommands.length; var instancesLength = collection.length; var batchTable = collection._batchTable; var usesBatchTable = defined(batchTable); var cull = collection._cull; for (var i = 0; i < commandsLength; ++i) { for (var j = 0; j < instancesLength; ++j) { var drawCommand = DrawCommand.shallowClone(drawCommands[i]); drawCommand.modelMatrix = new Matrix4(); // Updated in updateCommandsNonInstanced drawCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced drawCommand.cull = cull; drawCommand.uniformMap = clone$1(drawCommand.uniformMap); if (usesBatchTable) { drawCommand.uniformMap.a_batchId = createBatchIdFunction( instances[j]._instanceId ); } else { var pickId = collection._pickIds[j]; drawCommand.uniformMap.czm_pickColor = createPickColorFunction( pickId.color ); } collection._drawCommands.push(drawCommand); } } } function updateCommandsNonInstanced(collection) { var modelCommands = collection._modelCommands; var commandsLength = modelCommands.length; var instancesLength = collection.length; var collectionTransform = collection._rtcTransform; var collectionCenter = collection._center; for (var i = 0; i < commandsLength; ++i) { var modelCommand = modelCommands[i]; for (var j = 0; j < instancesLength; ++j) { var commandIndex = i * instancesLength + j; var drawCommand = collection._drawCommands[commandIndex]; var instanceMatrix = Matrix4.clone( collection._instances[j]._modelMatrix, scratchMatrix$3 ); instanceMatrix[12] -= collectionCenter.x; instanceMatrix[13] -= collectionCenter.y; instanceMatrix[14] -= collectionCenter.z; instanceMatrix = Matrix4.multiply( collectionTransform, instanceMatrix, scratchMatrix$3 ); var nodeMatrix = modelCommand.modelMatrix; var modelMatrix = drawCommand.modelMatrix; Matrix4.multiply(instanceMatrix, nodeMatrix, modelMatrix); var nodeBoundingSphere = modelCommand.boundingVolume; var boundingSphere = drawCommand.boundingVolume; BoundingSphere.transform( nodeBoundingSphere, instanceMatrix, boundingSphere ); } } } function getModelCommands(model) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; var drawCommands = []; for (var i = 0; i < length; ++i) { var nc = nodeCommands[i]; if (nc.show) { drawCommands.push(nc.command); } } return drawCommands; } function commandsDirty(model) { var nodeCommands = model._nodeCommands; var length = nodeCommands.length; var commandsDirty = false; for (var i = 0; i < length; i++) { var nc = nodeCommands[i]; if (nc.command.dirty) { nc.command.dirty = false; commandsDirty = true; } } return commandsDirty; } function generateModelCommands(modelInstanceCollection, instancingSupported) { modelInstanceCollection._drawCommands = []; var modelCommands = getModelCommands(modelInstanceCollection._model); if (instancingSupported) { createCommands$2(modelInstanceCollection, modelCommands); } else { createCommandsNonInstanced(modelInstanceCollection, modelCommands); updateCommandsNonInstanced(modelInstanceCollection); } } function updateShadows(collection, force) { if (collection.shadows !== collection._shadows || force) { collection._shadows = collection.shadows; var castShadows = ShadowMode$1.castShadows(collection.shadows); var receiveShadows = ShadowMode$1.receiveShadows(collection.shadows); var drawCommands = collection._drawCommands; var length = drawCommands.length; for (var i = 0; i < length; ++i) { var drawCommand = drawCommands[i]; drawCommand.castShadows = castShadows; drawCommand.receiveShadows = receiveShadows; } } } ModelInstanceCollection.prototype.update = function (frameState) { if (frameState.mode === SceneMode$1.MORPHING) { return; } if (!this.show) { return; } if (this.length === 0) { return; } var context = frameState.context; if (this._state === LoadState.NEEDS_LOAD) { this._state = LoadState.LOADING; this._instancingSupported = context.instancedArrays; createModel$1(this, context); var that = this; this._model.readyPromise.otherwise(function (error) { that._state = LoadState.FAILED; that._readyPromise.reject(error); }); } var instancingSupported = this._instancingSupported; var model = this._model; model.imageBasedLightingFactor = this.imageBasedLightingFactor; model.lightColor = this.lightColor; model.luminanceAtZenith = this.luminanceAtZenith; model.sphericalHarmonicCoefficients = this.sphericalHarmonicCoefficients; model.specularEnvironmentMaps = this.specularEnvironmentMaps; model.update(frameState); if (model.ready && this._state === LoadState.LOADING) { this._state = LoadState.LOADED; this._ready = true; // Expand bounding volume to fit the radius of the loaded model including the model's offset from the center var modelRadius = model.boundingSphere.radius + Cartesian3.magnitude(model.boundingSphere.center); this._boundingSphere.radius += modelRadius; this._modelCommands = getModelCommands(model); generateModelCommands(this, instancingSupported); this._readyPromise.resolve(this); return; } if (this._state !== LoadState.LOADED) { return; } var modeChanged = frameState.mode !== this._mode; var modelMatrix = this.modelMatrix; var modelMatrixChanged = !Matrix4.equals(this._modelMatrix, modelMatrix); if (modeChanged || modelMatrixChanged) { this._mode = frameState.mode; Matrix4.clone(modelMatrix, this._modelMatrix); var rtcTransform = Matrix4.multiplyByTranslation( this._modelMatrix, this._center, this._rtcTransform ); if (this._mode !== SceneMode$1.SCENE3D) { rtcTransform = Transforms.basisTo2D( frameState.mapProjection, rtcTransform, rtcTransform ); } Matrix4.getTranslation(rtcTransform, this._boundingSphere.center); } if (instancingSupported && this._dirty) { // If at least one instance has moved assume the collection is now dynamic this._dynamic = true; this._dirty = false; // PERFORMANCE_IDEA: only update dirty sub-sections instead of the whole collection updateVertexBuffer(this); } // If the model was set to rebuild shaders during update, rebuild instanced commands. var modelCommandsDirty = commandsDirty(model); if (modelCommandsDirty) { generateModelCommands(this, instancingSupported); } // If any node changes due to an animation, update the commands. This could be inefficient if the model is // composed of many nodes and only one changes, however it is probably fine in the general use case. // Only applies when instancing is disabled. The instanced shader automatically handles node transformations. if ( !instancingSupported && (model.dirty || this._dirty || modeChanged || modelMatrixChanged) ) { updateCommandsNonInstanced(this); } updateShadows(this, modelCommandsDirty); updateWireframe(this, modelCommandsDirty); updateBackFaceCulling(this, modelCommandsDirty); updateShowBoundingVolume(this, modelCommandsDirty); var passes = frameState.passes; if (!passes.render && !passes.pick) { return; } var commandList = frameState.commandList; var commands = this._drawCommands; var commandsLength = commands.length; for (var i = 0; i < commandsLength; ++i) { commandList.push(commands[i]); } }; ModelInstanceCollection.prototype.isDestroyed = function () { return false; }; ModelInstanceCollection.prototype.destroy = function () { this._model = this._model && this._model.destroy(); var pickIds = this._pickIds; if (defined(pickIds)) { var length = pickIds.length; for (var i = 0; i < length; ++i) { pickIds[i].destroy(); } } return destroyObject(this); }; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel|Instanced 3D Model} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Instanced3DModel3DTileContent * @constructor * * @private */ function Instanced3DModel3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._modelInstanceCollection = undefined; this._batchTable = undefined; this._features = undefined; this.featurePropertiesDirty = false; initialize$4(this, arrayBuffer, byteOffset); } // This can be overridden for testing purposes Instanced3DModel3DTileContent._deprecationWarning = deprecationWarning; Object.defineProperties(Instanced3DModel3DTileContent.prototype, { featuresLength: { get: function () { return this._batchTable.featuresLength; }, }, pointsLength: { get: function () { return 0; }, }, trianglesLength: { get: function () { var model = this._modelInstanceCollection._model; if (defined(model)) { return model.trianglesLength; } return 0; }, }, geometryByteLength: { get: function () { var model = this._modelInstanceCollection._model; if (defined(model)) { return model.geometryByteLength; } return 0; }, }, texturesByteLength: { get: function () { var model = this._modelInstanceCollection._model; if (defined(model)) { return model.texturesByteLength; } return 0; }, }, batchTableByteLength: { get: function () { return this._batchTable.memorySizeInBytes; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._modelInstanceCollection.readyPromise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return this._batchTable; }, }, }); function getPickIdCallback(content) { return function () { return content._batchTable.getPickId(); }; } var sizeOfUint32$2 = Uint32Array.BYTES_PER_ELEMENT; var propertyScratch1 = new Array(4); var propertyScratch2 = new Array(4); function initialize$4(content, arrayBuffer, byteOffset) { var byteStart = defaultValue(byteOffset, 0); byteOffset = byteStart; var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32$2; // Skip magic var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Instanced 3D Model version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32$2; var byteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$2; var featureTableJsonByteLength = view.getUint32(byteOffset, true); if (featureTableJsonByteLength === 0) { throw new RuntimeError( "featureTableJsonByteLength is zero, the feature table must be defined." ); } byteOffset += sizeOfUint32$2; var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$2; var batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$2; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$2; var gltfFormat = view.getUint32(byteOffset, true); if (gltfFormat !== 1 && gltfFormat !== 0) { throw new RuntimeError( "Only glTF format 0 (uri) or 1 (embedded) are supported. Format " + gltfFormat + " is not." ); } byteOffset += sizeOfUint32$2; var featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJsonByteLength ); byteOffset += featureTableJsonByteLength; var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; var featureTable = new Cesium3DTileFeatureTable( featureTableJson, featureTableBinary ); var instancesLength = featureTable.getGlobalProperty("INSTANCES_LENGTH"); featureTable.featuresLength = instancesLength; if (!defined(instancesLength)) { throw new RuntimeError( "Feature table global property: INSTANCES_LENGTH must be defined" ); } var batchTableJson; var batchTableBinary; if (batchTableJsonByteLength > 0) { batchTableJson = getJsonFromTypedArray( uint8Array, byteOffset, batchTableJsonByteLength ); byteOffset += batchTableJsonByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array( arrayBuffer, byteOffset, batchTableBinaryByteLength ); // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); byteOffset += batchTableBinaryByteLength; } } content._batchTable = new Cesium3DTileBatchTable( content, instancesLength, batchTableJson, batchTableBinary ); var gltfByteLength = byteStart + byteLength - byteOffset; if (gltfByteLength === 0) { throw new RuntimeError( "glTF byte length is zero, i3dm must have a glTF to instance." ); } var gltfView; if (byteOffset % 4 === 0) { gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); } else { // Create a copy of the glb so that it is 4-byte aligned Instanced3DModel3DTileContent._deprecationWarning( "i3dm-glb-unaligned", "The embedded glb is not aligned to a 4-byte boundary." ); gltfView = new Uint8Array( uint8Array.subarray(byteOffset, byteOffset + gltfByteLength) ); } var tileset = content._tileset; // Create model instance collection var collectionOptions = { instances: new Array(instancesLength), batchTable: content._batchTable, cull: false, // Already culled by 3D Tiles url: undefined, requestType: RequestType$1.TILES3D, gltf: undefined, basePath: undefined, incrementallyLoadTextures: false, upAxis: tileset._gltfUpAxis, forwardAxis: Axis$1.X, opaquePass: Pass$1.CESIUM_3D_TILE, // Draw opaque portions during the 3D Tiles pass pickIdLoaded: getPickIdCallback(content), imageBasedLightingFactor: tileset.imageBasedLightingFactor, lightColor: tileset.lightColor, luminanceAtZenith: tileset.luminanceAtZenith, sphericalHarmonicCoefficients: tileset.sphericalHarmonicCoefficients, specularEnvironmentMaps: tileset.specularEnvironmentMaps, backFaceCulling: tileset.backFaceCulling, }; if (gltfFormat === 0) { var gltfUrl = getStringFromTypedArray(gltfView); // We need to remove padding from the end of the model URL in case this tile was part of a composite tile. // This removes all white space and null characters from the end of the string. gltfUrl = gltfUrl.replace(/[\s\0]+$/, ""); collectionOptions.url = content._resource.getDerivedResource({ url: gltfUrl, }); } else { collectionOptions.gltf = gltfView; collectionOptions.basePath = content._resource.clone(); } var eastNorthUp = featureTable.getGlobalProperty("EAST_NORTH_UP"); var rtcCenter; var rtcCenterArray = featureTable.getGlobalProperty( "RTC_CENTER", ComponentDatatype$1.FLOAT, 3 ); if (defined(rtcCenterArray)) { rtcCenter = Cartesian3.unpack(rtcCenterArray); } var instances = collectionOptions.instances; var instancePosition = new Cartesian3(); var instancePositionArray = new Array(3); var instanceNormalRight = new Cartesian3(); var instanceNormalUp = new Cartesian3(); var instanceNormalForward = new Cartesian3(); var instanceRotation = new Matrix3(); var instanceQuaternion = new Quaternion(); var instanceScale = new Cartesian3(); var instanceTranslationRotationScale = new TranslationRotationScale(); var instanceTransform = new Matrix4(); for (var i = 0; i < instancesLength; i++) { // Get the instance position var position = featureTable.getProperty( "POSITION", ComponentDatatype$1.FLOAT, 3, i, propertyScratch1 ); if (!defined(position)) { position = instancePositionArray; var positionQuantized = featureTable.getProperty( "POSITION_QUANTIZED", ComponentDatatype$1.UNSIGNED_SHORT, 3, i, propertyScratch1 ); if (!defined(positionQuantized)) { throw new RuntimeError( "Either POSITION or POSITION_QUANTIZED must be defined for each instance." ); } var quantizedVolumeOffset = featureTable.getGlobalProperty( "QUANTIZED_VOLUME_OFFSET", ComponentDatatype$1.FLOAT, 3 ); if (!defined(quantizedVolumeOffset)) { throw new RuntimeError( "Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions." ); } var quantizedVolumeScale = featureTable.getGlobalProperty( "QUANTIZED_VOLUME_SCALE", ComponentDatatype$1.FLOAT, 3 ); if (!defined(quantizedVolumeScale)) { throw new RuntimeError( "Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions." ); } for (var j = 0; j < 3; j++) { position[j] = (positionQuantized[j] / 65535.0) * quantizedVolumeScale[j] + quantizedVolumeOffset[j]; } } Cartesian3.unpack(position, 0, instancePosition); if (defined(rtcCenter)) { Cartesian3.add(instancePosition, rtcCenter, instancePosition); } instanceTranslationRotationScale.translation = instancePosition; // Get the instance rotation var normalUp = featureTable.getProperty( "NORMAL_UP", ComponentDatatype$1.FLOAT, 3, i, propertyScratch1 ); var normalRight = featureTable.getProperty( "NORMAL_RIGHT", ComponentDatatype$1.FLOAT, 3, i, propertyScratch2 ); var hasCustomOrientation = false; if (defined(normalUp)) { if (!defined(normalRight)) { throw new RuntimeError( "To define a custom orientation, both NORMAL_UP and NORMAL_RIGHT must be defined." ); } Cartesian3.unpack(normalUp, 0, instanceNormalUp); Cartesian3.unpack(normalRight, 0, instanceNormalRight); hasCustomOrientation = true; } else { var octNormalUp = featureTable.getProperty( "NORMAL_UP_OCT32P", ComponentDatatype$1.UNSIGNED_SHORT, 2, i, propertyScratch1 ); var octNormalRight = featureTable.getProperty( "NORMAL_RIGHT_OCT32P", ComponentDatatype$1.UNSIGNED_SHORT, 2, i, propertyScratch2 ); if (defined(octNormalUp)) { if (!defined(octNormalRight)) { throw new RuntimeError( "To define a custom orientation with oct-encoded vectors, both NORMAL_UP_OCT32P and NORMAL_RIGHT_OCT32P must be defined." ); } AttributeCompression.octDecodeInRange( octNormalUp[0], octNormalUp[1], 65535, instanceNormalUp ); AttributeCompression.octDecodeInRange( octNormalRight[0], octNormalRight[1], 65535, instanceNormalRight ); hasCustomOrientation = true; } else if (eastNorthUp) { Transforms.eastNorthUpToFixedFrame( instancePosition, Ellipsoid.WGS84, instanceTransform ); Matrix4.getMatrix3(instanceTransform, instanceRotation); } else { Matrix3.clone(Matrix3.IDENTITY, instanceRotation); } } if (hasCustomOrientation) { Cartesian3.cross( instanceNormalRight, instanceNormalUp, instanceNormalForward ); Cartesian3.normalize(instanceNormalForward, instanceNormalForward); Matrix3.setColumn( instanceRotation, 0, instanceNormalRight, instanceRotation ); Matrix3.setColumn( instanceRotation, 1, instanceNormalUp, instanceRotation ); Matrix3.setColumn( instanceRotation, 2, instanceNormalForward, instanceRotation ); } Quaternion.fromRotationMatrix(instanceRotation, instanceQuaternion); instanceTranslationRotationScale.rotation = instanceQuaternion; // Get the instance scale instanceScale = Cartesian3.fromElements(1.0, 1.0, 1.0, instanceScale); var scale = featureTable.getProperty( "SCALE", ComponentDatatype$1.FLOAT, 1, i ); if (defined(scale)) { Cartesian3.multiplyByScalar(instanceScale, scale, instanceScale); } var nonUniformScale = featureTable.getProperty( "SCALE_NON_UNIFORM", ComponentDatatype$1.FLOAT, 3, i, propertyScratch1 ); if (defined(nonUniformScale)) { instanceScale.x *= nonUniformScale[0]; instanceScale.y *= nonUniformScale[1]; instanceScale.z *= nonUniformScale[2]; } instanceTranslationRotationScale.scale = instanceScale; // Get the batchId var batchId = featureTable.getProperty( "BATCH_ID", ComponentDatatype$1.UNSIGNED_SHORT, 1, i ); if (!defined(batchId)) { // If BATCH_ID semantic is undefined, batchId is just the instance number batchId = i; } // Create the model matrix and the instance Matrix4.fromTranslationRotationScale( instanceTranslationRotationScale, instanceTransform ); var modelMatrix = instanceTransform.clone(); instances[i] = { modelMatrix: modelMatrix, batchId: batchId, }; } content._modelInstanceCollection = new ModelInstanceCollection( collectionOptions ); content._modelInstanceCollection.readyPromise.then(function (collection) { collection.activeAnimations.addAll({ loop: ModelAnimationLoop$1.REPEAT, }); }); } function createFeatures$2(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && featuresLength > 0) { var features = new Array(featuresLength); for (var i = 0; i < featuresLength; ++i) { features[i] = new Cesium3DTileFeature(content, i); } content._features = features; } } Instanced3DModel3DTileContent.prototype.hasProperty = function (batchId, name) { return this._batchTable.hasProperty(batchId, name); }; Instanced3DModel3DTileContent.prototype.getFeature = function (batchId) { var featuresLength = this.featuresLength; //>>includeStart('debug', pragmas.debug); if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + (featuresLength - 1) + ")." ); } //>>includeEnd('debug'); createFeatures$2(this); return this._features[batchId]; }; Instanced3DModel3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) { color = enabled ? color : Color.WHITE; this._batchTable.setAllColor(color); }; Instanced3DModel3DTileContent.prototype.applyStyle = function (style) { this._batchTable.applyStyle(style); }; Instanced3DModel3DTileContent.prototype.update = function ( tileset, frameState ) { var commandStart = frameState.commandList.length; // In the PROCESSING state we may be calling update() to move forward // the content's resource loading. In the READY state, it will // actually generate commands. this._batchTable.update(tileset, frameState); this._modelInstanceCollection.modelMatrix = this._tile.computedTransform; this._modelInstanceCollection.shadows = this._tileset.shadows; this._modelInstanceCollection.lightColor = this._tileset.lightColor; this._modelInstanceCollection.luminanceAtZenith = this._tileset.luminanceAtZenith; this._modelInstanceCollection.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients; this._modelInstanceCollection.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps; this._modelInstanceCollection.backFaceCulling = this._tileset.backFaceCulling; this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe; var model = this._modelInstanceCollection._model; if (defined(model)) { // Update for clipping planes var tilesetClippingPlanes = this._tileset.clippingPlanes; model.referenceMatrix = this._tileset.clippingPlanesOriginMatrix; if (defined(tilesetClippingPlanes) && this._tile.clippingPlanesDirty) { // Dereference the clipping planes from the model if they are irrelevant - saves on shading // Link/Dereference directly to avoid ownership checks. model._clippingPlanes = tilesetClippingPlanes.enabled && this._tile._isClipped ? tilesetClippingPlanes : undefined; } // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection. if ( defined(tilesetClippingPlanes) && defined(model._clippingPlanes) && model._clippingPlanes !== tilesetClippingPlanes ) { model._clippingPlanes = tilesetClippingPlanes; } } this._modelInstanceCollection.update(frameState); // If any commands were pushed, add derived commands var commandEnd = frameState.commandList.length; if ( commandStart < commandEnd && (frameState.passes.render || frameState.passes.pick) ) { this._batchTable.addDerivedCommands(frameState, commandStart, false); } }; Instanced3DModel3DTileContent.prototype.isDestroyed = function () { return false; }; Instanced3DModel3DTileContent.prototype.destroy = function () { this._modelInstanceCollection = this._modelInstanceCollection && this._modelInstanceCollection.destroy(); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; /** * The refinement approach for a tile. *

* See the {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification#refinement|Refinement} * in the 3D Tiles spec. *

* * @enum {Number} * * @private */ var Cesium3DTileRefine = { /** * Render this tile and, if it doesn't meet the screen space error, also refine to its children. * * @type {Number} * @constant */ ADD: 0, /** * Render this tile or, if it doesn't meet the screen space error, refine to its descendants instead. * * @type {Number} * @constant */ REPLACE: 1, }; var Cesium3DTileRefine$1 = Object.freeze(Cesium3DTileRefine); var DecodingState = { NEEDS_DECODE: 0, DECODING: 1, READY: 2, FAILED: 3, }; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/PointCloud|Point Cloud} * tile. Used internally by {@link PointCloud3DTileContent} and {@link TimeDynamicPointCloud}. * * @alias PointCloud * @constructor * * @see PointCloud3DTileContent * @see TimeDynamicPointCloud * * @private */ function PointCloud(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.arrayBuffer", options.arrayBuffer); //>>includeEnd('debug'); // Hold onto the payload until the render resources are created this._parsedContent = undefined; this._drawCommand = undefined; this._isTranslucent = false; this._styleTranslucent = false; this._constantColor = Color.clone(Color.DARKGRAY); this._highlightColor = Color.clone(Color.WHITE); this._pointSize = 1.0; this._rtcCenter = undefined; this._quantizedVolumeScale = undefined; this._quantizedVolumeOffset = undefined; // These values are used to regenerate the shader when the style changes this._styleableShaderAttributes = undefined; this._isQuantized = false; this._isOctEncoded16P = false; this._isRGB565 = false; this._hasColors = false; this._hasNormals = false; this._hasBatchIds = false; // Draco this._decodingState = DecodingState.READY; this._dequantizeInShader = true; this._isQuantizedDraco = false; this._isOctEncodedDraco = false; this._quantizedRange = 0.0; this._octEncodedRange = 0.0; // Use per-point normals to hide back-facing points. this.backFaceCulling = false; this._backFaceCulling = false; // Whether to enable normal shading this.normalShading = true; this._normalShading = true; this._opaqueRenderState = undefined; this._translucentRenderState = undefined; this._mode = undefined; this._ready = false; this._readyPromise = when.defer(); this._pointsLength = 0; this._geometryByteLength = 0; this._vertexShaderLoaded = options.vertexShaderLoaded; this._fragmentShaderLoaded = options.fragmentShaderLoaded; this._uniformMapLoaded = options.uniformMapLoaded; this._batchTableLoaded = options.batchTableLoaded; this._pickIdLoaded = options.pickIdLoaded; this._opaquePass = defaultValue(options.opaquePass, Pass$1.OPAQUE); this._cull = defaultValue(options.cull, true); this.style = undefined; this._style = undefined; this.styleDirty = false; this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY); this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); this.time = 0.0; // For styling this.shadows = ShadowMode$1.ENABLED; this._boundingSphere = undefined; this.clippingPlanes = undefined; this.isClipped = false; this.clippingPlanesDirty = false; // If defined, use this matrix to position the clipping planes instead of the modelMatrix. // This is so that when point clouds are part of a tileset they all get clipped relative // to the root tile. this.clippingPlanesOriginMatrix = undefined; this.attenuation = false; this._attenuation = false; // Options for geometric error based attenuation this.geometricError = 0.0; this.geometricErrorScale = 1.0; this.maximumAttenuation = this._pointSize; initialize$3(this, options); } Object.defineProperties(PointCloud.prototype, { pointsLength: { get: function () { return this._pointsLength; }, }, geometryByteLength: { get: function () { return this._geometryByteLength; }, }, ready: { get: function () { return this._ready; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, color: { get: function () { return Color.clone(this._highlightColor); }, set: function (value) { this._highlightColor = Color.clone(value, this._highlightColor); }, }, boundingSphere: { get: function () { if (defined(this._drawCommand)) { return this._drawCommand.boundingVolume; } return undefined; }, set: function (value) { this._boundingSphere = BoundingSphere.clone(value, this._boundingSphere); }, }, }); var sizeOfUint32$1 = Uint32Array.BYTES_PER_ELEMENT; function initialize$3(pointCloud, options) { var arrayBuffer = options.arrayBuffer; var byteOffset = defaultValue(options.byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32$1; // Skip magic var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Point Cloud tile version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32$1; // Skip byteLength byteOffset += sizeOfUint32$1; var featureTableJsonByteLength = view.getUint32(byteOffset, true); if (featureTableJsonByteLength === 0) { throw new RuntimeError( "Feature table must have a byte length greater than zero" ); } byteOffset += sizeOfUint32$1; var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$1; var batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$1; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32$1; var featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJsonByteLength ); byteOffset += featureTableJsonByteLength; var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; // Get the batch table JSON and binary var batchTableJson; var batchTableBinary; if (batchTableJsonByteLength > 0) { // Has a batch table JSON batchTableJson = getJsonFromTypedArray( uint8Array, byteOffset, batchTableJsonByteLength ); byteOffset += batchTableJsonByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array( arrayBuffer, byteOffset, batchTableBinaryByteLength ); byteOffset += batchTableBinaryByteLength; } } var featureTable = new Cesium3DTileFeatureTable( featureTableJson, featureTableBinary ); var pointsLength = featureTable.getGlobalProperty("POINTS_LENGTH"); featureTable.featuresLength = pointsLength; if (!defined(pointsLength)) { throw new RuntimeError( "Feature table global property: POINTS_LENGTH must be defined" ); } var rtcCenter = featureTable.getGlobalProperty( "RTC_CENTER", ComponentDatatype$1.FLOAT, 3 ); if (defined(rtcCenter)) { pointCloud._rtcCenter = Cartesian3.unpack(rtcCenter); } var positions; var colors; var normals; var batchIds; var hasPositions = false; var hasColors = false; var hasNormals = false; var hasBatchIds = false; var isQuantized = false; var isTranslucent = false; var isRGB565 = false; var isOctEncoded16P = false; var dracoBuffer; var dracoFeatureTableProperties; var dracoBatchTableProperties; var featureTableDraco = defined(featureTableJson.extensions) ? featureTableJson.extensions["3DTILES_draco_point_compression"] : undefined; var batchTableDraco = defined(batchTableJson) && defined(batchTableJson.extensions) ? batchTableJson.extensions["3DTILES_draco_point_compression"] : undefined; if (defined(batchTableDraco)) { dracoBatchTableProperties = batchTableDraco.properties; } if (defined(featureTableDraco)) { dracoFeatureTableProperties = featureTableDraco.properties; var dracoByteOffset = featureTableDraco.byteOffset; var dracoByteLength = featureTableDraco.byteLength; if ( !defined(dracoFeatureTableProperties) || !defined(dracoByteOffset) || !defined(dracoByteLength) ) { throw new RuntimeError( "Draco properties, byteOffset, and byteLength must be defined" ); } dracoBuffer = arraySlice( featureTableBinary, dracoByteOffset, dracoByteOffset + dracoByteLength ); hasPositions = defined(dracoFeatureTableProperties.POSITION); hasColors = defined(dracoFeatureTableProperties.RGB) || defined(dracoFeatureTableProperties.RGBA); hasNormals = defined(dracoFeatureTableProperties.NORMAL); hasBatchIds = defined(dracoFeatureTableProperties.BATCH_ID); isTranslucent = defined(dracoFeatureTableProperties.RGBA); pointCloud._decodingState = DecodingState.NEEDS_DECODE; } var draco; if (defined(dracoBuffer)) { draco = { buffer: dracoBuffer, featureTableProperties: dracoFeatureTableProperties, batchTableProperties: dracoBatchTableProperties, properties: combine$2( dracoFeatureTableProperties, dracoBatchTableProperties ), dequantizeInShader: pointCloud._dequantizeInShader, }; } if (!hasPositions) { if (defined(featureTableJson.POSITION)) { positions = featureTable.getPropertyArray( "POSITION", ComponentDatatype$1.FLOAT, 3 ); hasPositions = true; } else if (defined(featureTableJson.POSITION_QUANTIZED)) { positions = featureTable.getPropertyArray( "POSITION_QUANTIZED", ComponentDatatype$1.UNSIGNED_SHORT, 3 ); isQuantized = true; hasPositions = true; var quantizedVolumeScale = featureTable.getGlobalProperty( "QUANTIZED_VOLUME_SCALE", ComponentDatatype$1.FLOAT, 3 ); if (!defined(quantizedVolumeScale)) { throw new RuntimeError( "Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions." ); } pointCloud._quantizedVolumeScale = Cartesian3.unpack( quantizedVolumeScale ); pointCloud._quantizedRange = (1 << 16) - 1; var quantizedVolumeOffset = featureTable.getGlobalProperty( "QUANTIZED_VOLUME_OFFSET", ComponentDatatype$1.FLOAT, 3 ); if (!defined(quantizedVolumeOffset)) { throw new RuntimeError( "Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions." ); } pointCloud._quantizedVolumeOffset = Cartesian3.unpack( quantizedVolumeOffset ); } } if (!hasColors) { if (defined(featureTableJson.RGBA)) { colors = featureTable.getPropertyArray( "RGBA", ComponentDatatype$1.UNSIGNED_BYTE, 4 ); isTranslucent = true; hasColors = true; } else if (defined(featureTableJson.RGB)) { colors = featureTable.getPropertyArray( "RGB", ComponentDatatype$1.UNSIGNED_BYTE, 3 ); hasColors = true; } else if (defined(featureTableJson.RGB565)) { colors = featureTable.getPropertyArray( "RGB565", ComponentDatatype$1.UNSIGNED_SHORT, 1 ); isRGB565 = true; hasColors = true; } } if (!hasNormals) { if (defined(featureTableJson.NORMAL)) { normals = featureTable.getPropertyArray( "NORMAL", ComponentDatatype$1.FLOAT, 3 ); hasNormals = true; } else if (defined(featureTableJson.NORMAL_OCT16P)) { normals = featureTable.getPropertyArray( "NORMAL_OCT16P", ComponentDatatype$1.UNSIGNED_BYTE, 2 ); isOctEncoded16P = true; hasNormals = true; } } if (!hasBatchIds) { if (defined(featureTableJson.BATCH_ID)) { batchIds = featureTable.getPropertyArray( "BATCH_ID", ComponentDatatype$1.UNSIGNED_SHORT, 1 ); hasBatchIds = true; } } if (!hasPositions) { throw new RuntimeError( "Either POSITION or POSITION_QUANTIZED must be defined." ); } if (defined(featureTableJson.CONSTANT_RGBA)) { var constantRGBA = featureTable.getGlobalProperty( "CONSTANT_RGBA", ComponentDatatype$1.UNSIGNED_BYTE, 4 ); pointCloud._constantColor = Color.fromBytes( constantRGBA[0], constantRGBA[1], constantRGBA[2], constantRGBA[3], pointCloud._constantColor ); } if (hasBatchIds) { var batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); if (!defined(batchLength)) { throw new RuntimeError( "Global property: BATCH_LENGTH must be defined when BATCH_ID is defined." ); } if (defined(batchTableBinary)) { // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); } if (defined(pointCloud._batchTableLoaded)) { pointCloud._batchTableLoaded( batchLength, batchTableJson, batchTableBinary ); } } // If points are not batched and there are per-point properties, use these properties for styling purposes var styleableProperties; if (!hasBatchIds && defined(batchTableBinary)) { styleableProperties = Cesium3DTileBatchTable.getBinaryProperties( pointsLength, batchTableJson, batchTableBinary ); } pointCloud._parsedContent = { positions: positions, colors: colors, normals: normals, batchIds: batchIds, styleableProperties: styleableProperties, draco: draco, }; pointCloud._pointsLength = pointsLength; pointCloud._isQuantized = isQuantized; pointCloud._isOctEncoded16P = isOctEncoded16P; pointCloud._isRGB565 = isRGB565; pointCloud._isTranslucent = isTranslucent; pointCloud._hasColors = hasColors; pointCloud._hasNormals = hasNormals; pointCloud._hasBatchIds = hasBatchIds; } var scratchMin$1 = new Cartesian3(); var scratchMax$1 = new Cartesian3(); var scratchPosition$5 = new Cartesian3(); var randomValues; function getRandomValues(samplesLength) { // Use same random values across all runs if (!defined(randomValues)) { CesiumMath.setRandomNumberSeed(0); randomValues = new Array(samplesLength); for (var i = 0; i < samplesLength; ++i) { randomValues[i] = CesiumMath.nextRandomNumber(); } } return randomValues; } function computeApproximateBoundingSphereFromPositions(positions) { var maximumSamplesLength = 20; var pointsLength = positions.length / 3; var samplesLength = Math.min(pointsLength, maximumSamplesLength); var randomValues = getRandomValues(maximumSamplesLength); var maxValue = Number.MAX_VALUE; var minValue = -Number.MAX_VALUE; var min = Cartesian3.fromElements(maxValue, maxValue, maxValue, scratchMin$1); var max = Cartesian3.fromElements(minValue, minValue, minValue, scratchMax$1); for (var i = 0; i < samplesLength; ++i) { var index = Math.floor(randomValues[i] * pointsLength); var position = Cartesian3.unpack(positions, index * 3, scratchPosition$5); Cartesian3.minimumByComponent(min, position, min); Cartesian3.maximumByComponent(max, position, max); } var boundingSphere = BoundingSphere.fromCornerPoints(min, max); boundingSphere.radius += CesiumMath.EPSILON2; // To avoid radius of zero return boundingSphere; } function prepareVertexAttribute(typedArray, name) { // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT. var componentDatatype = ComponentDatatype$1.fromTypedArray(typedArray); if ( componentDatatype === ComponentDatatype$1.INT || componentDatatype === ComponentDatatype$1.UNSIGNED_INT || componentDatatype === ComponentDatatype$1.DOUBLE ) { oneTimeWarning( "Cast pnts property to floats", 'Point cloud property "' + name + '" will be casted to a float array because INT, UNSIGNED_INT, and DOUBLE are not valid WebGL vertex attribute types. Some precision may be lost.' ); return new Float32Array(typedArray); } return typedArray; } var scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier = new Cartesian4(); var scratchQuantizedVolumeScaleAndOctEncodedRange = new Cartesian4(); var scratchColor$h = new Color(); var positionLocation = 0; var colorLocation = 1; var normalLocation = 2; var batchIdLocation = 3; var numberOfAttributes = 4; var scratchClippingPlanesMatrix$1 = new Matrix4(); var scratchInverseTransposeClippingPlanesMatrix$1 = new Matrix4(); function createResources$4(pointCloud, frameState) { var context = frameState.context; var parsedContent = pointCloud._parsedContent; var pointsLength = pointCloud._pointsLength; var positions = parsedContent.positions; var colors = parsedContent.colors; var normals = parsedContent.normals; var batchIds = parsedContent.batchIds; var styleableProperties = parsedContent.styleableProperties; var hasStyleableProperties = defined(styleableProperties); var isQuantized = pointCloud._isQuantized; var isQuantizedDraco = pointCloud._isQuantizedDraco; var isOctEncoded16P = pointCloud._isOctEncoded16P; var isOctEncodedDraco = pointCloud._isOctEncodedDraco; var quantizedRange = pointCloud._quantizedRange; var octEncodedRange = pointCloud._octEncodedRange; var isRGB565 = pointCloud._isRGB565; var isTranslucent = pointCloud._isTranslucent; var hasColors = pointCloud._hasColors; var hasNormals = pointCloud._hasNormals; var hasBatchIds = pointCloud._hasBatchIds; var componentsPerAttribute; var componentDatatype; var styleableVertexAttributes = []; var styleableShaderAttributes = {}; pointCloud._styleableShaderAttributes = styleableShaderAttributes; if (hasStyleableProperties) { var attributeLocation = numberOfAttributes; for (var name in styleableProperties) { if (styleableProperties.hasOwnProperty(name)) { var property = styleableProperties[name]; var typedArray = prepareVertexAttribute(property.typedArray, name); componentsPerAttribute = property.componentCount; componentDatatype = ComponentDatatype$1.fromTypedArray(typedArray); var vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: typedArray, usage: BufferUsage$1.STATIC_DRAW, }); pointCloud._geometryByteLength += vertexBuffer.sizeInBytes; var vertexAttribute = { index: attributeLocation, vertexBuffer: vertexBuffer, componentsPerAttribute: componentsPerAttribute, componentDatatype: componentDatatype, normalize: false, offsetInBytes: 0, strideInBytes: 0, }; styleableVertexAttributes.push(vertexAttribute); styleableShaderAttributes[name] = { location: attributeLocation, componentCount: componentsPerAttribute, }; ++attributeLocation; } } } var positionsVertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: positions, usage: BufferUsage$1.STATIC_DRAW, }); pointCloud._geometryByteLength += positionsVertexBuffer.sizeInBytes; var colorsVertexBuffer; if (hasColors) { colorsVertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: colors, usage: BufferUsage$1.STATIC_DRAW, }); pointCloud._geometryByteLength += colorsVertexBuffer.sizeInBytes; } var normalsVertexBuffer; if (hasNormals) { normalsVertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: normals, usage: BufferUsage$1.STATIC_DRAW, }); pointCloud._geometryByteLength += normalsVertexBuffer.sizeInBytes; } var batchIdsVertexBuffer; if (hasBatchIds) { batchIds = prepareVertexAttribute(batchIds, "batchIds"); batchIdsVertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: batchIds, usage: BufferUsage$1.STATIC_DRAW, }); pointCloud._geometryByteLength += batchIdsVertexBuffer.sizeInBytes; } var attributes = []; if (isQuantized) { componentDatatype = ComponentDatatype$1.UNSIGNED_SHORT; } else if (isQuantizedDraco) { componentDatatype = quantizedRange <= 255 ? ComponentDatatype$1.UNSIGNED_BYTE : ComponentDatatype$1.UNSIGNED_SHORT; } else { componentDatatype = ComponentDatatype$1.FLOAT; } attributes.push({ index: positionLocation, vertexBuffer: positionsVertexBuffer, componentsPerAttribute: 3, componentDatatype: componentDatatype, normalize: false, offsetInBytes: 0, strideInBytes: 0, }); if (pointCloud._cull) { if (isQuantized || isQuantizedDraco) { pointCloud._boundingSphere = BoundingSphere.fromCornerPoints( Cartesian3.ZERO, pointCloud._quantizedVolumeScale ); } else { pointCloud._boundingSphere = computeApproximateBoundingSphereFromPositions( positions ); } } if (hasColors) { if (isRGB565) { attributes.push({ index: colorLocation, vertexBuffer: colorsVertexBuffer, componentsPerAttribute: 1, componentDatatype: ComponentDatatype$1.UNSIGNED_SHORT, normalize: false, offsetInBytes: 0, strideInBytes: 0, }); } else { var colorComponentsPerAttribute = isTranslucent ? 4 : 3; attributes.push({ index: colorLocation, vertexBuffer: colorsVertexBuffer, componentsPerAttribute: colorComponentsPerAttribute, componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, normalize: true, offsetInBytes: 0, strideInBytes: 0, }); } } if (hasNormals) { if (isOctEncoded16P) { componentsPerAttribute = 2; componentDatatype = ComponentDatatype$1.UNSIGNED_BYTE; } else if (isOctEncodedDraco) { componentsPerAttribute = 2; componentDatatype = octEncodedRange <= 255 ? ComponentDatatype$1.UNSIGNED_BYTE : ComponentDatatype$1.UNSIGNED_SHORT; } else { componentsPerAttribute = 3; componentDatatype = ComponentDatatype$1.FLOAT; } attributes.push({ index: normalLocation, vertexBuffer: normalsVertexBuffer, componentsPerAttribute: componentsPerAttribute, componentDatatype: componentDatatype, normalize: false, offsetInBytes: 0, strideInBytes: 0, }); } if (hasBatchIds) { attributes.push({ index: batchIdLocation, vertexBuffer: batchIdsVertexBuffer, componentsPerAttribute: 1, componentDatatype: ComponentDatatype$1.fromTypedArray(batchIds), normalize: false, offsetInBytes: 0, strideInBytes: 0, }); } if (hasStyleableProperties) { attributes = attributes.concat(styleableVertexAttributes); } var vertexArray = new VertexArray({ context: context, attributes: attributes, }); var opaqueRenderState = { depthTest: { enabled: true, }, }; if (pointCloud._opaquePass === Pass$1.CESIUM_3D_TILE) { opaqueRenderState.stencilTest = StencilConstants$1.setCesium3DTileBit(); opaqueRenderState.stencilMask = StencilConstants$1.CESIUM_3D_TILE_MASK; } pointCloud._opaqueRenderState = RenderState.fromCache(opaqueRenderState); pointCloud._translucentRenderState = RenderState.fromCache({ depthTest: { enabled: true, }, depthMask: false, blending: BlendingState$1.ALPHA_BLEND, }); pointCloud._drawCommand = new DrawCommand({ boundingVolume: new BoundingSphere(), cull: pointCloud._cull, modelMatrix: new Matrix4(), primitiveType: PrimitiveType$1.POINTS, vertexArray: vertexArray, count: pointsLength, shaderProgram: undefined, // Updated in createShaders uniformMap: undefined, // Updated in createShaders renderState: isTranslucent ? pointCloud._translucentRenderState : pointCloud._opaqueRenderState, pass: isTranslucent ? Pass$1.TRANSLUCENT : pointCloud._opaquePass, owner: pointCloud, castShadows: false, receiveShadows: false, pickId: pointCloud._pickIdLoaded(), }); } function createUniformMap$3(pointCloud, frameState) { var context = frameState.context; var isQuantized = pointCloud._isQuantized; var isQuantizedDraco = pointCloud._isQuantizedDraco; var isOctEncodedDraco = pointCloud._isOctEncodedDraco; var uniformMap = { u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier: function () { var scratch = scratchPointSizeAndTimeAndGeometricErrorAndDepthMultiplier; scratch.x = pointCloud._attenuation ? pointCloud.maximumAttenuation : pointCloud._pointSize; scratch.x *= frameState.pixelRatio; scratch.y = pointCloud.time; if (pointCloud._attenuation) { var frustum = frameState.camera.frustum; var depthMultiplier; // Attenuation is maximumAttenuation in 2D/ortho if ( frameState.mode === SceneMode$1.SCENE2D || frustum instanceof OrthographicFrustum ) { depthMultiplier = Number.POSITIVE_INFINITY; } else { depthMultiplier = context.drawingBufferHeight / frameState.camera.frustum.sseDenominator; } scratch.z = pointCloud.geometricError * pointCloud.geometricErrorScale; scratch.w = depthMultiplier; } return scratch; }, u_highlightColor: function () { return pointCloud._highlightColor; }, u_constantColor: function () { return pointCloud._constantColor; }, u_clippingPlanes: function () { var clippingPlanes = pointCloud.clippingPlanes; var isClipped = pointCloud.isClipped; return isClipped ? clippingPlanes.texture : context.defaultTexture; }, u_clippingPlanesEdgeStyle: function () { var clippingPlanes = pointCloud.clippingPlanes; if (!defined(clippingPlanes)) { return Color.TRANSPARENT; } var style = Color.clone(clippingPlanes.edgeColor, scratchColor$h); style.alpha = clippingPlanes.edgeWidth; return style; }, u_clippingPlanesMatrix: function () { var clippingPlanes = pointCloud.clippingPlanes; if (!defined(clippingPlanes)) { return Matrix4.IDENTITY; } var clippingPlanesOriginMatrix = defaultValue( pointCloud.clippingPlanesOriginMatrix, pointCloud._modelMatrix ); Matrix4.multiply( context.uniformState.view3D, clippingPlanesOriginMatrix, scratchClippingPlanesMatrix$1 ); var transform = Matrix4.multiply( scratchClippingPlanesMatrix$1, clippingPlanes.modelMatrix, scratchClippingPlanesMatrix$1 ); return Matrix4.inverseTranspose( transform, scratchInverseTransposeClippingPlanesMatrix$1 ); }, }; if (isQuantized || isQuantizedDraco || isOctEncodedDraco) { uniformMap = combine$2(uniformMap, { u_quantizedVolumeScaleAndOctEncodedRange: function () { var scratch = scratchQuantizedVolumeScaleAndOctEncodedRange; if (defined(pointCloud._quantizedVolumeScale)) { var scale = Cartesian3.clone( pointCloud._quantizedVolumeScale, scratch ); Cartesian3.divideByScalar(scale, pointCloud._quantizedRange, scratch); } scratch.w = pointCloud._octEncodedRange; return scratch; }, }); } if (defined(pointCloud._uniformMapLoaded)) { uniformMap = pointCloud._uniformMapLoaded(uniformMap); } pointCloud._drawCommand.uniformMap = uniformMap; } function getStyleablePropertyIds(source, propertyIds) { // Get all the property IDs used by this style var regex = /czm_3dtiles_property_(\d+)/g; var matches = regex.exec(source); while (matches !== null) { var id = parseInt(matches[1]); if (propertyIds.indexOf(id) === -1) { propertyIds.push(id); } matches = regex.exec(source); } } function getBuiltinPropertyNames(source, propertyNames) { // Get all the builtin property names used by this style var regex = /czm_3dtiles_builtin_property_(\w+)/g; var matches = regex.exec(source); while (matches !== null) { var name = matches[1]; if (propertyNames.indexOf(name) === -1) { propertyNames.push(name); } matches = regex.exec(source); } } function getVertexAttribute(vertexArray, index) { var numberOfAttributes = vertexArray.numberOfAttributes; for (var i = 0; i < numberOfAttributes; ++i) { var attribute = vertexArray.getAttribute(i); if (attribute.index === index) { return attribute; } } } var builtinPropertyNameMap = { POSITION: "czm_3dtiles_builtin_property_POSITION", POSITION_ABSOLUTE: "czm_3dtiles_builtin_property_POSITION_ABSOLUTE", COLOR: "czm_3dtiles_builtin_property_COLOR", NORMAL: "czm_3dtiles_builtin_property_NORMAL", }; function modifyStyleFunction(source) { // Edit the function header to accept the point position, color, and normal var functionHeader = "(" + "vec3 czm_3dtiles_builtin_property_POSITION, " + "vec3 czm_3dtiles_builtin_property_POSITION_ABSOLUTE, " + "vec4 czm_3dtiles_builtin_property_COLOR, " + "vec3 czm_3dtiles_builtin_property_NORMAL" + ")"; return source.replace("()", functionHeader); } function createShaders$1(pointCloud, frameState, style) { var i; var name; var attribute; var context = frameState.context; var hasStyle = defined(style); var isQuantized = pointCloud._isQuantized; var isQuantizedDraco = pointCloud._isQuantizedDraco; var isOctEncoded16P = pointCloud._isOctEncoded16P; var isOctEncodedDraco = pointCloud._isOctEncodedDraco; var isRGB565 = pointCloud._isRGB565; var isTranslucent = pointCloud._isTranslucent; var hasColors = pointCloud._hasColors; var hasNormals = pointCloud._hasNormals; var hasBatchIds = pointCloud._hasBatchIds; var backFaceCulling = pointCloud._backFaceCulling; var normalShading = pointCloud._normalShading; var vertexArray = pointCloud._drawCommand.vertexArray; var clippingPlanes = pointCloud.clippingPlanes; var attenuation = pointCloud._attenuation; var colorStyleFunction; var showStyleFunction; var pointSizeStyleFunction; var styleTranslucent = isTranslucent; var propertyNameMap = clone$1(builtinPropertyNameMap); var propertyIdToAttributeMap = {}; var styleableShaderAttributes = pointCloud._styleableShaderAttributes; for (name in styleableShaderAttributes) { if (styleableShaderAttributes.hasOwnProperty(name)) { attribute = styleableShaderAttributes[name]; propertyNameMap[name] = "czm_3dtiles_property_" + attribute.location; propertyIdToAttributeMap[attribute.location] = attribute; } } if (hasStyle) { var shaderState = { translucent: false, }; colorStyleFunction = style.getColorShaderFunction( "getColorFromStyle", propertyNameMap, shaderState ); showStyleFunction = style.getShowShaderFunction( "getShowFromStyle", propertyNameMap, shaderState ); pointSizeStyleFunction = style.getPointSizeShaderFunction( "getPointSizeFromStyle", propertyNameMap, shaderState ); if (defined(colorStyleFunction) && shaderState.translucent) { styleTranslucent = true; } } pointCloud._styleTranslucent = styleTranslucent; var hasColorStyle = defined(colorStyleFunction); var hasShowStyle = defined(showStyleFunction); var hasPointSizeStyle = defined(pointSizeStyleFunction); var hasClippedContent = pointCloud.isClipped; // Get the properties in use by the style var styleablePropertyIds = []; var builtinPropertyNames = []; if (hasColorStyle) { getStyleablePropertyIds(colorStyleFunction, styleablePropertyIds); getBuiltinPropertyNames(colorStyleFunction, builtinPropertyNames); colorStyleFunction = modifyStyleFunction(colorStyleFunction); } if (hasShowStyle) { getStyleablePropertyIds(showStyleFunction, styleablePropertyIds); getBuiltinPropertyNames(showStyleFunction, builtinPropertyNames); showStyleFunction = modifyStyleFunction(showStyleFunction); } if (hasPointSizeStyle) { getStyleablePropertyIds(pointSizeStyleFunction, styleablePropertyIds); getBuiltinPropertyNames(pointSizeStyleFunction, builtinPropertyNames); pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction); } var usesColorSemantic = builtinPropertyNames.indexOf("COLOR") >= 0; var usesNormalSemantic = builtinPropertyNames.indexOf("NORMAL") >= 0; if (usesNormalSemantic && !hasNormals) { throw new RuntimeError( "Style references the NORMAL semantic but the point cloud does not have normals" ); } // Disable vertex attributes that aren't used in the style, enable attributes that are for (name in styleableShaderAttributes) { if (styleableShaderAttributes.hasOwnProperty(name)) { attribute = styleableShaderAttributes[name]; var enabled = styleablePropertyIds.indexOf(attribute.location) >= 0; var vertexAttribute = getVertexAttribute(vertexArray, attribute.location); vertexAttribute.enabled = enabled; } } var usesColors = hasColors && (!hasColorStyle || usesColorSemantic); if (hasColors) { // Disable the color vertex attribute if the color style does not reference the color semantic var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation); colorVertexAttribute.enabled = usesColors; } var usesNormals = hasNormals && (normalShading || backFaceCulling || usesNormalSemantic); if (hasNormals) { // Disable the normal vertex attribute if normals are not used var normalVertexAttribute = getVertexAttribute(vertexArray, normalLocation); normalVertexAttribute.enabled = usesNormals; } var attributeLocations = { a_position: positionLocation, }; if (usesColors) { attributeLocations.a_color = colorLocation; } if (usesNormals) { attributeLocations.a_normal = normalLocation; } if (hasBatchIds) { attributeLocations.a_batchId = batchIdLocation; } var attributeDeclarations = ""; var length = styleablePropertyIds.length; for (i = 0; i < length; ++i) { var propertyId = styleablePropertyIds[i]; attribute = propertyIdToAttributeMap[propertyId]; var componentCount = attribute.componentCount; var attributeName = "czm_3dtiles_property_" + propertyId; var attributeType; if (componentCount === 1) { attributeType = "float"; } else { attributeType = "vec" + componentCount; } attributeDeclarations += "attribute " + attributeType + " " + attributeName + "; \n"; attributeLocations[attributeName] = attribute.location; } createUniformMap$3(pointCloud, frameState); var vs = "attribute vec3 a_position; \n" + "varying vec4 v_color; \n" + "uniform vec4 u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier; \n" + "uniform vec4 u_constantColor; \n" + "uniform vec4 u_highlightColor; \n"; vs += "float u_pointSize; \n" + "float u_time; \n"; if (attenuation) { vs += "float u_geometricError; \n" + "float u_depthMultiplier; \n"; } vs += attributeDeclarations; if (usesColors) { if (isTranslucent) { vs += "attribute vec4 a_color; \n"; } else if (isRGB565) { vs += "attribute float a_color; \n" + "const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n" + "const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n" + "const float SHIFT_LEFT_11 = 2048.0; \n" + "const float SHIFT_LEFT_5 = 32.0; \n" + "const float NORMALIZE_6 = 1.0 / 64.0; \n" + "const float NORMALIZE_5 = 1.0 / 32.0; \n"; } else { vs += "attribute vec3 a_color; \n"; } } if (usesNormals) { if (isOctEncoded16P || isOctEncodedDraco) { vs += "attribute vec2 a_normal; \n"; } else { vs += "attribute vec3 a_normal; \n"; } } if (hasBatchIds) { vs += "attribute float a_batchId; \n"; } if (isQuantized || isQuantizedDraco || isOctEncodedDraco) { vs += "uniform vec4 u_quantizedVolumeScaleAndOctEncodedRange; \n"; } if (hasColorStyle) { vs += colorStyleFunction; } if (hasShowStyle) { vs += showStyleFunction; } if (hasPointSizeStyle) { vs += pointSizeStyleFunction; } vs += "void main() \n" + "{ \n" + " u_pointSize = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.x; \n" + " u_time = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.y; \n"; if (attenuation) { vs += " u_geometricError = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.z; \n" + " u_depthMultiplier = u_pointSizeAndTimeAndGeometricErrorAndDepthMultiplier.w; \n"; } if (usesColors) { if (isTranslucent) { vs += " vec4 color = a_color; \n"; } else if (isRGB565) { vs += " float compressed = a_color; \n" + " float r = floor(compressed * SHIFT_RIGHT_11); \n" + " compressed -= r * SHIFT_LEFT_11; \n" + " float g = floor(compressed * SHIFT_RIGHT_5); \n" + " compressed -= g * SHIFT_LEFT_5; \n" + " float b = compressed; \n" + " vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n" + " vec4 color = vec4(rgb, 1.0); \n"; } else { vs += " vec4 color = vec4(a_color, 1.0); \n"; } } else { vs += " vec4 color = u_constantColor; \n"; } if (isQuantized || isQuantizedDraco) { vs += " vec3 position = a_position * u_quantizedVolumeScaleAndOctEncodedRange.xyz; \n"; } else { vs += " vec3 position = a_position; \n"; } vs += " vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n"; if (usesNormals) { if (isOctEncoded16P) { vs += " vec3 normal = czm_octDecode(a_normal); \n"; } else if (isOctEncodedDraco) { // Draco oct-encoding decodes to zxy order vs += " vec3 normal = czm_octDecode(a_normal, u_quantizedVolumeScaleAndOctEncodedRange.w).zxy; \n"; } else { vs += " vec3 normal = a_normal; \n"; } vs += " vec3 normalEC = czm_normal * normal; \n"; } else { vs += " vec3 normal = vec3(1.0); \n"; } if (hasColorStyle) { vs += " color = getColorFromStyle(position, position_absolute, color, normal); \n"; } if (hasShowStyle) { vs += " float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n"; } if (hasPointSizeStyle) { vs += " gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal) * czm_pixelRatio; \n"; } else if (attenuation) { vs += " vec4 positionEC = czm_modelView * vec4(position, 1.0); \n" + " float depth = -positionEC.z; \n" + // compute SSE for this point " gl_PointSize = min((u_geometricError / depth) * u_depthMultiplier, u_pointSize); \n"; } else { vs += " gl_PointSize = u_pointSize; \n"; } vs += " color = color * u_highlightColor; \n"; if (usesNormals && normalShading) { vs += " float diffuseStrength = czm_getLambertDiffuse(czm_lightDirectionEC, normalEC); \n" + " diffuseStrength = max(diffuseStrength, 0.4); \n" + // Apply some ambient lighting " color.xyz *= diffuseStrength * czm_lightColor; \n"; } vs += " v_color = color; \n" + " gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n"; if (usesNormals && backFaceCulling) { vs += " float visible = step(-normalEC.z, 0.0); \n" + " gl_Position *= visible; \n" + " gl_PointSize *= visible; \n"; } if (hasShowStyle) { vs += " gl_Position.w *= float(show); \n" + " gl_PointSize *= float(show); \n"; } vs += "} \n"; var fs = "varying vec4 v_color; \n"; if (hasClippedContent) { fs += "uniform highp sampler2D u_clippingPlanes; \n" + "uniform mat4 u_clippingPlanesMatrix; \n" + "uniform vec4 u_clippingPlanesEdgeStyle; \n"; fs += "\n"; fs += getClippingFunction(clippingPlanes, context); fs += "\n"; } fs += "void main() \n" + "{ \n" + " gl_FragColor = czm_gammaCorrect(v_color); \n"; if (hasClippedContent) { fs += getClipAndStyleCode( "u_clippingPlanes", "u_clippingPlanesMatrix", "u_clippingPlanesEdgeStyle" ); } fs += "} \n"; if (defined(pointCloud._vertexShaderLoaded)) { vs = pointCloud._vertexShaderLoaded(vs); } if (defined(pointCloud._fragmentShaderLoaded)) { fs = pointCloud._fragmentShaderLoaded(fs); } var drawCommand = pointCloud._drawCommand; if (defined(drawCommand.shaderProgram)) { // Destroy the old shader drawCommand.shaderProgram.destroy(); } drawCommand.shaderProgram = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations, }); try { // Check if the shader compiles correctly. If not there is likely a syntax error with the style. drawCommand.shaderProgram._bind(); } catch (error) { // Rephrase the error. throw new RuntimeError( "Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error." ); } } function decodeDraco(pointCloud, context) { if (pointCloud._decodingState === DecodingState.READY) { return false; } if (pointCloud._decodingState === DecodingState.NEEDS_DECODE) { var parsedContent = pointCloud._parsedContent; var draco = parsedContent.draco; var decodePromise = DracoLoader.decodePointCloud(draco, context); if (defined(decodePromise)) { pointCloud._decodingState = DecodingState.DECODING; decodePromise .then(function (result) { pointCloud._decodingState = DecodingState.READY; var decodedPositions = defined(result.POSITION) ? result.POSITION.array : undefined; var decodedRgb = defined(result.RGB) ? result.RGB.array : undefined; var decodedRgba = defined(result.RGBA) ? result.RGBA.array : undefined; var decodedNormals = defined(result.NORMAL) ? result.NORMAL.array : undefined; var decodedBatchIds = defined(result.BATCH_ID) ? result.BATCH_ID.array : undefined; var isQuantizedDraco = defined(decodedPositions) && defined(result.POSITION.data.quantization); var isOctEncodedDraco = defined(decodedNormals) && defined(result.NORMAL.data.quantization); if (isQuantizedDraco) { // Draco quantization range == quantized volume scale - size in meters of the quantized volume // Internal quantized range is the range of values of the quantized data, e.g. 255 for 8-bit, 1023 for 10-bit, etc var quantization = result.POSITION.data.quantization; var range = quantization.range; pointCloud._quantizedVolumeScale = Cartesian3.fromElements( range, range, range ); pointCloud._quantizedVolumeOffset = Cartesian3.unpack( quantization.minValues ); pointCloud._quantizedRange = (1 << quantization.quantizationBits) - 1.0; pointCloud._isQuantizedDraco = true; } if (isOctEncodedDraco) { pointCloud._octEncodedRange = (1 << result.NORMAL.data.quantization.quantizationBits) - 1.0; pointCloud._isOctEncodedDraco = true; } var styleableProperties = parsedContent.styleableProperties; var batchTableProperties = draco.batchTableProperties; for (var name in batchTableProperties) { if (batchTableProperties.hasOwnProperty(name)) { var property = result[name]; if (!defined(styleableProperties)) { styleableProperties = {}; } styleableProperties[name] = { typedArray: property.array, componentCount: property.data.componentsPerAttribute, }; } } parsedContent.positions = defaultValue( decodedPositions, parsedContent.positions ); parsedContent.colors = defaultValue( defaultValue(decodedRgba, decodedRgb), parsedContent.colors ); parsedContent.normals = defaultValue( decodedNormals, parsedContent.normals ); parsedContent.batchIds = defaultValue( decodedBatchIds, parsedContent.batchIds ); parsedContent.styleableProperties = styleableProperties; }) .otherwise(function (error) { pointCloud._decodingState = DecodingState.FAILED; pointCloud._readyPromise.reject(error); }); } } return true; } var scratchComputedTranslation = new Cartesian4(); var scratchScale$3 = new Cartesian3(); PointCloud.prototype.update = function (frameState) { var context = frameState.context; var decoding = decodeDraco(this, context); if (decoding) { return; } var shadersDirty = false; var modelMatrixDirty = !Matrix4.equals(this._modelMatrix, this.modelMatrix); if (this._mode !== frameState.mode) { this._mode = frameState.mode; modelMatrixDirty = true; } if (!defined(this._drawCommand)) { createResources$4(this, frameState); modelMatrixDirty = true; shadersDirty = true; this._ready = true; this._readyPromise.resolve(this); this._parsedContent = undefined; // Unload } if (modelMatrixDirty) { Matrix4.clone(this.modelMatrix, this._modelMatrix); var modelMatrix = this._drawCommand.modelMatrix; Matrix4.clone(this._modelMatrix, modelMatrix); if (defined(this._rtcCenter)) { Matrix4.multiplyByTranslation(modelMatrix, this._rtcCenter, modelMatrix); } if (defined(this._quantizedVolumeOffset)) { Matrix4.multiplyByTranslation( modelMatrix, this._quantizedVolumeOffset, modelMatrix ); } if (frameState.mode !== SceneMode$1.SCENE3D) { var projection = frameState.mapProjection; var translation = Matrix4.getColumn( modelMatrix, 3, scratchComputedTranslation ); if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) { Transforms.basisTo2D(projection, modelMatrix, modelMatrix); } } var boundingSphere = this._drawCommand.boundingVolume; BoundingSphere.clone(this._boundingSphere, boundingSphere); if (this._cull) { var center = boundingSphere.center; Matrix4.multiplyByPoint(modelMatrix, center, center); var scale = Matrix4.getScale(modelMatrix, scratchScale$3); boundingSphere.radius *= Cartesian3.maximumComponent(scale); } } if (this.clippingPlanesDirty) { this.clippingPlanesDirty = false; shadersDirty = true; } if (this._attenuation !== this.attenuation) { this._attenuation = this.attenuation; shadersDirty = true; } if (this.backFaceCulling !== this._backFaceCulling) { this._backFaceCulling = this.backFaceCulling; shadersDirty = true; } if (this.normalShading !== this._normalShading) { this._normalShading = this.normalShading; shadersDirty = true; } if (this._style !== this.style || this.styleDirty) { this._style = this.style; this.styleDirty = false; shadersDirty = true; } if (shadersDirty) { createShaders$1(this, frameState, this._style); } this._drawCommand.castShadows = ShadowMode$1.castShadows(this.shadows); this._drawCommand.receiveShadows = ShadowMode$1.receiveShadows(this.shadows); // Update the render state var isTranslucent = this._highlightColor.alpha < 1.0 || this._constantColor.alpha < 1.0 || this._styleTranslucent; this._drawCommand.renderState = isTranslucent ? this._translucentRenderState : this._opaqueRenderState; this._drawCommand.pass = isTranslucent ? Pass$1.TRANSLUCENT : this._opaquePass; var commandList = frameState.commandList; var passes = frameState.passes; if (passes.render || passes.pick) { commandList.push(this._drawCommand); } }; PointCloud.prototype.isDestroyed = function () { return false; }; PointCloud.prototype.destroy = function () { var command = this._drawCommand; if (defined(command)) { command.vertexArray = command.vertexArray && command.vertexArray.destroy(); command.shaderProgram = command.shaderProgram && command.shaderProgram.destroy(); } return destroyObject(this); }; function attachTexture(framebuffer, attachment, texture) { var gl = framebuffer._gl; gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment, texture._target, texture._texture, 0 ); } function attachRenderbuffer(framebuffer, attachment, renderbuffer) { var gl = framebuffer._gl; gl.framebufferRenderbuffer( gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer._getRenderbuffer() ); } /** * Creates a framebuffer with optional initial color, depth, and stencil attachments. * Framebuffers are used for render-to-texture effects; they allow us to render to * textures in one pass, and read from it in a later pass. * * @param {Object} options The initial framebuffer attachments as shown in the example below. context is required. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, and depthStencilRenderbuffer. * * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments. * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer. * @exception {DeveloperError} The color-texture pixel-format must be a color format. * @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT. * @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL. * @exception {DeveloperError} The number of color attachments exceeds the number supported. * @exception {DeveloperError} The color-texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. * @exception {DeveloperError} The color-texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. * * @example * // Create a framebuffer with color and depth texture attachments. * var width = context.canvas.clientWidth; * var height = context.canvas.clientHeight; * var framebuffer = new Framebuffer({ * context : context, * colorTextures : [new Texture({ * context : context, * width : width, * height : height, * pixelFormat : PixelFormat.RGBA * })], * depthTexture : new Texture({ * context : context, * width : width, * height : height, * pixelFormat : PixelFormat.DEPTH_COMPONENT, * pixelDatatype : PixelDatatype.UNSIGNED_SHORT * }) * }); * * @private * @constructor */ function Framebuffer(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var context = options.context; //>>includeStart('debug', pragmas.debug); Check.defined("options.context", context); //>>includeEnd('debug'); var gl = context._gl; var maximumColorAttachments = ContextLimits.maximumColorAttachments; this._gl = gl; this._framebuffer = gl.createFramebuffer(); this._colorTextures = []; this._colorRenderbuffers = []; this._activeColorAttachments = []; this._depthTexture = undefined; this._depthRenderbuffer = undefined; this._stencilRenderbuffer = undefined; this._depthStencilTexture = undefined; this._depthStencilRenderbuffer = undefined; /** * When true, the framebuffer owns its attachments so they will be destroyed when * {@link Framebuffer#destroy} is called or when a new attachment is assigned * to an attachment point. * * @type {Boolean} * @default true * * @see Framebuffer#destroy */ this.destroyAttachments = defaultValue(options.destroyAttachments, true); // Throw if a texture and renderbuffer are attached to the same point. This won't // cause a WebGL error (because only one will be attached), but is likely a developer error. //>>includeStart('debug', pragmas.debug); if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) { throw new DeveloperError( "Cannot have both color texture and color renderbuffer attachments." ); } if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) { throw new DeveloperError( "Cannot have both a depth texture and depth renderbuffer attachment." ); } if ( defined(options.depthStencilTexture) && defined(options.depthStencilRenderbuffer) ) { throw new DeveloperError( "Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment." ); } //>>includeEnd('debug'); // Avoid errors defined in Section 6.5 of the WebGL spec var depthAttachment = defined(options.depthTexture) || defined(options.depthRenderbuffer); var depthStencilAttachment = defined(options.depthStencilTexture) || defined(options.depthStencilRenderbuffer); //>>includeStart('debug', pragmas.debug); if (depthAttachment && depthStencilAttachment) { throw new DeveloperError( "Cannot have both a depth and depth-stencil attachment." ); } if (defined(options.stencilRenderbuffer) && depthStencilAttachment) { throw new DeveloperError( "Cannot have both a stencil and depth-stencil attachment." ); } if (depthAttachment && defined(options.stencilRenderbuffer)) { throw new DeveloperError( "Cannot have both a depth and stencil attachment." ); } //>>includeEnd('debug'); /////////////////////////////////////////////////////////////////// this._bind(); var texture; var renderbuffer; var i; var length; var attachmentEnum; if (defined(options.colorTextures)) { var textures = options.colorTextures; length = this._colorTextures.length = this._activeColorAttachments.length = textures.length; //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { throw new DeveloperError( "The number of color attachments exceeds the number supported." ); } //>>includeEnd('debug'); for (i = 0; i < length; ++i) { texture = textures[i]; //>>includeStart('debug', pragmas.debug); if (!PixelFormat$1.isColorFormat(texture.pixelFormat)) { throw new DeveloperError( "The color-texture pixel-format must be a color format." ); } if ( texture.pixelDatatype === PixelDatatype$1.FLOAT && !context.colorBufferFloat ) { throw new DeveloperError( "The color texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. See Context.colorBufferFloat." ); } if ( texture.pixelDatatype === PixelDatatype$1.HALF_FLOAT && !context.colorBufferHalfFloat ) { throw new DeveloperError( "The color texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. See Context.colorBufferHalfFloat." ); } //>>includeEnd('debug'); attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; attachTexture(this, attachmentEnum, texture); this._activeColorAttachments[i] = attachmentEnum; this._colorTextures[i] = texture; } } if (defined(options.colorRenderbuffers)) { var renderbuffers = options.colorRenderbuffers; length = this._colorRenderbuffers.length = this._activeColorAttachments.length = renderbuffers.length; //>>includeStart('debug', pragmas.debug); if (length > maximumColorAttachments) { throw new DeveloperError( "The number of color attachments exceeds the number supported." ); } //>>includeEnd('debug'); for (i = 0; i < length; ++i) { renderbuffer = renderbuffers[i]; attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i; attachRenderbuffer(this, attachmentEnum, renderbuffer); this._activeColorAttachments[i] = attachmentEnum; this._colorRenderbuffers[i] = renderbuffer; } } if (defined(options.depthTexture)) { texture = options.depthTexture; //>>includeStart('debug', pragmas.debug); if (texture.pixelFormat !== PixelFormat$1.DEPTH_COMPONENT) { throw new DeveloperError( "The depth-texture pixel-format must be DEPTH_COMPONENT." ); } //>>includeEnd('debug'); attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture); this._depthTexture = texture; } if (defined(options.depthRenderbuffer)) { renderbuffer = options.depthRenderbuffer; attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer); this._depthRenderbuffer = renderbuffer; } if (defined(options.stencilRenderbuffer)) { renderbuffer = options.stencilRenderbuffer; attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer); this._stencilRenderbuffer = renderbuffer; } if (defined(options.depthStencilTexture)) { texture = options.depthStencilTexture; //>>includeStart('debug', pragmas.debug); if (texture.pixelFormat !== PixelFormat$1.DEPTH_STENCIL) { throw new DeveloperError( "The depth-stencil pixel-format must be DEPTH_STENCIL." ); } //>>includeEnd('debug'); attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture); this._depthStencilTexture = texture; } if (defined(options.depthStencilRenderbuffer)) { renderbuffer = options.depthStencilRenderbuffer; attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer); this._depthStencilRenderbuffer = renderbuffer; } this._unBind(); } Object.defineProperties(Framebuffer.prototype, { /** * The status of the framebuffer. If the status is not WebGLConstants.FRAMEBUFFER_COMPLETE, * a {@link DeveloperError} will be thrown when attempting to render to the framebuffer. * @memberof Framebuffer.prototype * @type {Number} */ status: { get: function () { this._bind(); var status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER); this._unBind(); return status; }, }, numberOfColorAttachments: { get: function () { return this._activeColorAttachments.length; }, }, depthTexture: { get: function () { return this._depthTexture; }, }, depthRenderbuffer: { get: function () { return this._depthRenderbuffer; }, }, stencilRenderbuffer: { get: function () { return this._stencilRenderbuffer; }, }, depthStencilTexture: { get: function () { return this._depthStencilTexture; }, }, depthStencilRenderbuffer: { get: function () { return this._depthStencilRenderbuffer; }, }, /** * True if the framebuffer has a depth attachment. Depth attachments include * depth and depth-stencil textures, and depth and depth-stencil renderbuffers. When * rendering to a framebuffer, a depth attachment is required for the depth test to have effect. * @memberof Framebuffer.prototype * @type {Boolean} */ hasDepthAttachment: { get: function () { return !!( this.depthTexture || this.depthRenderbuffer || this.depthStencilTexture || this.depthStencilRenderbuffer ); }, }, }); Framebuffer.prototype._bind = function () { var gl = this._gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer); }; Framebuffer.prototype._unBind = function () { var gl = this._gl; gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; Framebuffer.prototype._getActiveColorAttachments = function () { return this._activeColorAttachments; }; Framebuffer.prototype.getColorTexture = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index) || index < 0 || index >= this._colorTextures.length) { throw new DeveloperError( "index is required, must be greater than or equal to zero and must be less than the number of color attachments." ); } //>>includeEnd('debug'); return this._colorTextures[index]; }; Framebuffer.prototype.getColorRenderbuffer = function (index) { //>>includeStart('debug', pragmas.debug); if ( !defined(index) || index < 0 || index >= this._colorRenderbuffers.length ) { throw new DeveloperError( "index is required, must be greater than or equal to zero and must be less than the number of color attachments." ); } //>>includeEnd('debug'); return this._colorRenderbuffers[index]; }; Framebuffer.prototype.isDestroyed = function () { return false; }; Framebuffer.prototype.destroy = function () { if (this.destroyAttachments) { // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed. var i = 0; var textures = this._colorTextures; var length = textures.length; for (; i < length; ++i) { var texture = textures[i]; if (defined(texture)) { texture.destroy(); } } var renderbuffers = this._colorRenderbuffers; length = renderbuffers.length; for (i = 0; i < length; ++i) { var renderbuffer = renderbuffers[i]; if (defined(renderbuffer)) { renderbuffer.destroy(); } } this._depthTexture = this._depthTexture && this._depthTexture.destroy(); this._depthRenderbuffer = this._depthRenderbuffer && this._depthRenderbuffer.destroy(); this._stencilRenderbuffer = this._stencilRenderbuffer && this._stencilRenderbuffer.destroy(); this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy(); this._depthStencilRenderbuffer = this._depthStencilRenderbuffer && this._depthStencilRenderbuffer.destroy(); } this._gl.deleteFramebuffer(this._framebuffer); return destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var PointCloudEyeDomeLightingShader = "#extension GL_EXT_frag_depth : enable\n\ \n\ uniform sampler2D u_pointCloud_colorGBuffer;\n\ uniform sampler2D u_pointCloud_depthGBuffer;\n\ uniform vec2 u_distanceAndEdlStrength;\n\ varying vec2 v_textureCoordinates;\n\ \n\ vec2 neighborContribution(float log2Depth, vec2 offset)\n\ {\n\ float dist = u_distanceAndEdlStrength.x;\n\ vec2 texCoordOrig = v_textureCoordinates + offset * dist;\n\ vec2 texCoord0 = v_textureCoordinates + offset * floor(dist);\n\ vec2 texCoord1 = v_textureCoordinates + offset * ceil(dist);\n\ \n\ float depthOrLogDepth0 = czm_unpackDepth(texture2D(u_pointCloud_depthGBuffer, texCoord0));\n\ float depthOrLogDepth1 = czm_unpackDepth(texture2D(u_pointCloud_depthGBuffer, texCoord1));\n\ \n\ // ignore depth values that are the clear depth\n\ if (depthOrLogDepth0 == 0.0 || depthOrLogDepth1 == 0.0) {\n\ return vec2(0.0);\n\ }\n\ \n\ // interpolate the two adjacent depth values\n\ float depthMix = mix(depthOrLogDepth0, depthOrLogDepth1, fract(dist));\n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(texCoordOrig, depthMix);\n\ return vec2(max(0.0, log2Depth - log2(-eyeCoordinate.z / eyeCoordinate.w)), 1.0);\n\ }\n\ \n\ void main()\n\ {\n\ float depthOrLogDepth = czm_unpackDepth(texture2D(u_pointCloud_depthGBuffer, v_textureCoordinates));\n\ \n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depthOrLogDepth);\n\ eyeCoordinate /= eyeCoordinate.w;\n\ \n\ float log2Depth = log2(-eyeCoordinate.z);\n\ \n\ if (depthOrLogDepth == 0.0) // 0.0 is the clear value for the gbuffer\n\ {\n\ discard;\n\ }\n\ \n\ vec4 color = texture2D(u_pointCloud_colorGBuffer, v_textureCoordinates);\n\ \n\ // sample from neighbors left, right, down, up\n\ vec2 texelSize = 1.0 / czm_viewport.zw;\n\ \n\ vec2 responseAndCount = vec2(0.0);\n\ \n\ responseAndCount += neighborContribution(log2Depth, vec2(-texelSize.x, 0.0));\n\ responseAndCount += neighborContribution(log2Depth, vec2(+texelSize.x, 0.0));\n\ responseAndCount += neighborContribution(log2Depth, vec2(0.0, -texelSize.y));\n\ responseAndCount += neighborContribution(log2Depth, vec2(0.0, +texelSize.y));\n\ \n\ float response = responseAndCount.x / responseAndCount.y;\n\ float strength = u_distanceAndEdlStrength.y;\n\ float shade = exp(-response * 300.0 * strength);\n\ color.rgb *= shade;\n\ gl_FragColor = vec4(color);\n\ \n\ // Input and output depth are the same.\n\ gl_FragDepthEXT = depthOrLogDepth;\n\ }\n\ "; /** * Eye dome lighting. Does not support points with per-point translucency, but does allow translucent styling against the globe. * Requires support for EXT_frag_depth and WEBGL_draw_buffers extensions in WebGL 1.0. * * @private */ function PointCloudEyeDomeLighting() { this._framebuffer = undefined; this._colorGBuffer = undefined; // color gbuffer this._depthGBuffer = undefined; // depth gbuffer this._depthTexture = undefined; // needed to write depth so camera based on depth works this._drawCommand = undefined; this._clearCommand = undefined; this._strength = 1.0; this._radius = 1.0; } function destroyFramebuffer$1(processor) { var framebuffer = processor._framebuffer; if (!defined(framebuffer)) { return; } processor._colorGBuffer.destroy(); processor._depthGBuffer.destroy(); processor._depthTexture.destroy(); framebuffer.destroy(); processor._framebuffer = undefined; processor._colorGBuffer = undefined; processor._depthGBuffer = undefined; processor._depthTexture = undefined; processor._drawCommand = undefined; processor._clearCommand = undefined; } function createFramebuffer$3(processor, context) { var screenWidth = context.drawingBufferWidth; var screenHeight = context.drawingBufferHeight; var colorGBuffer = new Texture({ context: context, width: screenWidth, height: screenHeight, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: PixelDatatype$1.UNSIGNED_BYTE, sampler: Sampler.NEAREST, }); var depthGBuffer = new Texture({ context: context, width: screenWidth, height: screenHeight, pixelFormat: PixelFormat$1.RGBA, pixelDatatype: PixelDatatype$1.UNSIGNED_BYTE, sampler: Sampler.NEAREST, }); var depthTexture = new Texture({ context: context, width: screenWidth, height: screenHeight, pixelFormat: PixelFormat$1.DEPTH_COMPONENT, pixelDatatype: PixelDatatype$1.UNSIGNED_INT, sampler: Sampler.NEAREST, }); processor._framebuffer = new Framebuffer({ context: context, colorTextures: [colorGBuffer, depthGBuffer], depthTexture: depthTexture, destroyAttachments: false, }); processor._colorGBuffer = colorGBuffer; processor._depthGBuffer = depthGBuffer; processor._depthTexture = depthTexture; } var distanceAndEdlStrengthScratch = new Cartesian2(); function createCommands$1(processor, context) { var blendFS = new ShaderSource({ defines: ["LOG_DEPTH_WRITE"], sources: [PointCloudEyeDomeLightingShader], }); var blendUniformMap = { u_pointCloud_colorGBuffer: function () { return processor._colorGBuffer; }, u_pointCloud_depthGBuffer: function () { return processor._depthGBuffer; }, u_distanceAndEdlStrength: function () { distanceAndEdlStrengthScratch.x = processor._radius; distanceAndEdlStrengthScratch.y = processor._strength; return distanceAndEdlStrengthScratch; }, }; var blendRenderState = RenderState.fromCache({ blending: BlendingState$1.ALPHA_BLEND, depthMask: true, depthTest: { enabled: true, }, stencilTest: StencilConstants$1.setCesium3DTileBit(), stencilMask: StencilConstants$1.CESIUM_3D_TILE_MASK, }); processor._drawCommand = context.createViewportQuadCommand(blendFS, { uniformMap: blendUniformMap, renderState: blendRenderState, pass: Pass$1.CESIUM_3D_TILE, owner: processor, }); processor._clearCommand = new ClearCommand({ framebuffer: processor._framebuffer, color: new Color(0.0, 0.0, 0.0, 0.0), depth: 1.0, renderState: RenderState.fromCache(), pass: Pass$1.CESIUM_3D_TILE, owner: processor, }); } function createResources$3(processor, context) { var screenWidth = context.drawingBufferWidth; var screenHeight = context.drawingBufferHeight; var colorGBuffer = processor._colorGBuffer; var nowDirty = false; var resized = defined(colorGBuffer) && (colorGBuffer.width !== screenWidth || colorGBuffer.height !== screenHeight); if (!defined(colorGBuffer) || resized) { destroyFramebuffer$1(processor); createFramebuffer$3(processor, context); createCommands$1(processor, context); nowDirty = true; } return nowDirty; } function isSupported(context) { return context.drawBuffers && context.fragmentDepth; } PointCloudEyeDomeLighting.isSupported = isSupported; function getECShaderProgram(context, shaderProgram) { var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, "EC"); if (!defined(shader)) { var attributeLocations = shaderProgram._attributeLocations; var fs = shaderProgram.fragmentShaderSource.clone(); fs.sources = fs.sources.map(function (source) { source = ShaderSource.replaceMain( source, "czm_point_cloud_post_process_main" ); source = source.replace(/gl_FragColor/g, "gl_FragData[0]"); return source; }); fs.sources.unshift("#extension GL_EXT_draw_buffers : enable \n"); fs.sources.push( "void main() \n" + "{ \n" + " czm_point_cloud_post_process_main(); \n" + "#ifdef LOG_DEPTH\n" + " czm_writeLogDepth();\n" + " gl_FragData[1] = czm_packDepth(gl_FragDepthEXT); \n" + "#else\n" + " gl_FragData[1] = czm_packDepth(gl_FragCoord.z);\n" + "#endif\n" + "}" ); shader = context.shaderCache.createDerivedShaderProgram( shaderProgram, "EC", { vertexShaderSource: shaderProgram.vertexShaderSource, fragmentShaderSource: fs, attributeLocations: attributeLocations, } ); } return shader; } PointCloudEyeDomeLighting.prototype.update = function ( frameState, commandStart, pointCloudShading, boundingVolume ) { if (!isSupported(frameState.context)) { return; } this._strength = pointCloudShading.eyeDomeLightingStrength; this._radius = pointCloudShading.eyeDomeLightingRadius * frameState.pixelRatio; var dirty = createResources$3(this, frameState.context); // Hijack existing point commands to render into an offscreen FBO. var i; var commandList = frameState.commandList; var commandEnd = commandList.length; for (i = commandStart; i < commandEnd; ++i) { var command = commandList[i]; if ( command.primitiveType !== PrimitiveType$1.POINTS || command.pass === Pass$1.TRANSLUCENT ) { continue; } var derivedCommand = command.derivedCommands.pointCloudProcessor; if ( !defined(derivedCommand) || command.dirty || dirty || derivedCommand.framebuffer !== this._framebuffer ) { // Prevent crash when tiles out-of-view come in-view during context size change derivedCommand = DrawCommand.shallowClone(command); command.derivedCommands.pointCloudProcessor = derivedCommand; derivedCommand.framebuffer = this._framebuffer; derivedCommand.shaderProgram = getECShaderProgram( frameState.context, command.shaderProgram ); derivedCommand.castShadows = false; derivedCommand.receiveShadows = false; } commandList[i] = derivedCommand; } var clearCommand = this._clearCommand; var blendCommand = this._drawCommand; blendCommand.boundingVolume = boundingVolume; // Blend EDL into the main FBO commandList.push(blendCommand); commandList.push(clearCommand); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see PointCloudEyeDomeLighting#destroy */ PointCloudEyeDomeLighting.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * processor = processor && processor.destroy(); * * @see PointCloudEyeDomeLighting#isDestroyed */ PointCloudEyeDomeLighting.prototype.destroy = function () { destroyFramebuffer$1(this); return destroyObject(this); }; /** * Options for performing point attenuation based on geometric error when rendering * point clouds using 3D Tiles. * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.attenuation=false] Perform point attenuation based on geometric error. * @param {Number} [options.geometricErrorScale=1.0] Scale to be applied to each tile's geometric error. * @param {Number} [options.maximumAttenuation] Maximum attenuation in pixels. Defaults to the Cesium3DTileset's maximumScreenSpaceError. * @param {Number} [options.baseResolution] Average base resolution for the dataset in meters. Substitute for Geometric Error when not available. * @param {Boolean} [options.eyeDomeLighting=true] When true, use eye dome lighting when drawing with point attenuation. * @param {Number} [options.eyeDomeLightingStrength=1.0] Increasing this value increases contrast on slopes and edges. * @param {Number} [options.eyeDomeLightingRadius=1.0] Increase the thickness of contours from eye dome lighting. * @param {Boolean} [options.backFaceCulling=false] Determines whether back-facing points are hidden. This option works only if data has normals included. * @param {Boolean} [options.normalShading=true] Determines whether a point cloud that contains normals is shaded by the scene's light source. * * @alias PointCloudShading * @constructor */ function PointCloudShading(options) { var pointCloudShading = defaultValue(options, {}); /** * Perform point attenuation based on geometric error. * @type {Boolean} * @default false */ this.attenuation = defaultValue(pointCloudShading.attenuation, false); /** * Scale to be applied to the geometric error before computing attenuation. * @type {Number} * @default 1.0 */ this.geometricErrorScale = defaultValue( pointCloudShading.geometricErrorScale, 1.0 ); /** * Maximum point attenuation in pixels. If undefined, the Cesium3DTileset's maximumScreenSpaceError will be used. * @type {Number} */ this.maximumAttenuation = pointCloudShading.maximumAttenuation; /** * Average base resolution for the dataset in meters. * Used in place of geometric error when geometric error is 0. * If undefined, an approximation will be computed for each tile that has geometric error of 0. * @type {Number} */ this.baseResolution = pointCloudShading.baseResolution; /** * Use eye dome lighting when drawing with point attenuation * Requires support for EXT_frag_depth, OES_texture_float, and WEBGL_draw_buffers extensions in WebGL 1.0, * otherwise eye dome lighting is ignored. * * @type {Boolean} * @default true */ this.eyeDomeLighting = defaultValue(pointCloudShading.eyeDomeLighting, true); /** * Eye dome lighting strength (apparent contrast) * @type {Number} * @default 1.0 */ this.eyeDomeLightingStrength = defaultValue( pointCloudShading.eyeDomeLightingStrength, 1.0 ); /** * Thickness of contours from eye dome lighting * @type {Number} * @default 1.0 */ this.eyeDomeLightingRadius = defaultValue( pointCloudShading.eyeDomeLightingRadius, 1.0 ); /** * Determines whether back-facing points are hidden. * This option works only if data has normals included. * * @type {Boolean} * @default false */ this.backFaceCulling = defaultValue(pointCloudShading.backFaceCulling, false); /** * Determines whether a point cloud that contains normals is shaded by the scene's light source. * * @type {Boolean} * @default true */ this.normalShading = defaultValue(pointCloudShading.normalShading, true); } /** * Determines if point cloud shading is supported. * * @param {Scene} scene The scene. * @returns {Boolean} true if point cloud shading is supported; otherwise, returns false */ PointCloudShading.isSupported = function (scene) { return PointCloudEyeDomeLighting.isSupported(scene.context); }; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/PointCloud|Point Cloud} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias PointCloud3DTileContent * @constructor * * @private */ function PointCloud3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._pickId = undefined; // Only defined when batchTable is undefined this._batchTable = undefined; // Used when feature table contains BATCH_ID semantic this._styleDirty = false; this._features = undefined; this.featurePropertiesDirty = false; this._pointCloud = new PointCloud({ arrayBuffer: arrayBuffer, byteOffset: byteOffset, cull: false, opaquePass: Pass$1.CESIUM_3D_TILE, vertexShaderLoaded: getVertexShaderLoaded(this), fragmentShaderLoaded: getFragmentShaderLoaded$1(this), uniformMapLoaded: getUniformMapLoaded$1(this), batchTableLoaded: getBatchTableLoaded(this), pickIdLoaded: getPickIdLoaded$1(this), }); } Object.defineProperties(PointCloud3DTileContent.prototype, { featuresLength: { get: function () { if (defined(this._batchTable)) { return this._batchTable.featuresLength; } return 0; }, }, pointsLength: { get: function () { return this._pointCloud.pointsLength; }, }, trianglesLength: { get: function () { return 0; }, }, geometryByteLength: { get: function () { return this._pointCloud.geometryByteLength; }, }, texturesByteLength: { get: function () { return 0; }, }, batchTableByteLength: { get: function () { if (defined(this._batchTable)) { return this._batchTable.memorySizeInBytes; } return 0; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._pointCloud.readyPromise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return this._batchTable; }, }, }); function getVertexShaderLoaded(content) { return function (vs) { if (defined(content._batchTable)) { return content._batchTable.getVertexShaderCallback( false, "a_batchId", undefined )(vs); } return vs; }; } function getFragmentShaderLoaded$1(content) { return function (fs) { if (defined(content._batchTable)) { return content._batchTable.getFragmentShaderCallback( false, undefined )(fs); } return "uniform vec4 czm_pickColor;\n" + fs; }; } function getUniformMapLoaded$1(content) { return function (uniformMap) { if (defined(content._batchTable)) { return content._batchTable.getUniformMapCallback()(uniformMap); } return combine$2(uniformMap, { czm_pickColor: function () { return content._pickId.color; }, }); }; } function getBatchTableLoaded(content) { return function (batchLength, batchTableJson, batchTableBinary) { content._batchTable = new Cesium3DTileBatchTable( content, batchLength, batchTableJson, batchTableBinary ); }; } function getPickIdLoaded$1(content) { return function () { return defined(content._batchTable) ? content._batchTable.getPickId() : "czm_pickColor"; }; } function getGeometricError$1(content) { var pointCloudShading = content._tileset.pointCloudShading; var sphereVolume = content._tile.contentBoundingVolume.boundingSphere.volume(); var baseResolutionApproximation = CesiumMath.cbrt( sphereVolume / content.pointsLength ); var geometricError = content._tile.geometricError; if (geometricError === 0) { if ( defined(pointCloudShading) && defined(pointCloudShading.baseResolution) ) { geometricError = pointCloudShading.baseResolution; } else { geometricError = baseResolutionApproximation; } } return geometricError; } function createFeatures$1(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && featuresLength > 0) { var features = new Array(featuresLength); for (var i = 0; i < featuresLength; ++i) { features[i] = new Cesium3DTileFeature(content, i); } content._features = features; } } PointCloud3DTileContent.prototype.hasProperty = function (batchId, name) { if (defined(this._batchTable)) { return this._batchTable.hasProperty(batchId, name); } return false; }; /** * Part of the {@link Cesium3DTileContent} interface. * * In this context a feature refers to a group of points that share the same BATCH_ID. * For example all the points that represent a door in a house point cloud would be a feature. * * Features are backed by a batch table and can be colored, shown/hidden, picked, etc like features * in b3dm and i3dm. * * When the BATCH_ID semantic is omitted and the point cloud stores per-point properties, they * are not accessible by getFeature. They are only used for dynamic styling. */ PointCloud3DTileContent.prototype.getFeature = function (batchId) { if (!defined(this._batchTable)) { return undefined; } var featuresLength = this.featuresLength; //>>includeStart('debug', pragmas.debug); if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + (featuresLength - 1) + ")." ); } //>>includeEnd('debug'); createFeatures$1(this); return this._features[batchId]; }; PointCloud3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) { this._pointCloud.color = enabled ? color : Color.WHITE; }; PointCloud3DTileContent.prototype.applyStyle = function (style) { if (defined(this._batchTable)) { this._batchTable.applyStyle(style); } else { this._styleDirty = true; } }; var defaultShading$1 = new PointCloudShading(); PointCloud3DTileContent.prototype.update = function (tileset, frameState) { var pointCloud = this._pointCloud; var pointCloudShading = defaultValue( tileset.pointCloudShading, defaultShading$1 ); var tile = this._tile; var batchTable = this._batchTable; var mode = frameState.mode; var clippingPlanes = tileset.clippingPlanes; if (!defined(this._pickId) && !defined(batchTable)) { this._pickId = frameState.context.createPickId({ primitive: tileset, content: this, }); } if (defined(batchTable)) { batchTable.update(tileset, frameState); } var boundingSphere; if (defined(tile._contentBoundingVolume)) { boundingSphere = mode === SceneMode$1.SCENE3D ? tile._contentBoundingVolume.boundingSphere : tile._contentBoundingVolume2D.boundingSphere; } else { boundingSphere = mode === SceneMode$1.SCENE3D ? tile._boundingVolume.boundingSphere : tile._boundingVolume2D.boundingSphere; } var styleDirty = this._styleDirty; this._styleDirty = false; pointCloud.clippingPlanesOriginMatrix = tileset.clippingPlanesOriginMatrix; pointCloud.style = defined(batchTable) ? undefined : tileset.style; pointCloud.styleDirty = styleDirty; pointCloud.modelMatrix = tile.computedTransform; pointCloud.time = tileset.timeSinceLoad; pointCloud.shadows = tileset.shadows; pointCloud.boundingSphere = boundingSphere; pointCloud.clippingPlanes = clippingPlanes; pointCloud.isClipped = defined(clippingPlanes) && clippingPlanes.enabled && tile._isClipped; pointCloud.clippingPlanesDirty = tile.clippingPlanesDirty; pointCloud.attenuation = pointCloudShading.attenuation; pointCloud.backFaceCulling = pointCloudShading.backFaceCulling; pointCloud.normalShading = pointCloudShading.normalShading; pointCloud.geometricError = getGeometricError$1(this); pointCloud.geometricErrorScale = pointCloudShading.geometricErrorScale; if ( defined(pointCloudShading) && defined(pointCloudShading.maximumAttenuation) ) { pointCloud.maximumAttenuation = pointCloudShading.maximumAttenuation; } else if (tile.refine === Cesium3DTileRefine$1.ADD) { pointCloud.maximumAttenuation = 5.0; } else { pointCloud.maximumAttenuation = tileset.maximumScreenSpaceError; } pointCloud.update(frameState); }; PointCloud3DTileContent.prototype.isDestroyed = function () { return false; }; PointCloud3DTileContent.prototype.destroy = function () { this._pickId = this._pickId && this._pickId.destroy(); this._pointCloud = this._pointCloud && this._pointCloud.destroy(); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; /** * Represents content for a tile in a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset whose * content points to another 3D Tiles tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Tileset3DTileContent * @constructor * * @private */ function Tileset3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._readyPromise = when.defer(); this.featurePropertiesDirty = false; initialize$2(this, arrayBuffer, byteOffset); } Object.defineProperties(Tileset3DTileContent.prototype, { featuresLength: { get: function () { return 0; }, }, pointsLength: { get: function () { return 0; }, }, trianglesLength: { get: function () { return 0; }, }, geometryByteLength: { get: function () { return 0; }, }, texturesByteLength: { get: function () { return 0; }, }, batchTableByteLength: { get: function () { return 0; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return undefined; }, }, }); function initialize$2(content, arrayBuffer, byteOffset) { byteOffset = defaultValue(byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var tilesetJson; try { tilesetJson = getJsonFromTypedArray(uint8Array, byteOffset); } catch (error) { content._readyPromise.reject(new RuntimeError("Invalid tile content.")); return; } content._tileset.loadTileset(content._resource, tilesetJson, content._tile); content._readyPromise.resolve(content); } /** * Part of the {@link Cesium3DTileContent} interface. Tileset3DTileContent * always returns false since a tile of this type does not have any features. */ Tileset3DTileContent.prototype.hasProperty = function (batchId, name) { return false; }; /** * Part of the {@link Cesium3DTileContent} interface. Tileset3DTileContent * always returns undefined since a tile of this type does not have any features. */ Tileset3DTileContent.prototype.getFeature = function (batchId) { return undefined; }; Tileset3DTileContent.prototype.applyDebugSettings = function ( enabled, color ) {}; Tileset3DTileContent.prototype.applyStyle = function (style) {}; Tileset3DTileContent.prototype.update = function (tileset, frameState) {}; Tileset3DTileContent.prototype.isDestroyed = function () { return false; }; Tileset3DTileContent.prototype.destroy = function () { return destroyObject(this); }; /** * @private */ function VertexArrayFacade(context, attributes, sizeInVertices, instanced) { //>>includeStart('debug', pragmas.debug); Check.defined("context", context); if (!attributes || attributes.length === 0) { throw new DeveloperError("At least one attribute is required."); } //>>includeEnd('debug'); var attrs = VertexArrayFacade._verifyAttributes(attributes); sizeInVertices = defaultValue(sizeInVertices, 0); var precreatedAttributes = []; var attributesByUsage = {}; var attributesForUsage; var usage; // Bucket the attributes by usage. var length = attrs.length; for (var i = 0; i < length; ++i) { var attribute = attrs[i]; // If the attribute already has a vertex buffer, we do not need // to manage a vertex buffer or typed array for it. if (attribute.vertexBuffer) { precreatedAttributes.push(attribute); continue; } usage = attribute.usage; attributesForUsage = attributesByUsage[usage]; if (!defined(attributesForUsage)) { attributesForUsage = attributesByUsage[usage] = []; } attributesForUsage.push(attribute); } // A function to sort attributes by the size of their components. From left to right, a vertex // stores floats, shorts, and then bytes. function compare(left, right) { return ( ComponentDatatype$1.getSizeInBytes(right.componentDatatype) - ComponentDatatype$1.getSizeInBytes(left.componentDatatype) ); } this._allBuffers = []; for (usage in attributesByUsage) { if (attributesByUsage.hasOwnProperty(usage)) { attributesForUsage = attributesByUsage[usage]; attributesForUsage.sort(compare); var vertexSizeInBytes = VertexArrayFacade._vertexSizeInBytes( attributesForUsage ); var bufferUsage = attributesForUsage[0].usage; var buffer = { vertexSizeInBytes: vertexSizeInBytes, vertexBuffer: undefined, usage: bufferUsage, needsCommit: false, arrayBuffer: undefined, arrayViews: VertexArrayFacade._createArrayViews( attributesForUsage, vertexSizeInBytes ), }; this._allBuffers.push(buffer); } } this._size = 0; this._instanced = defaultValue(instanced, false); this._precreated = precreatedAttributes; this._context = context; this.writers = undefined; this.va = undefined; this.resize(sizeInVertices); } VertexArrayFacade._verifyAttributes = function (attributes) { var attrs = []; for (var i = 0; i < attributes.length; ++i) { var attribute = attributes[i]; var attr = { index: defaultValue(attribute.index, i), enabled: defaultValue(attribute.enabled, true), componentsPerAttribute: attribute.componentsPerAttribute, componentDatatype: defaultValue( attribute.componentDatatype, ComponentDatatype$1.FLOAT ), normalize: defaultValue(attribute.normalize, false), // There will be either a vertexBuffer or an [optional] usage. vertexBuffer: attribute.vertexBuffer, usage: defaultValue(attribute.usage, BufferUsage$1.STATIC_DRAW), }; attrs.push(attr); //>>includeStart('debug', pragmas.debug); if ( attr.componentsPerAttribute !== 1 && attr.componentsPerAttribute !== 2 && attr.componentsPerAttribute !== 3 && attr.componentsPerAttribute !== 4 ) { throw new DeveloperError( "attribute.componentsPerAttribute must be in the range [1, 4]." ); } var datatype = attr.componentDatatype; if (!ComponentDatatype$1.validate(datatype)) { throw new DeveloperError( "Attribute must have a valid componentDatatype or not specify it." ); } if (!BufferUsage$1.validate(attr.usage)) { throw new DeveloperError( "Attribute must have a valid usage or not specify it." ); } //>>includeEnd('debug'); } // Verify all attribute names are unique. var uniqueIndices = new Array(attrs.length); for (var j = 0; j < attrs.length; ++j) { var currentAttr = attrs[j]; var index = currentAttr.index; //>>includeStart('debug', pragmas.debug); if (uniqueIndices[index]) { throw new DeveloperError( "Index " + index + " is used by more than one attribute." ); } //>>includeEnd('debug'); uniqueIndices[index] = true; } return attrs; }; VertexArrayFacade._vertexSizeInBytes = function (attributes) { var sizeInBytes = 0; var length = attributes.length; for (var i = 0; i < length; ++i) { var attribute = attributes[i]; sizeInBytes += attribute.componentsPerAttribute * ComponentDatatype$1.getSizeInBytes(attribute.componentDatatype); } var maxComponentSizeInBytes = length > 0 ? ComponentDatatype$1.getSizeInBytes(attributes[0].componentDatatype) : 0; // Sorted by size var remainder = maxComponentSizeInBytes > 0 ? sizeInBytes % maxComponentSizeInBytes : 0; var padding = remainder === 0 ? 0 : maxComponentSizeInBytes - remainder; sizeInBytes += padding; return sizeInBytes; }; VertexArrayFacade._createArrayViews = function (attributes, vertexSizeInBytes) { var views = []; var offsetInBytes = 0; var length = attributes.length; for (var i = 0; i < length; ++i) { var attribute = attributes[i]; var componentDatatype = attribute.componentDatatype; views.push({ index: attribute.index, enabled: attribute.enabled, componentsPerAttribute: attribute.componentsPerAttribute, componentDatatype: componentDatatype, normalize: attribute.normalize, offsetInBytes: offsetInBytes, vertexSizeInComponentType: vertexSizeInBytes / ComponentDatatype$1.getSizeInBytes(componentDatatype), view: undefined, }); offsetInBytes += attribute.componentsPerAttribute * ComponentDatatype$1.getSizeInBytes(componentDatatype); } return views; }; /** * Invalidates writers. Can't render again until commit is called. */ VertexArrayFacade.prototype.resize = function (sizeInVertices) { this._size = sizeInVertices; var allBuffers = this._allBuffers; this.writers = []; for (var i = 0, len = allBuffers.length; i < len; ++i) { var buffer = allBuffers[i]; VertexArrayFacade._resize(buffer, this._size); // Reserving invalidates the writers, so if client's cache them, they need to invalidate their cache. VertexArrayFacade._appendWriters(this.writers, buffer); } // VAs are recreated next time commit is called. destroyVA(this); }; VertexArrayFacade._resize = function (buffer, size) { if (buffer.vertexSizeInBytes > 0) { // Create larger array buffer var arrayBuffer = new ArrayBuffer(size * buffer.vertexSizeInBytes); // Copy contents from previous array buffer if (defined(buffer.arrayBuffer)) { var destView = new Uint8Array(arrayBuffer); var sourceView = new Uint8Array(buffer.arrayBuffer); var sourceLength = sourceView.length; for (var j = 0; j < sourceLength; ++j) { destView[j] = sourceView[j]; } } // Create typed views into the new array buffer var views = buffer.arrayViews; var length = views.length; for (var i = 0; i < length; ++i) { var view = views[i]; view.view = ComponentDatatype$1.createArrayBufferView( view.componentDatatype, arrayBuffer, view.offsetInBytes ); } buffer.arrayBuffer = arrayBuffer; } }; var createWriters = [ // 1 component per attribute function (buffer, view, vertexSizeInComponentType) { return function (index, attribute) { view[index * vertexSizeInComponentType] = attribute; buffer.needsCommit = true; }; }, // 2 component per attribute function (buffer, view, vertexSizeInComponentType) { return function (index, component0, component1) { var i = index * vertexSizeInComponentType; view[i] = component0; view[i + 1] = component1; buffer.needsCommit = true; }; }, // 3 component per attribute function (buffer, view, vertexSizeInComponentType) { return function (index, component0, component1, component2) { var i = index * vertexSizeInComponentType; view[i] = component0; view[i + 1] = component1; view[i + 2] = component2; buffer.needsCommit = true; }; }, // 4 component per attribute function (buffer, view, vertexSizeInComponentType) { return function (index, component0, component1, component2, component3) { var i = index * vertexSizeInComponentType; view[i] = component0; view[i + 1] = component1; view[i + 2] = component2; view[i + 3] = component3; buffer.needsCommit = true; }; }, ]; VertexArrayFacade._appendWriters = function (writers, buffer) { var arrayViews = buffer.arrayViews; var length = arrayViews.length; for (var i = 0; i < length; ++i) { var arrayView = arrayViews[i]; writers[arrayView.index] = createWriters[ arrayView.componentsPerAttribute - 1 ](buffer, arrayView.view, arrayView.vertexSizeInComponentType); } }; VertexArrayFacade.prototype.commit = function (indexBuffer) { var recreateVA = false; var allBuffers = this._allBuffers; var buffer; var i; var length; for (i = 0, length = allBuffers.length; i < length; ++i) { buffer = allBuffers[i]; recreateVA = commit(this, buffer) || recreateVA; } /////////////////////////////////////////////////////////////////////// if (recreateVA || !defined(this.va)) { destroyVA(this); var va = (this.va = []); var chunkSize = CesiumMath.SIXTY_FOUR_KILOBYTES - 4; // The 65535 index is reserved for primitive restart. Reserve the last 4 indices so that billboard quads are not broken up. var numberOfVertexArrays = defined(indexBuffer) && !this._instanced ? Math.ceil(this._size / chunkSize) : 1; for (var k = 0; k < numberOfVertexArrays; ++k) { var attributes = []; for (i = 0, length = allBuffers.length; i < length; ++i) { buffer = allBuffers[i]; var offset = k * (buffer.vertexSizeInBytes * chunkSize); VertexArrayFacade._appendAttributes( attributes, buffer, offset, this._instanced ); } attributes = attributes.concat(this._precreated); va.push({ va: new VertexArray({ context: this._context, attributes: attributes, indexBuffer: indexBuffer, }), indicesCount: 1.5 * (k !== numberOfVertexArrays - 1 ? chunkSize : this._size % chunkSize), // TODO: not hardcode 1.5, this assumes 6 indices per 4 vertices (as for Billboard quads). }); } } }; function commit(vertexArrayFacade, buffer) { if (buffer.needsCommit && buffer.vertexSizeInBytes > 0) { buffer.needsCommit = false; var vertexBuffer = buffer.vertexBuffer; var vertexBufferSizeInBytes = vertexArrayFacade._size * buffer.vertexSizeInBytes; var vertexBufferDefined = defined(vertexBuffer); if ( !vertexBufferDefined || vertexBuffer.sizeInBytes < vertexBufferSizeInBytes ) { if (vertexBufferDefined) { vertexBuffer.destroy(); } buffer.vertexBuffer = Buffer$1.createVertexBuffer({ context: vertexArrayFacade._context, typedArray: buffer.arrayBuffer, usage: buffer.usage, }); buffer.vertexBuffer.vertexArrayDestroyable = false; return true; // Created new vertex buffer } buffer.vertexBuffer.copyFromArrayView(buffer.arrayBuffer); } return false; // Did not create new vertex buffer } VertexArrayFacade._appendAttributes = function ( attributes, buffer, vertexBufferOffset, instanced ) { var arrayViews = buffer.arrayViews; var length = arrayViews.length; for (var i = 0; i < length; ++i) { var view = arrayViews[i]; attributes.push({ index: view.index, enabled: view.enabled, componentsPerAttribute: view.componentsPerAttribute, componentDatatype: view.componentDatatype, normalize: view.normalize, vertexBuffer: buffer.vertexBuffer, offsetInBytes: vertexBufferOffset + view.offsetInBytes, strideInBytes: buffer.vertexSizeInBytes, instanceDivisor: instanced ? 1 : 0, }); } }; VertexArrayFacade.prototype.subCommit = function ( offsetInVertices, lengthInVertices ) { //>>includeStart('debug', pragmas.debug); if (offsetInVertices < 0 || offsetInVertices >= this._size) { throw new DeveloperError( "offsetInVertices must be greater than or equal to zero and less than the vertex array size." ); } if (offsetInVertices + lengthInVertices > this._size) { throw new DeveloperError( "offsetInVertices + lengthInVertices cannot exceed the vertex array size." ); } //>>includeEnd('debug'); var allBuffers = this._allBuffers; for (var i = 0, len = allBuffers.length; i < len; ++i) { subCommit(allBuffers[i], offsetInVertices, lengthInVertices); } }; function subCommit(buffer, offsetInVertices, lengthInVertices) { if (buffer.needsCommit && buffer.vertexSizeInBytes > 0) { var byteOffset = buffer.vertexSizeInBytes * offsetInVertices; var byteLength = buffer.vertexSizeInBytes * lengthInVertices; // PERFORMANCE_IDEA: If we want to get really crazy, we could consider updating // individual attributes instead of the entire (sub-)vertex. // // PERFORMANCE_IDEA: Does creating the typed view add too much GC overhead? buffer.vertexBuffer.copyFromArrayView( new Uint8Array(buffer.arrayBuffer, byteOffset, byteLength), byteOffset ); } } VertexArrayFacade.prototype.endSubCommits = function () { var allBuffers = this._allBuffers; for (var i = 0, len = allBuffers.length; i < len; ++i) { allBuffers[i].needsCommit = false; } }; function destroyVA(vertexArrayFacade) { var va = vertexArrayFacade.va; if (!defined(va)) { return; } var length = va.length; for (var i = 0; i < length; ++i) { va[i].va.destroy(); } vertexArrayFacade.va = undefined; } VertexArrayFacade.prototype.isDestroyed = function () { return false; }; VertexArrayFacade.prototype.destroy = function () { var allBuffers = this._allBuffers; for (var i = 0, len = allBuffers.length; i < len; ++i) { var buffer = allBuffers[i]; buffer.vertexBuffer = buffer.vertexBuffer && buffer.vertexBuffer.destroy(); } destroyVA(this); return destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var BillboardCollectionFS = "#ifdef GL_OES_standard_derivatives\n\ #extension GL_OES_standard_derivatives : enable\n\ #endif\n\ \n\ uniform sampler2D u_atlas;\n\ \n\ #ifdef VECTOR_TILE\n\ uniform vec4 u_highlightColor;\n\ #endif\n\ \n\ varying vec2 v_textureCoordinates;\n\ varying vec4 v_pickColor;\n\ varying vec4 v_color;\n\ \n\ #ifdef SDF\n\ varying vec4 v_outlineColor;\n\ varying float v_outlineWidth;\n\ #endif\n\ \n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ varying vec4 v_textureCoordinateBounds; // the min and max x and y values for the texture coordinates\n\ varying vec4 v_originTextureCoordinateAndTranslate; // texture coordinate at the origin, billboard translate (used for label glyphs)\n\ varying vec4 v_compressed; // x: eyeDepth, y: applyTranslate & enableDepthCheck, z: dimensions, w: imageSize\n\ varying mat2 v_rotationMatrix;\n\ \n\ const float SHIFT_LEFT12 = 4096.0;\n\ const float SHIFT_LEFT1 = 2.0;\n\ \n\ const float SHIFT_RIGHT12 = 1.0 / 4096.0;\n\ const float SHIFT_RIGHT1 = 1.0 / 2.0;\n\ \n\ float getGlobeDepth(vec2 adjustedST, vec2 depthLookupST, bool applyTranslate, vec2 dimensions, vec2 imageSize)\n\ {\n\ vec2 lookupVector = imageSize * (depthLookupST - adjustedST);\n\ lookupVector = v_rotationMatrix * lookupVector;\n\ vec2 labelOffset = (dimensions - imageSize) * (depthLookupST - vec2(0.0, v_originTextureCoordinateAndTranslate.y)); // aligns label glyph with bounding rectangle. Will be zero for billboards because dimensions and imageSize will be equal\n\ \n\ vec2 translation = v_originTextureCoordinateAndTranslate.zw;\n\ \n\ if (applyTranslate)\n\ {\n\ // this is only needed for labels where the horizontal origin is not LEFT\n\ // it moves the label back to where the \"origin\" should be since all label glyphs are set to HorizontalOrigin.LEFT\n\ translation += (dimensions * v_originTextureCoordinateAndTranslate.xy * vec2(1.0, 0.0));\n\ }\n\ \n\ vec2 st = ((lookupVector - translation + labelOffset) + gl_FragCoord.xy) / czm_viewport.zw;\n\ float logDepthOrDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, st));\n\ \n\ if (logDepthOrDepth == 0.0)\n\ {\n\ return 0.0; // not on the globe\n\ }\n\ \n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, logDepthOrDepth);\n\ return eyeCoordinate.z / eyeCoordinate.w;\n\ }\n\ #endif\n\ \n\ \n\ #ifdef SDF\n\ \n\ // Get the distance from the edge of a glyph at a given position sampling an SDF texture.\n\ float getDistance(vec2 position)\n\ {\n\ return texture2D(u_atlas, position).r;\n\ }\n\ \n\ // Samples the sdf texture at the given position and produces a color based on the fill color and the outline.\n\ vec4 getSDFColor(vec2 position, float outlineWidth, vec4 outlineColor, float smoothing)\n\ {\n\ float distance = getDistance(position);\n\ \n\ if (outlineWidth > 0.0)\n\ {\n\ // Don't get the outline edge exceed the SDF_EDGE\n\ float outlineEdge = clamp(SDF_EDGE - outlineWidth, 0.0, SDF_EDGE);\n\ float outlineFactor = smoothstep(SDF_EDGE - smoothing, SDF_EDGE + smoothing, distance);\n\ vec4 sdfColor = mix(outlineColor, v_color, outlineFactor);\n\ float alpha = smoothstep(outlineEdge - smoothing, outlineEdge + smoothing, distance);\n\ return vec4(sdfColor.rgb, sdfColor.a * alpha);\n\ }\n\ else\n\ {\n\ float alpha = smoothstep(SDF_EDGE - smoothing, SDF_EDGE + smoothing, distance);\n\ return vec4(v_color.rgb, v_color.a * alpha);\n\ }\n\ }\n\ #endif\n\ \n\ void main()\n\ {\n\ vec4 color = texture2D(u_atlas, v_textureCoordinates);\n\ \n\ #ifdef SDF\n\ float outlineWidth = v_outlineWidth;\n\ vec4 outlineColor = v_outlineColor;\n\ \n\ // Get the current distance\n\ float distance = getDistance(v_textureCoordinates);\n\ \n\ #ifdef GL_OES_standard_derivatives\n\ float smoothing = fwidth(distance);\n\ // Get an offset that is approximately half the distance to the neighbor pixels\n\ // 0.354 is approximately half of 1/sqrt(2)\n\ vec2 sampleOffset = 0.354 * vec2(dFdx(v_textureCoordinates) + dFdy(v_textureCoordinates));\n\ \n\ // Sample the center point\n\ vec4 center = getSDFColor(v_textureCoordinates, outlineWidth, outlineColor, smoothing);\n\ \n\ // Sample the 4 neighbors\n\ vec4 color1 = getSDFColor(v_textureCoordinates + vec2(sampleOffset.x, sampleOffset.y), outlineWidth, outlineColor, smoothing);\n\ vec4 color2 = getSDFColor(v_textureCoordinates + vec2(-sampleOffset.x, sampleOffset.y), outlineWidth, outlineColor, smoothing);\n\ vec4 color3 = getSDFColor(v_textureCoordinates + vec2(-sampleOffset.x, -sampleOffset.y), outlineWidth, outlineColor, smoothing);\n\ vec4 color4 = getSDFColor(v_textureCoordinates + vec2(sampleOffset.x, -sampleOffset.y), outlineWidth, outlineColor, smoothing);\n\ \n\ // Equally weight the center sample and the 4 neighboring samples\n\ color = (center + color1 + color2 + color3 + color4)/5.0;\n\ #else\n\ // Just do a single sample\n\ float smoothing = 1.0/32.0;\n\ color = getSDFColor(v_textureCoordinates, outlineWidth, outlineColor, smoothing);\n\ #endif\n\ \n\ color = czm_gammaCorrect(color);\n\ #else\n\ color = czm_gammaCorrect(color);\n\ color *= czm_gammaCorrect(v_color);\n\ #endif\n\ \n\ // Fully transparent parts of the billboard are not pickable.\n\ #if !defined(OPAQUE) && !defined(TRANSLUCENT)\n\ if (color.a < 0.005) // matches 0/255 and 1/255\n\ {\n\ discard;\n\ }\n\ #else\n\ // The billboard is rendered twice. The opaque pass discards translucent fragments\n\ // and the translucent pass discards opaque fragments.\n\ #ifdef OPAQUE\n\ if (color.a < 0.995) // matches < 254/255\n\ {\n\ discard;\n\ }\n\ #else\n\ if (color.a >= 0.995) // matches 254/255 and 255/255\n\ {\n\ discard;\n\ }\n\ #endif\n\ #endif\n\ \n\ #ifdef VECTOR_TILE\n\ color *= u_highlightColor;\n\ #endif\n\ gl_FragColor = color;\n\ \n\ #ifdef LOG_DEPTH\n\ czm_writeLogDepth();\n\ #endif\n\ \n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ float temp = v_compressed.y;\n\ \n\ temp = temp * SHIFT_RIGHT1;\n\ \n\ float temp2 = (temp - floor(temp)) * SHIFT_LEFT1;\n\ bool enableDepthTest = temp2 != 0.0;\n\ bool applyTranslate = floor(temp) != 0.0;\n\ \n\ if (enableDepthTest) {\n\ temp = v_compressed.z;\n\ temp = temp * SHIFT_RIGHT12;\n\ \n\ vec2 dimensions;\n\ dimensions.y = (temp - floor(temp)) * SHIFT_LEFT12;\n\ dimensions.x = floor(temp);\n\ \n\ temp = v_compressed.w;\n\ temp = temp * SHIFT_RIGHT12;\n\ \n\ vec2 imageSize;\n\ imageSize.y = (temp - floor(temp)) * SHIFT_LEFT12;\n\ imageSize.x = floor(temp);\n\ \n\ vec2 adjustedST = v_textureCoordinates - v_textureCoordinateBounds.xy;\n\ adjustedST = adjustedST / vec2(v_textureCoordinateBounds.z - v_textureCoordinateBounds.x, v_textureCoordinateBounds.w - v_textureCoordinateBounds.y);\n\ \n\ float epsilonEyeDepth = v_compressed.x + czm_epsilon1;\n\ float globeDepth1 = getGlobeDepth(adjustedST, v_originTextureCoordinateAndTranslate.xy, applyTranslate, dimensions, imageSize);\n\ \n\ // negative values go into the screen\n\ if (globeDepth1 != 0.0 && globeDepth1 > epsilonEyeDepth)\n\ {\n\ float globeDepth2 = getGlobeDepth(adjustedST, vec2(0.0, 1.0), applyTranslate, dimensions, imageSize); // top left corner\n\ if (globeDepth2 != 0.0 && globeDepth2 > epsilonEyeDepth)\n\ {\n\ float globeDepth3 = getGlobeDepth(adjustedST, vec2(1.0, 1.0), applyTranslate, dimensions, imageSize); // top right corner\n\ if (globeDepth3 != 0.0 && globeDepth3 > epsilonEyeDepth)\n\ {\n\ discard;\n\ }\n\ }\n\ }\n\ }\n\ #endif\n\ \n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var BillboardCollectionVS = "#ifdef INSTANCED\n\ attribute vec2 direction;\n\ #endif\n\ attribute vec4 positionHighAndScale;\n\ attribute vec4 positionLowAndRotation;\n\ attribute vec4 compressedAttribute0; // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates (texture offset)\n\ attribute vec4 compressedAttribute1; // aligned axis, translucency by distance, image width\n\ attribute vec4 compressedAttribute2; // label horizontal origin, image height, color, pick color, size in meters, valid aligned axis, 13 bits free\n\ attribute vec4 eyeOffset; // eye offset in meters, 4 bytes free (texture range)\n\ attribute vec4 scaleByDistance; // near, nearScale, far, farScale\n\ attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale\n\ attribute vec4 compressedAttribute3; // distance display condition near, far, disableDepthTestDistance, dimensions\n\ attribute vec2 sdf; // sdf outline color (rgb) and width (w)\n\ #if defined(VERTEX_DEPTH_CHECK) || defined(FRAGMENT_DEPTH_CHECK)\n\ attribute vec4 textureCoordinateBoundsOrLabelTranslate; // the min and max x and y values for the texture coordinates\n\ #endif\n\ #ifdef VECTOR_TILE\n\ attribute float a_batchId;\n\ #endif\n\ \n\ varying vec2 v_textureCoordinates;\n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ varying vec4 v_textureCoordinateBounds;\n\ varying vec4 v_originTextureCoordinateAndTranslate;\n\ varying vec4 v_compressed; // x: eyeDepth, y: applyTranslate & enableDepthCheck, z: dimensions, w: imageSize\n\ varying mat2 v_rotationMatrix;\n\ #endif\n\ \n\ varying vec4 v_pickColor;\n\ varying vec4 v_color;\n\ #ifdef SDF\n\ varying vec4 v_outlineColor;\n\ varying float v_outlineWidth;\n\ #endif\n\ \n\ const float UPPER_BOUND = 32768.0;\n\ \n\ const float SHIFT_LEFT16 = 65536.0;\n\ const float SHIFT_LEFT12 = 4096.0;\n\ const float SHIFT_LEFT8 = 256.0;\n\ const float SHIFT_LEFT7 = 128.0;\n\ const float SHIFT_LEFT5 = 32.0;\n\ const float SHIFT_LEFT3 = 8.0;\n\ const float SHIFT_LEFT2 = 4.0;\n\ const float SHIFT_LEFT1 = 2.0;\n\ \n\ const float SHIFT_RIGHT12 = 1.0 / 4096.0;\n\ const float SHIFT_RIGHT8 = 1.0 / 256.0;\n\ const float SHIFT_RIGHT7 = 1.0 / 128.0;\n\ const float SHIFT_RIGHT5 = 1.0 / 32.0;\n\ const float SHIFT_RIGHT3 = 1.0 / 8.0;\n\ const float SHIFT_RIGHT2 = 1.0 / 4.0;\n\ const float SHIFT_RIGHT1 = 1.0 / 2.0;\n\ \n\ vec4 addScreenSpaceOffset(vec4 positionEC, vec2 imageSize, float scale, vec2 direction, vec2 origin, vec2 translate, vec2 pixelOffset, vec3 alignedAxis, bool validAlignedAxis, float rotation, bool sizeInMeters, out mat2 rotationMatrix, out float mpp)\n\ {\n\ // Note the halfSize cannot be computed in JavaScript because it is sent via\n\ // compressed vertex attributes that coerce it to an integer.\n\ vec2 halfSize = imageSize * scale * 0.5;\n\ halfSize *= ((direction * 2.0) - 1.0);\n\ \n\ vec2 originTranslate = origin * abs(halfSize);\n\ \n\ #if defined(ROTATION) || defined(ALIGNED_AXIS)\n\ if (validAlignedAxis || rotation != 0.0)\n\ {\n\ float angle = rotation;\n\ if (validAlignedAxis)\n\ {\n\ vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0);\n\ angle += sign(-projectedAlignedAxis.x) * acos(sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) /\n\ (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y));\n\ }\n\ \n\ float cosTheta = cos(angle);\n\ float sinTheta = sin(angle);\n\ rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta);\n\ halfSize = rotationMatrix * halfSize;\n\ }\n\ else\n\ {\n\ rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0);\n\ }\n\ #endif\n\ \n\ mpp = czm_metersPerPixel(positionEC);\n\ positionEC.xy += (originTranslate + halfSize) * czm_branchFreeTernary(sizeInMeters, 1.0, mpp);\n\ positionEC.xy += (translate + pixelOffset) * mpp;\n\ \n\ return positionEC;\n\ }\n\ \n\ #ifdef VERTEX_DEPTH_CHECK\n\ float getGlobeDepth(vec4 positionEC)\n\ {\n\ vec4 posWC = czm_eyeToWindowCoordinates(positionEC);\n\ \n\ float globeDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, posWC.xy / czm_viewport.zw));\n\ \n\ if (globeDepth == 0.0)\n\ {\n\ return 0.0; // not on the globe\n\ }\n\ \n\ vec4 eyeCoordinate = czm_windowToEyeCoordinates(posWC.xy, globeDepth);\n\ return eyeCoordinate.z / eyeCoordinate.w;\n\ }\n\ #endif\n\ void main()\n\ {\n\ // Modifying this shader may also require modifications to Billboard._computeScreenSpacePosition\n\ \n\ // unpack attributes\n\ vec3 positionHigh = positionHighAndScale.xyz;\n\ vec3 positionLow = positionLowAndRotation.xyz;\n\ float scale = positionHighAndScale.w;\n\ \n\ #if defined(ROTATION) || defined(ALIGNED_AXIS)\n\ float rotation = positionLowAndRotation.w;\n\ #else\n\ float rotation = 0.0;\n\ #endif\n\ \n\ float compressed = compressedAttribute0.x;\n\ \n\ vec2 pixelOffset;\n\ pixelOffset.x = floor(compressed * SHIFT_RIGHT7);\n\ compressed -= pixelOffset.x * SHIFT_LEFT7;\n\ pixelOffset.x -= UPPER_BOUND;\n\ \n\ vec2 origin;\n\ origin.x = floor(compressed * SHIFT_RIGHT5);\n\ compressed -= origin.x * SHIFT_LEFT5;\n\ \n\ origin.y = floor(compressed * SHIFT_RIGHT3);\n\ compressed -= origin.y * SHIFT_LEFT3;\n\ \n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ vec2 depthOrigin = origin.xy;\n\ #endif\n\ origin -= vec2(1.0);\n\ \n\ float show = floor(compressed * SHIFT_RIGHT2);\n\ compressed -= show * SHIFT_LEFT2;\n\ \n\ #ifdef INSTANCED\n\ vec2 textureCoordinatesBottomLeft = czm_decompressTextureCoordinates(compressedAttribute0.w);\n\ vec2 textureCoordinatesRange = czm_decompressTextureCoordinates(eyeOffset.w);\n\ vec2 textureCoordinates = textureCoordinatesBottomLeft + direction * textureCoordinatesRange;\n\ #else\n\ vec2 direction;\n\ direction.x = floor(compressed * SHIFT_RIGHT1);\n\ direction.y = compressed - direction.x * SHIFT_LEFT1;\n\ \n\ vec2 textureCoordinates = czm_decompressTextureCoordinates(compressedAttribute0.w);\n\ #endif\n\ \n\ float temp = compressedAttribute0.y * SHIFT_RIGHT8;\n\ pixelOffset.y = -(floor(temp) - UPPER_BOUND);\n\ \n\ vec2 translate;\n\ translate.y = (temp - floor(temp)) * SHIFT_LEFT16;\n\ \n\ temp = compressedAttribute0.z * SHIFT_RIGHT8;\n\ translate.x = floor(temp) - UPPER_BOUND;\n\ \n\ translate.y += (temp - floor(temp)) * SHIFT_LEFT8;\n\ translate.y -= UPPER_BOUND;\n\ \n\ temp = compressedAttribute1.x * SHIFT_RIGHT8;\n\ float temp2 = floor(compressedAttribute2.w * SHIFT_RIGHT2);\n\ \n\ vec2 imageSize = vec2(floor(temp), temp2);\n\ \n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ float labelHorizontalOrigin = floor(compressedAttribute2.w - (temp2 * SHIFT_LEFT2));\n\ float applyTranslate = 0.0;\n\ if (labelHorizontalOrigin != 0.0) // is a billboard, so set apply translate to false\n\ {\n\ applyTranslate = 1.0;\n\ labelHorizontalOrigin -= 2.0;\n\ depthOrigin.x = labelHorizontalOrigin + 1.0;\n\ }\n\ \n\ depthOrigin = vec2(1.0) - (depthOrigin * 0.5);\n\ #endif\n\ \n\ #ifdef EYE_DISTANCE_TRANSLUCENCY\n\ vec4 translucencyByDistance;\n\ translucencyByDistance.x = compressedAttribute1.z;\n\ translucencyByDistance.z = compressedAttribute1.w;\n\ \n\ translucencyByDistance.y = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0;\n\ \n\ temp = compressedAttribute1.y * SHIFT_RIGHT8;\n\ translucencyByDistance.w = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0;\n\ #endif\n\ \n\ #if defined(VERTEX_DEPTH_CHECK) || defined(FRAGMENT_DEPTH_CHECK)\n\ temp = compressedAttribute3.w;\n\ temp = temp * SHIFT_RIGHT12;\n\ \n\ vec2 dimensions;\n\ dimensions.y = (temp - floor(temp)) * SHIFT_LEFT12;\n\ dimensions.x = floor(temp);\n\ #endif\n\ \n\ #ifdef ALIGNED_AXIS\n\ vec3 alignedAxis = czm_octDecode(floor(compressedAttribute1.y * SHIFT_RIGHT8));\n\ temp = compressedAttribute2.z * SHIFT_RIGHT5;\n\ bool validAlignedAxis = (temp - floor(temp)) * SHIFT_LEFT1 > 0.0;\n\ #else\n\ vec3 alignedAxis = vec3(0.0);\n\ bool validAlignedAxis = false;\n\ #endif\n\ \n\ vec4 pickColor;\n\ vec4 color;\n\ \n\ temp = compressedAttribute2.y;\n\ temp = temp * SHIFT_RIGHT8;\n\ pickColor.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ pickColor.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ pickColor.r = floor(temp);\n\ \n\ temp = compressedAttribute2.x;\n\ temp = temp * SHIFT_RIGHT8;\n\ color.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ color.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ color.r = floor(temp);\n\ \n\ temp = compressedAttribute2.z * SHIFT_RIGHT8;\n\ bool sizeInMeters = floor((temp - floor(temp)) * SHIFT_LEFT7) > 0.0;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ \n\ pickColor.a = (temp - floor(temp)) * SHIFT_LEFT8;\n\ pickColor /= 255.0;\n\ \n\ color.a = floor(temp);\n\ color /= 255.0;\n\ \n\ ///////////////////////////////////////////////////////////////////////////\n\ \n\ vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);\n\ vec4 positionEC = czm_modelViewRelativeToEye * p;\n\ \n\ #if defined(FRAGMENT_DEPTH_CHECK) || defined(VERTEX_DEPTH_CHECK)\n\ float eyeDepth = positionEC.z;\n\ #endif\n\ \n\ positionEC = czm_eyeOffset(positionEC, eyeOffset.xyz);\n\ positionEC.xyz *= show;\n\ \n\ ///////////////////////////////////////////////////////////////////////////\n\ \n\ #if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(EYE_DISTANCE_PIXEL_OFFSET) || defined(DISTANCE_DISPLAY_CONDITION) || defined(DISABLE_DEPTH_DISTANCE)\n\ float lengthSq;\n\ if (czm_sceneMode == czm_sceneMode2D)\n\ {\n\ // 2D camera distance is a special case\n\ // treat all billboards as flattened to the z=0.0 plane\n\ lengthSq = czm_eyeHeight2D.y;\n\ }\n\ else\n\ {\n\ lengthSq = dot(positionEC.xyz, positionEC.xyz);\n\ }\n\ #endif\n\ \n\ #ifdef EYE_DISTANCE_SCALING\n\ float distanceScale = czm_nearFarScalar(scaleByDistance, lengthSq);\n\ scale *= distanceScale;\n\ translate *= distanceScale;\n\ // push vertex behind near plane for clipping\n\ if (scale == 0.0)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ }\n\ #endif\n\ \n\ float translucency = 1.0;\n\ #ifdef EYE_DISTANCE_TRANSLUCENCY\n\ translucency = czm_nearFarScalar(translucencyByDistance, lengthSq);\n\ // push vertex behind near plane for clipping\n\ if (translucency == 0.0)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ }\n\ #endif\n\ \n\ #ifdef EYE_DISTANCE_PIXEL_OFFSET\n\ float pixelOffsetScale = czm_nearFarScalar(pixelOffsetScaleByDistance, lengthSq);\n\ pixelOffset *= pixelOffsetScale;\n\ #endif\n\ \n\ #ifdef DISTANCE_DISPLAY_CONDITION\n\ float nearSq = compressedAttribute3.x;\n\ float farSq = compressedAttribute3.y;\n\ if (lengthSq < nearSq || lengthSq > farSq)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ }\n\ #endif\n\ \n\ mat2 rotationMatrix;\n\ float mpp;\n\ \n\ #ifdef DISABLE_DEPTH_DISTANCE\n\ float disableDepthTestDistance = compressedAttribute3.z;\n\ #endif\n\ \n\ #ifdef VERTEX_DEPTH_CHECK\n\ if (lengthSq < disableDepthTestDistance) {\n\ float depthsilon = 10.0;\n\ \n\ vec2 labelTranslate = textureCoordinateBoundsOrLabelTranslate.xy;\n\ vec4 pEC1 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(0.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp);\n\ float globeDepth1 = getGlobeDepth(pEC1);\n\ \n\ if (globeDepth1 != 0.0 && pEC1.z + depthsilon < globeDepth1)\n\ {\n\ vec4 pEC2 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(0.0, 1.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp);\n\ float globeDepth2 = getGlobeDepth(pEC2);\n\ \n\ if (globeDepth2 != 0.0 && pEC2.z + depthsilon < globeDepth2)\n\ {\n\ vec4 pEC3 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(1.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp);\n\ float globeDepth3 = getGlobeDepth(pEC3);\n\ if (globeDepth3 != 0.0 && pEC3.z + depthsilon < globeDepth3)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ }\n\ }\n\ }\n\ }\n\ #endif\n\ \n\ positionEC = addScreenSpaceOffset(positionEC, imageSize, scale, direction, origin, translate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp);\n\ gl_Position = czm_projection * positionEC;\n\ v_textureCoordinates = textureCoordinates;\n\ \n\ #ifdef LOG_DEPTH\n\ czm_vertexLogDepth();\n\ #endif\n\ \n\ #ifdef DISABLE_DEPTH_DISTANCE\n\ if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0)\n\ {\n\ disableDepthTestDistance = czm_minimumDisableDepthTestDistance;\n\ }\n\ \n\ if (disableDepthTestDistance != 0.0)\n\ {\n\ // Don't try to \"multiply both sides\" by w. Greater/less-than comparisons won't work for negative values of w.\n\ float zclip = gl_Position.z / gl_Position.w;\n\ bool clipped = (zclip < -1.0 || zclip > 1.0);\n\ if (!clipped && (disableDepthTestDistance < 0.0 || (lengthSq > 0.0 && lengthSq < disableDepthTestDistance)))\n\ {\n\ // Position z on the near plane.\n\ gl_Position.z = -gl_Position.w;\n\ #ifdef LOG_DEPTH\n\ v_depthFromNearPlusOne = 1.0;\n\ #endif\n\ }\n\ }\n\ #endif\n\ \n\ #ifdef FRAGMENT_DEPTH_CHECK\n\ if (sizeInMeters) {\n\ translate /= mpp;\n\ dimensions /= mpp;\n\ imageSize /= mpp;\n\ }\n\ \n\ #if defined(ROTATION) || defined(ALIGNED_AXIS)\n\ v_rotationMatrix = rotationMatrix;\n\ #else\n\ v_rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0);\n\ #endif\n\ \n\ float enableDepthCheck = 0.0;\n\ if (lengthSq < disableDepthTestDistance)\n\ {\n\ enableDepthCheck = 1.0;\n\ }\n\ \n\ float dw = floor(clamp(dimensions.x, 0.0, SHIFT_LEFT12));\n\ float dh = floor(clamp(dimensions.y, 0.0, SHIFT_LEFT12));\n\ \n\ float iw = floor(clamp(imageSize.x, 0.0, SHIFT_LEFT12));\n\ float ih = floor(clamp(imageSize.y, 0.0, SHIFT_LEFT12));\n\ \n\ v_compressed.x = eyeDepth;\n\ v_compressed.y = applyTranslate * SHIFT_LEFT1 + enableDepthCheck;\n\ v_compressed.z = dw * SHIFT_LEFT12 + dh;\n\ v_compressed.w = iw * SHIFT_LEFT12 + ih;\n\ v_originTextureCoordinateAndTranslate.xy = depthOrigin;\n\ v_originTextureCoordinateAndTranslate.zw = translate;\n\ v_textureCoordinateBounds = textureCoordinateBoundsOrLabelTranslate;\n\ \n\ #endif\n\ \n\ #ifdef SDF\n\ vec4 outlineColor;\n\ float outlineWidth;\n\ \n\ temp = sdf.x;\n\ temp = temp * SHIFT_RIGHT8;\n\ outlineColor.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ outlineColor.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ outlineColor.r = floor(temp);\n\ \n\ temp = sdf.y;\n\ temp = temp * SHIFT_RIGHT8;\n\ float temp3 = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ outlineWidth = (temp - floor(temp)) * SHIFT_LEFT8;\n\ outlineColor.a = floor(temp);\n\ outlineColor /= 255.0;\n\ \n\ v_outlineWidth = outlineWidth / 255.0;\n\ v_outlineColor = outlineColor;\n\ v_outlineColor.a *= translucency;\n\ #endif\n\ \n\ v_pickColor = pickColor;\n\ \n\ v_color = color;\n\ v_color.a *= translucency;\n\ \n\ }\n\ "; /** * Functions that do scene-dependent transforms between rendering-related coordinate systems. * * @namespace SceneTransforms */ var SceneTransforms = {}; var actualPositionScratch = new Cartesian4(0, 0, 0, 1); var positionCC = new Cartesian4(); var scratchViewport$1 = new BoundingRectangle(); var scratchWindowCoord0 = new Cartesian2(); var scratchWindowCoord1 = new Cartesian2(); /** * Transforms a position in WGS84 coordinates to window coordinates. This is commonly used to place an * HTML element at the same screen position as an object in the scene. * * @param {Scene} scene The scene. * @param {Cartesian3} position The position in WGS84 (world) coordinates. * @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. This may be undefined if the input position is near the center of the ellipsoid. * * @example * // Output the window position of longitude/latitude (0, 0) every time the mouse moves. * var scene = widget.scene; * var ellipsoid = scene.globe.ellipsoid; * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); * handler.setInputAction(function(movement) { * console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position)); * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); */ SceneTransforms.wgs84ToWindowCoordinates = function (scene, position, result) { return SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates( scene, position, Cartesian3.ZERO, result ); }; var scratchCartesian4$3 = new Cartesian4(); var scratchEyeOffset = new Cartesian3(); function worldToClip(position, eyeOffset, camera, result) { var viewMatrix = camera.viewMatrix; var positionEC = Matrix4.multiplyByVector( viewMatrix, Cartesian4.fromElements( position.x, position.y, position.z, 1, scratchCartesian4$3 ), scratchCartesian4$3 ); var zEyeOffset = Cartesian3.multiplyComponents( eyeOffset, Cartesian3.normalize(positionEC, scratchEyeOffset), scratchEyeOffset ); positionEC.x += eyeOffset.x + zEyeOffset.x; positionEC.y += eyeOffset.y + zEyeOffset.y; positionEC.z += zEyeOffset.z; return Matrix4.multiplyByVector( camera.frustum.projectionMatrix, positionEC, result ); } var scratchMaxCartographic = new Cartographic(Math.PI, CesiumMath.PI_OVER_TWO); var scratchProjectedCartesian = new Cartesian3(); var scratchCameraPosition$1 = new Cartesian3(); /** * @private */ SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates = function ( scene, position, eyeOffset, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError("scene is required."); } if (!defined(position)) { throw new DeveloperError("position is required."); } //>>includeEnd('debug'); // Transform for 3D, 2D, or Columbus view var frameState = scene.frameState; var actualPosition = SceneTransforms.computeActualWgs84Position( frameState, position, actualPositionScratch ); if (!defined(actualPosition)) { return undefined; } // Assuming viewport takes up the entire canvas... var canvas = scene.canvas; var viewport = scratchViewport$1; viewport.x = 0; viewport.y = 0; viewport.width = canvas.clientWidth; viewport.height = canvas.clientHeight; var camera = scene.camera; var cameraCentered = false; if (frameState.mode === SceneMode$1.SCENE2D) { var projection = scene.mapProjection; var maxCartographic = scratchMaxCartographic; var maxCoord = projection.project( maxCartographic, scratchProjectedCartesian ); var cameraPosition = Cartesian3.clone( camera.position, scratchCameraPosition$1 ); var frustum = camera.frustum.clone(); var viewportTransformation = Matrix4.computeViewportTransformation( viewport, 0.0, 1.0, new Matrix4() ); var projectionMatrix = camera.frustum.projectionMatrix; var x = camera.positionWC.y; var eyePoint = Cartesian3.fromElements( CesiumMath.sign(x) * maxCoord.x - x, 0.0, -camera.positionWC.x ); var windowCoordinates = Transforms.pointToGLWindowCoordinates( projectionMatrix, viewportTransformation, eyePoint ); if ( x === 0.0 || windowCoordinates.x <= 0.0 || windowCoordinates.x >= canvas.clientWidth ) { cameraCentered = true; } else { if (windowCoordinates.x > canvas.clientWidth * 0.5) { viewport.width = windowCoordinates.x; camera.frustum.right = maxCoord.x - x; positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC); SceneTransforms.clipToGLWindowCoordinates( viewport, positionCC, scratchWindowCoord0 ); viewport.x += windowCoordinates.x; camera.position.x = -camera.position.x; var right = camera.frustum.right; camera.frustum.right = -camera.frustum.left; camera.frustum.left = -right; positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC); SceneTransforms.clipToGLWindowCoordinates( viewport, positionCC, scratchWindowCoord1 ); } else { viewport.x += windowCoordinates.x; viewport.width -= windowCoordinates.x; camera.frustum.left = -maxCoord.x - x; positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC); SceneTransforms.clipToGLWindowCoordinates( viewport, positionCC, scratchWindowCoord0 ); viewport.x = viewport.x - viewport.width; camera.position.x = -camera.position.x; var left = camera.frustum.left; camera.frustum.left = -camera.frustum.right; camera.frustum.right = -left; positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC); SceneTransforms.clipToGLWindowCoordinates( viewport, positionCC, scratchWindowCoord1 ); } Cartesian3.clone(cameraPosition, camera.position); camera.frustum = frustum.clone(); result = Cartesian2.clone(scratchWindowCoord0, result); if (result.x < 0.0 || result.x > canvas.clientWidth) { result.x = scratchWindowCoord1.x; } } } if (frameState.mode !== SceneMode$1.SCENE2D || cameraCentered) { // View-projection matrix to transform from world coordinates to clip coordinates positionCC = worldToClip(actualPosition, eyeOffset, camera, positionCC); if ( positionCC.z < 0 && !(camera.frustum instanceof OrthographicFrustum) && !(camera.frustum instanceof OrthographicOffCenterFrustum) ) { return undefined; } result = SceneTransforms.clipToGLWindowCoordinates( viewport, positionCC, result ); } result.y = canvas.clientHeight - result.y; return result; }; /** * Transforms a position in WGS84 coordinates to drawing buffer coordinates. This may produce different * results from SceneTransforms.wgs84ToWindowCoordinates when the browser zoom is not 100%, or on high-DPI displays. * * @param {Scene} scene The scene. * @param {Cartesian3} position The position in WGS84 (world) coordinates. * @param {Cartesian2} [result] An optional object to return the input position transformed to window coordinates. * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. This may be undefined if the input position is near the center of the ellipsoid. * * @example * // Output the window position of longitude/latitude (0, 0) every time the mouse moves. * var scene = widget.scene; * var ellipsoid = scene.globe.ellipsoid; * var position = Cesium.Cartesian3.fromDegrees(0.0, 0.0); * var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas); * handler.setInputAction(function(movement) { * console.log(Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position)); * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); */ SceneTransforms.wgs84ToDrawingBufferCoordinates = function ( scene, position, result ) { result = SceneTransforms.wgs84ToWindowCoordinates(scene, position, result); if (!defined(result)) { return undefined; } return SceneTransforms.transformWindowToDrawingBuffer(scene, result, result); }; var projectedPosition = new Cartesian3(); var positionInCartographic = new Cartographic(); /** * @private */ SceneTransforms.computeActualWgs84Position = function ( frameState, position, result ) { var mode = frameState.mode; if (mode === SceneMode$1.SCENE3D) { return Cartesian3.clone(position, result); } var projection = frameState.mapProjection; var cartographic = projection.ellipsoid.cartesianToCartographic( position, positionInCartographic ); if (!defined(cartographic)) { return undefined; } projection.project(cartographic, projectedPosition); if (mode === SceneMode$1.COLUMBUS_VIEW) { return Cartesian3.fromElements( projectedPosition.z, projectedPosition.x, projectedPosition.y, result ); } if (mode === SceneMode$1.SCENE2D) { return Cartesian3.fromElements( 0.0, projectedPosition.x, projectedPosition.y, result ); } // mode === SceneMode.MORPHING var morphTime = frameState.morphTime; return Cartesian3.fromElements( CesiumMath.lerp(projectedPosition.z, position.x, morphTime), CesiumMath.lerp(projectedPosition.x, position.y, morphTime), CesiumMath.lerp(projectedPosition.y, position.z, morphTime), result ); }; var positionNDC = new Cartesian3(); var positionWC = new Cartesian3(); var viewportTransform = new Matrix4(); /** * @private */ SceneTransforms.clipToGLWindowCoordinates = function ( viewport, position, result ) { // Perspective divide to transform from clip coordinates to normalized device coordinates Cartesian3.divideByScalar(position, position.w, positionNDC); // Viewport transform to transform from clip coordinates to window coordinates Matrix4.computeViewportTransformation(viewport, 0.0, 1.0, viewportTransform); Matrix4.multiplyByPoint(viewportTransform, positionNDC, positionWC); return Cartesian2.fromCartesian3(positionWC, result); }; /** * @private */ SceneTransforms.transformWindowToDrawingBuffer = function ( scene, windowPosition, result ) { var canvas = scene.canvas; var xScale = scene.drawingBufferWidth / canvas.clientWidth; var yScale = scene.drawingBufferHeight / canvas.clientHeight; return Cartesian2.fromElements( windowPosition.x * xScale, windowPosition.y * yScale, result ); }; var scratchNDC = new Cartesian4(); var scratchWorldCoords = new Cartesian4(); /** * @private */ SceneTransforms.drawingBufferToWgs84Coordinates = function ( scene, drawingBufferPosition, depth, result ) { var context = scene.context; var uniformState = context.uniformState; var currentFrustum = uniformState.currentFrustum; var near = currentFrustum.x; var far = currentFrustum.y; if (scene.frameState.useLogDepth) { // transforming logarithmic depth of form // log2(z + 1) / log2( far + 1); // to perspective form // (far - far * near / z) / (far - near) var log2Depth = depth * uniformState.log2FarDepthFromNearPlusOne; var depthFromNear = Math.pow(2.0, log2Depth) - 1.0; depth = (far * (1.0 - near / (depthFromNear + near))) / (far - near); } var viewport = scene.view.passState.viewport; var ndc = Cartesian4.clone(Cartesian4.UNIT_W, scratchNDC); ndc.x = ((drawingBufferPosition.x - viewport.x) / viewport.width) * 2.0 - 1.0; ndc.y = ((drawingBufferPosition.y - viewport.y) / viewport.height) * 2.0 - 1.0; ndc.z = depth * 2.0 - 1.0; ndc.w = 1.0; var worldCoords; var frustum = scene.camera.frustum; if (!defined(frustum.fovy)) { if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } worldCoords = scratchWorldCoords; worldCoords.x = (ndc.x * (frustum.right - frustum.left) + frustum.left + frustum.right) * 0.5; worldCoords.y = (ndc.y * (frustum.top - frustum.bottom) + frustum.bottom + frustum.top) * 0.5; worldCoords.z = (ndc.z * (near - far) - near - far) * 0.5; worldCoords.w = 1.0; worldCoords = Matrix4.multiplyByVector( uniformState.inverseView, worldCoords, worldCoords ); } else { worldCoords = Matrix4.multiplyByVector( uniformState.inverseViewProjection, ndc, scratchWorldCoords ); // Reverse perspective divide var w = 1.0 / worldCoords.w; Cartesian3.multiplyByScalar(worldCoords, w, worldCoords); } return Cartesian3.fromCartesian4(worldCoords, result); }; /** * A viewport-aligned image positioned in the 3D scene, that is created * and rendered using a {@link BillboardCollection}. A billboard is created and its initial * properties are set by calling {@link BillboardCollection#add}. *

*
*
* Example billboards *
* * @alias Billboard * * @performance Reading a property, e.g., {@link Billboard#show}, is constant time. * Assigning to a property is constant time but results in * CPU to GPU traffic when {@link BillboardCollection#update} is called. The per-billboard traffic is * the same regardless of how many properties were updated. If most billboards in a collection need to be * updated, it may be more efficient to clear the collection with {@link BillboardCollection#removeAll} * and add new billboards instead of modifying each one. * * @exception {DeveloperError} scaleByDistance.far must be greater than scaleByDistance.near * @exception {DeveloperError} translucencyByDistance.far must be greater than translucencyByDistance.near * @exception {DeveloperError} pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near * @exception {DeveloperError} distanceDisplayCondition.far must be greater than distanceDisplayCondition.near * * @see BillboardCollection * @see BillboardCollection#add * @see Label * * @internalConstructor * @class * * @demo {@link https://sandcastle.cesium.com/index.html?src=Billboards.html|Cesium Sandcastle Billboard Demo} */ function Billboard(options, billboardCollection) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if ( defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0 ) { throw new DeveloperError( "disableDepthTestDistance must be greater than or equal to 0.0." ); } //>>includeEnd('debug'); var translucencyByDistance = options.translucencyByDistance; var pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance; var scaleByDistance = options.scaleByDistance; var distanceDisplayCondition = options.distanceDisplayCondition; if (defined(translucencyByDistance)) { //>>includeStart('debug', pragmas.debug); if (translucencyByDistance.far <= translucencyByDistance.near) { throw new DeveloperError( "translucencyByDistance.far must be greater than translucencyByDistance.near." ); } //>>includeEnd('debug'); translucencyByDistance = NearFarScalar.clone(translucencyByDistance); } if (defined(pixelOffsetScaleByDistance)) { //>>includeStart('debug', pragmas.debug); if (pixelOffsetScaleByDistance.far <= pixelOffsetScaleByDistance.near) { throw new DeveloperError( "pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near." ); } //>>includeEnd('debug'); pixelOffsetScaleByDistance = NearFarScalar.clone( pixelOffsetScaleByDistance ); } if (defined(scaleByDistance)) { //>>includeStart('debug', pragmas.debug); if (scaleByDistance.far <= scaleByDistance.near) { throw new DeveloperError( "scaleByDistance.far must be greater than scaleByDistance.near." ); } //>>includeEnd('debug'); scaleByDistance = NearFarScalar.clone(scaleByDistance); } if (defined(distanceDisplayCondition)) { //>>includeStart('debug', pragmas.debug); if (distanceDisplayCondition.far <= distanceDisplayCondition.near) { throw new DeveloperError( "distanceDisplayCondition.far must be greater than distanceDisplayCondition.near." ); } //>>includeEnd('debug'); distanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition ); } this._show = defaultValue(options.show, true); this._position = Cartesian3.clone( defaultValue(options.position, Cartesian3.ZERO) ); this._actualPosition = Cartesian3.clone(this._position); // For columbus view and 2D this._pixelOffset = Cartesian2.clone( defaultValue(options.pixelOffset, Cartesian2.ZERO) ); this._translate = new Cartesian2(0.0, 0.0); // used by labels for glyph vertex translation this._eyeOffset = Cartesian3.clone( defaultValue(options.eyeOffset, Cartesian3.ZERO) ); this._heightReference = defaultValue( options.heightReference, HeightReference$1.NONE ); this._verticalOrigin = defaultValue( options.verticalOrigin, VerticalOrigin$1.CENTER ); this._horizontalOrigin = defaultValue( options.horizontalOrigin, HorizontalOrigin$1.CENTER ); this._scale = defaultValue(options.scale, 1.0); this._color = Color.clone(defaultValue(options.color, Color.WHITE)); this._rotation = defaultValue(options.rotation, 0.0); this._alignedAxis = Cartesian3.clone( defaultValue(options.alignedAxis, Cartesian3.ZERO) ); this._width = options.width; this._height = options.height; this._scaleByDistance = scaleByDistance; this._translucencyByDistance = translucencyByDistance; this._pixelOffsetScaleByDistance = pixelOffsetScaleByDistance; this._sizeInMeters = defaultValue(options.sizeInMeters, false); this._distanceDisplayCondition = distanceDisplayCondition; this._disableDepthTestDistance = options.disableDepthTestDistance; this._id = options.id; this._collection = defaultValue(options.collection, billboardCollection); this._pickId = undefined; this._pickPrimitive = defaultValue(options._pickPrimitive, this); this._billboardCollection = billboardCollection; this._dirty = false; this._index = -1; //Used only by BillboardCollection this._batchIndex = undefined; // Used only by Vector3DTilePoints and BillboardCollection this._imageIndex = -1; this._imageIndexPromise = undefined; this._imageId = undefined; this._image = undefined; this._imageSubRegion = undefined; this._imageWidth = undefined; this._imageHeight = undefined; this._labelDimensions = undefined; this._labelHorizontalOrigin = undefined; this._labelTranslate = undefined; var image = options.image; var imageId = options.imageId; if (defined(image)) { if (!defined(imageId)) { if (typeof image === "string") { imageId = image; } else if (defined(image.src)) { imageId = image.src; } else { imageId = createGuid(); } } this._imageId = imageId; this._image = image; } if (defined(options.imageSubRegion)) { this._imageId = imageId; this._imageSubRegion = options.imageSubRegion; } if (defined(this._billboardCollection._textureAtlas)) { this._loadImage(); } this._actualClampedPosition = undefined; this._removeCallbackFunc = undefined; this._mode = SceneMode$1.SCENE3D; this._clusterShow = true; this._outlineColor = Color.clone( defaultValue(options.outlineColor, Color.BLACK) ); this._outlineWidth = defaultValue(options.outlineWidth, 0.0); this._updateClamping(); } var SHOW_INDEX$5 = (Billboard.SHOW_INDEX = 0); var POSITION_INDEX$5 = (Billboard.POSITION_INDEX = 1); var PIXEL_OFFSET_INDEX$1 = (Billboard.PIXEL_OFFSET_INDEX = 2); var EYE_OFFSET_INDEX$1 = (Billboard.EYE_OFFSET_INDEX = 3); var HORIZONTAL_ORIGIN_INDEX$1 = (Billboard.HORIZONTAL_ORIGIN_INDEX = 4); var VERTICAL_ORIGIN_INDEX$1 = (Billboard.VERTICAL_ORIGIN_INDEX = 5); var SCALE_INDEX$1 = (Billboard.SCALE_INDEX = 6); var IMAGE_INDEX_INDEX$1 = (Billboard.IMAGE_INDEX_INDEX = 7); var COLOR_INDEX$3 = (Billboard.COLOR_INDEX = 8); var ROTATION_INDEX$1 = (Billboard.ROTATION_INDEX = 9); var ALIGNED_AXIS_INDEX$1 = (Billboard.ALIGNED_AXIS_INDEX = 10); var SCALE_BY_DISTANCE_INDEX$3 = (Billboard.SCALE_BY_DISTANCE_INDEX = 11); var TRANSLUCENCY_BY_DISTANCE_INDEX$3 = (Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX = 12); var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX$1 = (Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = 13); var DISTANCE_DISPLAY_CONDITION$2 = (Billboard.DISTANCE_DISPLAY_CONDITION = 14); var DISABLE_DEPTH_DISTANCE$1 = (Billboard.DISABLE_DEPTH_DISTANCE = 15); Billboard.TEXTURE_COORDINATE_BOUNDS = 16; var SDF_INDEX$1 = (Billboard.SDF_INDEX = 17); Billboard.NUMBER_OF_PROPERTIES = 18; function makeDirty$2(billboard, propertyChanged) { var billboardCollection = billboard._billboardCollection; if (defined(billboardCollection)) { billboardCollection._updateBillboard(billboard, propertyChanged); billboard._dirty = true; } } Object.defineProperties(Billboard.prototype, { /** * Determines if this billboard will be shown. Use this to hide or show a billboard, instead * of removing it and re-adding it to the collection. * @memberof Billboard.prototype * @type {Boolean} * @default true */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._show !== value) { this._show = value; makeDirty$2(this, SHOW_INDEX$5); } }, }, /** * Gets or sets the Cartesian position of this billboard. * @memberof Billboard.prototype * @type {Cartesian3} */ position: { get: function () { return this._position; }, set: function (value) { //>>includeStart('debug', pragmas.debug) if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var position = this._position; if (!Cartesian3.equals(position, value)) { Cartesian3.clone(value, position); Cartesian3.clone(value, this._actualPosition); this._updateClamping(); makeDirty$2(this, POSITION_INDEX$5); } }, }, /** * Gets or sets the height reference of this billboard. * @memberof Billboard.prototype * @type {HeightReference} * @default HeightReference.NONE */ heightReference: { get: function () { return this._heightReference; }, set: function (value) { //>>includeStart('debug', pragmas.debug) if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var heightReference = this._heightReference; if (value !== heightReference) { this._heightReference = value; this._updateClamping(); makeDirty$2(this, POSITION_INDEX$5); } }, }, /** * Gets or sets the pixel offset in screen space from the origin of this billboard. This is commonly used * to align multiple billboards and labels at the same position, e.g., an image and text. The * screen space origin is the top, left corner of the canvas; x increases from * left to right, and y increases from top to bottom. *

*
* * * *
default
b.pixeloffset = new Cartesian2(50, 25);
* The billboard's origin is indicated by the yellow point. *
* @memberof Billboard.prototype * @type {Cartesian2} */ pixelOffset: { get: function () { return this._pixelOffset; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var pixelOffset = this._pixelOffset; if (!Cartesian2.equals(pixelOffset, value)) { Cartesian2.clone(value, pixelOffset); makeDirty$2(this, PIXEL_OFFSET_INDEX$1); } }, }, /** * Gets or sets near and far scaling properties of a Billboard based on the billboard's distance from the camera. * A billboard's scale will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's scale remains clamped to the nearest bound. If undefined, * scaleByDistance will be disabled. * @memberof Billboard.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a billboard's scaleByDistance to scale by 1.5 when the * // camera is 1500 meters from the billboard and disappear as * // the camera distance approaches 8.0e6 meters. * b.scaleByDistance = new Cesium.NearFarScalar(1.5e2, 1.5, 8.0e6, 0.0); * * @example * // Example 2. * // disable scaling by distance * b.scaleByDistance = undefined; */ scaleByDistance: { get: function () { return this._scaleByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var scaleByDistance = this._scaleByDistance; if (!NearFarScalar.equals(scaleByDistance, value)) { this._scaleByDistance = NearFarScalar.clone(value, scaleByDistance); makeDirty$2(this, SCALE_BY_DISTANCE_INDEX$3); } }, }, /** * Gets or sets near and far translucency properties of a Billboard based on the billboard's distance from the camera. * A billboard's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's translucency remains clamped to the nearest bound. If undefined, * translucencyByDistance will be disabled. * @memberof Billboard.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a billboard's translucency to 1.0 when the * // camera is 1500 meters from the billboard and disappear as * // the camera distance approaches 8.0e6 meters. * b.translucencyByDistance = new Cesium.NearFarScalar(1.5e2, 1.0, 8.0e6, 0.0); * * @example * // Example 2. * // disable translucency by distance * b.translucencyByDistance = undefined; */ translucencyByDistance: { get: function () { return this._translucencyByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var translucencyByDistance = this._translucencyByDistance; if (!NearFarScalar.equals(translucencyByDistance, value)) { this._translucencyByDistance = NearFarScalar.clone( value, translucencyByDistance ); makeDirty$2(this, TRANSLUCENCY_BY_DISTANCE_INDEX$3); } }, }, /** * Gets or sets near and far pixel offset scaling properties of a Billboard based on the billboard's distance from the camera. * A billboard's pixel offset will be scaled between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the billboard's pixel offset scale remains clamped to the nearest bound. If undefined, * pixelOffsetScaleByDistance will be disabled. * @memberof Billboard.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a billboard's pixel offset scale to 0.0 when the * // camera is 1500 meters from the billboard and scale pixel offset to 10.0 pixels * // in the y direction the camera distance approaches 8.0e6 meters. * b.pixelOffset = new Cesium.Cartesian2(0.0, 1.0); * b.pixelOffsetScaleByDistance = new Cesium.NearFarScalar(1.5e2, 0.0, 8.0e6, 10.0); * * @example * // Example 2. * // disable pixel offset by distance * b.pixelOffsetScaleByDistance = undefined; */ pixelOffsetScaleByDistance: { get: function () { return this._pixelOffsetScaleByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var pixelOffsetScaleByDistance = this._pixelOffsetScaleByDistance; if (!NearFarScalar.equals(pixelOffsetScaleByDistance, value)) { this._pixelOffsetScaleByDistance = NearFarScalar.clone( value, pixelOffsetScaleByDistance ); makeDirty$2(this, PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX$1); } }, }, /** * Gets or sets the 3D Cartesian offset applied to this billboard in eye coordinates. Eye coordinates is a left-handed * coordinate system, where x points towards the viewer's right, y points up, and * z points into the screen. Eye coordinates use the same scale as world and model coordinates, * which is typically meters. *

* An eye offset is commonly used to arrange multiple billboards or objects at the same position, e.g., to * arrange a billboard above its corresponding 3D model. *

* Below, the billboard is positioned at the center of the Earth but an eye offset makes it always * appear on top of the Earth regardless of the viewer's or Earth's orientation. *

*
* * * *
* b.eyeOffset = new Cartesian3(0.0, 8000000.0, 0.0);

*
* @memberof Billboard.prototype * @type {Cartesian3} */ eyeOffset: { get: function () { return this._eyeOffset; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var eyeOffset = this._eyeOffset; if (!Cartesian3.equals(eyeOffset, value)) { Cartesian3.clone(value, eyeOffset); makeDirty$2(this, EYE_OFFSET_INDEX$1); } }, }, /** * Gets or sets the horizontal origin of this billboard, which determines if the billboard is * to the left, center, or right of its anchor position. *

*
*
*
* @memberof Billboard.prototype * @type {HorizontalOrigin} * @example * // Use a bottom, left origin * b.horizontalOrigin = Cesium.HorizontalOrigin.LEFT; * b.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; */ horizontalOrigin: { get: function () { return this._horizontalOrigin; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._horizontalOrigin !== value) { this._horizontalOrigin = value; makeDirty$2(this, HORIZONTAL_ORIGIN_INDEX$1); } }, }, /** * Gets or sets the vertical origin of this billboard, which determines if the billboard is * to the above, below, or at the center of its anchor position. *

*
*
*
* @memberof Billboard.prototype * @type {VerticalOrigin} * @example * // Use a bottom, left origin * b.horizontalOrigin = Cesium.HorizontalOrigin.LEFT; * b.verticalOrigin = Cesium.VerticalOrigin.BOTTOM; */ verticalOrigin: { get: function () { return this._verticalOrigin; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._verticalOrigin !== value) { this._verticalOrigin = value; makeDirty$2(this, VERTICAL_ORIGIN_INDEX$1); } }, }, /** * Gets or sets the uniform scale that is multiplied with the billboard's image size in pixels. * A scale of 1.0 does not change the size of the billboard; a scale greater than * 1.0 enlarges the billboard; a positive scale less than 1.0 shrinks * the billboard. *

*
*
* From left to right in the above image, the scales are 0.5, 1.0, * and 2.0. *
* @memberof Billboard.prototype * @type {Number} */ scale: { get: function () { return this._scale; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._scale !== value) { this._scale = value; makeDirty$2(this, SCALE_INDEX$1); } }, }, /** * Gets or sets the color that is multiplied with the billboard's texture. This has two common use cases. First, * the same white texture may be used by many different billboards, each with a different color, to create * colored billboards. Second, the color's alpha component can be used to make the billboard translucent as shown below. * An alpha of 0.0 makes the billboard transparent, and 1.0 makes the billboard opaque. *

*
* * * *
default
alpha : 0.5
*
*
* The red, green, blue, and alpha values are indicated by value's red, green, * blue, and alpha properties as shown in Example 1. These components range from 0.0 * (no intensity) to 1.0 (full intensity). * @memberof Billboard.prototype * @type {Color} * * @example * // Example 1. Assign yellow. * b.color = Cesium.Color.YELLOW; * * @example * // Example 2. Make a billboard 50% translucent. * b.color = new Cesium.Color(1.0, 1.0, 1.0, 0.5); */ color: { get: function () { return this._color; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var color = this._color; if (!Color.equals(color, value)) { Color.clone(value, color); makeDirty$2(this, COLOR_INDEX$3); } }, }, /** * Gets or sets the rotation angle in radians. * @memberof Billboard.prototype * @type {Number} */ rotation: { get: function () { return this._rotation; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._rotation !== value) { this._rotation = value; makeDirty$2(this, ROTATION_INDEX$1); } }, }, /** * Gets or sets the aligned axis in world space. The aligned axis is the unit vector that the billboard up vector points towards. * The default is the zero vector, which means the billboard is aligned to the screen up vector. * @memberof Billboard.prototype * @type {Cartesian3} * @example * // Example 1. * // Have the billboard up vector point north * billboard.alignedAxis = Cesium.Cartesian3.UNIT_Z; * * @example * // Example 2. * // Have the billboard point east. * billboard.alignedAxis = Cesium.Cartesian3.UNIT_Z; * billboard.rotation = -Cesium.Math.PI_OVER_TWO; * * @example * // Example 3. * // Reset the aligned axis * billboard.alignedAxis = Cesium.Cartesian3.ZERO; */ alignedAxis: { get: function () { return this._alignedAxis; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var alignedAxis = this._alignedAxis; if (!Cartesian3.equals(alignedAxis, value)) { Cartesian3.clone(value, alignedAxis); makeDirty$2(this, ALIGNED_AXIS_INDEX$1); } }, }, /** * Gets or sets a width for the billboard. If undefined, the image width will be used. * @memberof Billboard.prototype * @type {Number} */ width: { get: function () { return defaultValue(this._width, this._imageWidth); }, set: function (value) { if (this._width !== value) { this._width = value; makeDirty$2(this, IMAGE_INDEX_INDEX$1); } }, }, /** * Gets or sets a height for the billboard. If undefined, the image height will be used. * @memberof Billboard.prototype * @type {Number} */ height: { get: function () { return defaultValue(this._height, this._imageHeight); }, set: function (value) { if (this._height !== value) { this._height = value; makeDirty$2(this, IMAGE_INDEX_INDEX$1); } }, }, /** * Gets or sets if the billboard size is in meters or pixels. true to size the billboard in meters; * otherwise, the size is in pixels. * @memberof Billboard.prototype * @type {Boolean} * @default false */ sizeInMeters: { get: function () { return this._sizeInMeters; }, set: function (value) { if (this._sizeInMeters !== value) { this._sizeInMeters = value; makeDirty$2(this, COLOR_INDEX$3); } }, }, /** * Gets or sets the condition specifying at what distance from the camera that this billboard will be displayed. * @memberof Billboard.prototype * @type {DistanceDisplayCondition} * @default undefined */ distanceDisplayCondition: { get: function () { return this._distanceDisplayCondition; }, set: function (value) { if ( !DistanceDisplayCondition.equals(value, this._distanceDisplayCondition) ) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); this._distanceDisplayCondition = DistanceDisplayCondition.clone( value, this._distanceDisplayCondition ); makeDirty$2(this, DISTANCE_DISPLAY_CONDITION$2); } }, }, /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof Billboard.prototype * @type {Number} */ disableDepthTestDistance: { get: function () { return this._disableDepthTestDistance; }, set: function (value) { if (this._disableDepthTestDistance !== value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value < 0.0) { throw new DeveloperError( "disableDepthTestDistance must be greater than or equal to 0.0." ); } //>>includeEnd('debug'); this._disableDepthTestDistance = value; makeDirty$2(this, DISABLE_DEPTH_DISTANCE$1); } }, }, /** * Gets or sets the user-defined object returned when the billboard is picked. * @memberof Billboard.prototype * @type {Object} */ id: { get: function () { return this._id; }, set: function (value) { this._id = value; if (defined(this._pickId)) { this._pickId.object.id = value; } }, }, /** * The primitive to return when picking this billboard. * @memberof Billboard.prototype * @private */ pickPrimitive: { get: function () { return this._pickPrimitive; }, set: function (value) { this._pickPrimitive = value; if (defined(this._pickId)) { this._pickId.object.primitive = value; } }, }, /** * @private */ pickId: { get: function () { return this._pickId; }, }, /** *

* Gets or sets the image to be used for this billboard. If a texture has already been created for the * given image, the existing texture is used. *

*

* This property can be set to a loaded Image, a URL which will be loaded as an Image automatically, * a canvas, or another billboard's image property (from the same billboard collection). *

* * @memberof Billboard.prototype * @type {String} * @example * // load an image from a URL * b.image = 'some/image/url.png'; * * // assuming b1 and b2 are billboards in the same billboard collection, * // use the same image for both billboards. * b2.image = b1.image; */ image: { get: function () { return this._imageId; }, set: function (value) { if (!defined(value)) { this._imageIndex = -1; this._imageSubRegion = undefined; this._imageId = undefined; this._image = undefined; this._imageIndexPromise = undefined; makeDirty$2(this, IMAGE_INDEX_INDEX$1); } else if (typeof value === "string") { this.setImage(value, value); } else if (value instanceof Resource) { this.setImage(value.url, value); } else if (defined(value.src)) { this.setImage(value.src, value); } else { this.setImage(createGuid(), value); } }, }, /** * When true, this billboard is ready to render, i.e., the image * has been downloaded and the WebGL resources are created. * * @memberof Billboard.prototype * * @type {Boolean} * @readonly * * @default false */ ready: { get: function () { return this._imageIndex !== -1; }, }, /** * Keeps track of the position of the billboard based on the height reference. * @memberof Billboard.prototype * @type {Cartesian3} * @private */ _clampedPosition: { get: function () { return this._actualClampedPosition; }, set: function (value) { this._actualClampedPosition = Cartesian3.clone( value, this._actualClampedPosition ); makeDirty$2(this, POSITION_INDEX$5); }, }, /** * Determines whether or not this billboard will be shown or hidden because it was clustered. * @memberof Billboard.prototype * @type {Boolean} * @private */ clusterShow: { get: function () { return this._clusterShow; }, set: function (value) { if (this._clusterShow !== value) { this._clusterShow = value; makeDirty$2(this, SHOW_INDEX$5); } }, }, /** * The outline color of this Billboard. Effective only for SDF billboards like Label glyphs. * @memberof Billboard.prototype * @type {Color} * @private */ outlineColor: { get: function () { return this._outlineColor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var outlineColor = this._outlineColor; if (!Color.equals(outlineColor, value)) { Color.clone(value, outlineColor); makeDirty$2(this, SDF_INDEX$1); } }, }, /** * The outline width of this Billboard in pixels. Effective only for SDF billboards like Label glyphs. * @memberof Billboard.prototype * @type {Number} * @private */ outlineWidth: { get: function () { return this._outlineWidth; }, set: function (value) { if (this._outlineWidth !== value) { this._outlineWidth = value; makeDirty$2(this, SDF_INDEX$1); } }, }, }); Billboard.prototype.getPickId = function (context) { if (!defined(this._pickId)) { this._pickId = context.createPickId({ primitive: this._pickPrimitive, collection: this._collection, id: this._id, }); } return this._pickId; }; Billboard.prototype._updateClamping = function () { Billboard._updateClamping(this._billboardCollection, this); }; var scratchCartographic$b = new Cartographic(); var scratchPosition$4 = new Cartesian3(); Billboard._updateClamping = function (collection, owner) { var scene = collection._scene; if (!defined(scene) || !defined(scene.globe)) { //>>includeStart('debug', pragmas.debug); if (owner._heightReference !== HeightReference$1.NONE) { throw new DeveloperError( "Height reference is not supported without a scene and globe." ); } //>>includeEnd('debug'); return; } var globe = scene.globe; var ellipsoid = globe.ellipsoid; var surface = globe._surface; var mode = scene.frameState.mode; var modeChanged = mode !== owner._mode; owner._mode = mode; if ( (owner._heightReference === HeightReference$1.NONE || modeChanged) && defined(owner._removeCallbackFunc) ) { owner._removeCallbackFunc(); owner._removeCallbackFunc = undefined; owner._clampedPosition = undefined; } if ( owner._heightReference === HeightReference$1.NONE || !defined(owner._position) ) { return; } var position = ellipsoid.cartesianToCartographic(owner._position); if (!defined(position)) { owner._actualClampedPosition = undefined; return; } if (defined(owner._removeCallbackFunc)) { owner._removeCallbackFunc(); } function updateFunction(clampedPosition) { if (owner._heightReference === HeightReference$1.RELATIVE_TO_GROUND) { if (owner._mode === SceneMode$1.SCENE3D) { var clampedCart = ellipsoid.cartesianToCartographic( clampedPosition, scratchCartographic$b ); clampedCart.height += position.height; ellipsoid.cartographicToCartesian(clampedCart, clampedPosition); } else { clampedPosition.x += position.height; } } owner._clampedPosition = Cartesian3.clone( clampedPosition, owner._clampedPosition ); } owner._removeCallbackFunc = surface.updateHeight(position, updateFunction); Cartographic.clone(position, scratchCartographic$b); var height = globe.getHeight(position); if (defined(height)) { scratchCartographic$b.height = height; } ellipsoid.cartographicToCartesian(scratchCartographic$b, scratchPosition$4); updateFunction(scratchPosition$4); }; Billboard.prototype._loadImage = function () { var atlas = this._billboardCollection._textureAtlas; var imageId = this._imageId; var image = this._image; var imageSubRegion = this._imageSubRegion; var imageIndexPromise; if (defined(image)) { imageIndexPromise = atlas.addImage(imageId, image); } if (defined(imageSubRegion)) { imageIndexPromise = atlas.addSubRegion(imageId, imageSubRegion); } this._imageIndexPromise = imageIndexPromise; if (!defined(imageIndexPromise)) { return; } var that = this; imageIndexPromise .then(function (index) { if ( that._imageId !== imageId || that._image !== image || !BoundingRectangle.equals(that._imageSubRegion, imageSubRegion) ) { // another load occurred before this one finished, ignore the index return; } // fill in imageWidth and imageHeight var textureCoordinates = atlas.textureCoordinates[index]; that._imageWidth = atlas.texture.width * textureCoordinates.width; that._imageHeight = atlas.texture.height * textureCoordinates.height; that._imageIndex = index; that._ready = true; that._image = undefined; that._imageIndexPromise = undefined; makeDirty$2(that, IMAGE_INDEX_INDEX$1); }) .otherwise(function (error) { console.error("Error loading image for billboard: " + error); that._imageIndexPromise = undefined; }); }; /** *

* Sets the image to be used for this billboard. If a texture has already been created for the * given id, the existing texture is used. *

*

* This function is useful for dynamically creating textures that are shared across many billboards. * Only the first billboard will actually call the function and create the texture, while subsequent * billboards created with the same id will simply re-use the existing texture. *

*

* To load an image from a URL, setting the {@link Billboard#image} property is more convenient. *

* * @param {String} id The id of the image. This can be any string that uniquely identifies the image. * @param {HTMLImageElement|HTMLCanvasElement|String|Resource|Billboard.CreateImageCallback} image The image to load. This parameter * can either be a loaded Image or Canvas, a URL which will be loaded as an Image automatically, * or a function which will be called to create the image if it hasn't been loaded already. * @example * // create a billboard image dynamically * function drawImage(id) { * // create and draw an image using a canvas * var canvas = document.createElement('canvas'); * var context2D = canvas.getContext('2d'); * // ... draw image * return canvas; * } * // drawImage will be called to create the texture * b.setImage('myImage', drawImage); * * // subsequent billboards created in the same collection using the same id will use the existing * // texture, without the need to create the canvas or draw the image * b2.setImage('myImage', drawImage); */ Billboard.prototype.setImage = function (id, image) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } if (!defined(image)) { throw new DeveloperError("image is required."); } //>>includeEnd('debug'); if (this._imageId === id) { return; } this._imageIndex = -1; this._imageSubRegion = undefined; this._imageId = id; this._image = image; if (defined(this._billboardCollection._textureAtlas)) { this._loadImage(); } }; /** * Uses a sub-region of the image with the given id as the image for this billboard, * measured in pixels from the bottom-left. * * @param {String} id The id of the image to use. * @param {BoundingRectangle} subRegion The sub-region of the image. * * @exception {RuntimeError} image with id must be in the atlas */ Billboard.prototype.setImageSubRegion = function (id, subRegion) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } if (!defined(subRegion)) { throw new DeveloperError("subRegion is required."); } //>>includeEnd('debug'); if ( this._imageId === id && BoundingRectangle.equals(this._imageSubRegion, subRegion) ) { return; } this._imageIndex = -1; this._imageId = id; this._imageSubRegion = BoundingRectangle.clone(subRegion); if (defined(this._billboardCollection._textureAtlas)) { this._loadImage(); } }; Billboard.prototype._setTranslate = function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var translate = this._translate; if (!Cartesian2.equals(translate, value)) { Cartesian2.clone(value, translate); makeDirty$2(this, PIXEL_OFFSET_INDEX$1); } }; Billboard.prototype._getActualPosition = function () { return defined(this._clampedPosition) ? this._clampedPosition : this._actualPosition; }; Billboard.prototype._setActualPosition = function (value) { if (!defined(this._clampedPosition)) { Cartesian3.clone(value, this._actualPosition); } makeDirty$2(this, POSITION_INDEX$5); }; var tempCartesian3$1 = new Cartesian4(); Billboard._computeActualPosition = function ( billboard, position, frameState, modelMatrix ) { if (defined(billboard._clampedPosition)) { if (frameState.mode !== billboard._mode) { billboard._updateClamping(); } return billboard._clampedPosition; } else if (frameState.mode === SceneMode$1.SCENE3D) { return position; } Matrix4.multiplyByPoint(modelMatrix, position, tempCartesian3$1); return SceneTransforms.computeActualWgs84Position(frameState, tempCartesian3$1); }; var scratchCartesian3$4 = new Cartesian3(); // This function is basically a stripped-down JavaScript version of BillboardCollectionVS.glsl Billboard._computeScreenSpacePosition = function ( modelMatrix, position, eyeOffset, pixelOffset, scene, result ) { // Model to world coordinates var positionWorld = Matrix4.multiplyByPoint( modelMatrix, position, scratchCartesian3$4 ); // World to window coordinates var positionWC = SceneTransforms.wgs84WithEyeOffsetToWindowCoordinates( scene, positionWorld, eyeOffset, result ); if (!defined(positionWC)) { return undefined; } // Apply pixel offset Cartesian2.add(positionWC, pixelOffset, positionWC); return positionWC; }; var scratchPixelOffset = new Cartesian2(0.0, 0.0); /** * Computes the screen-space position of the billboard's origin, taking into account eye and pixel offsets. * The screen space origin is the top, left corner of the canvas; x increases from * left to right, and y increases from top to bottom. * * @param {Scene} scene The scene. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The screen-space position of the billboard. * * @exception {DeveloperError} Billboard must be in a collection. * * @example * console.log(b.computeScreenSpacePosition(scene).toString()); * * @see Billboard#eyeOffset * @see Billboard#pixelOffset */ Billboard.prototype.computeScreenSpacePosition = function (scene, result) { var billboardCollection = this._billboardCollection; if (!defined(result)) { result = new Cartesian2(); } //>>includeStart('debug', pragmas.debug); if (!defined(billboardCollection)) { throw new DeveloperError( "Billboard must be in a collection. Was it removed?" ); } if (!defined(scene)) { throw new DeveloperError("scene is required."); } //>>includeEnd('debug'); // pixel offset for screen space computation is the pixelOffset + screen space translate Cartesian2.clone(this._pixelOffset, scratchPixelOffset); Cartesian2.add(scratchPixelOffset, this._translate, scratchPixelOffset); var modelMatrix = billboardCollection.modelMatrix; var position = this._position; if (defined(this._clampedPosition)) { position = this._clampedPosition; if (scene.mode !== SceneMode$1.SCENE3D) { // position needs to be in world coordinates var projection = scene.mapProjection; var ellipsoid = projection.ellipsoid; var cart = projection.unproject(position, scratchCartographic$b); position = ellipsoid.cartographicToCartesian(cart, scratchCartesian3$4); modelMatrix = Matrix4.IDENTITY; } } var windowCoordinates = Billboard._computeScreenSpacePosition( modelMatrix, position, this._eyeOffset, scratchPixelOffset, scene, result ); return windowCoordinates; }; /** * Gets a billboard's screen space bounding box centered around screenSpacePosition. * @param {Billboard} billboard The billboard to get the screen space bounding box for. * @param {Cartesian2} screenSpacePosition The screen space center of the label. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The screen space bounding box. * * @private */ Billboard.getScreenSpaceBoundingBox = function ( billboard, screenSpacePosition, result ) { var width = billboard.width; var height = billboard.height; var scale = billboard.scale; width *= scale; height *= scale; var x = screenSpacePosition.x; if (billboard.horizontalOrigin === HorizontalOrigin$1.RIGHT) { x -= width; } else if (billboard.horizontalOrigin === HorizontalOrigin$1.CENTER) { x -= width * 0.5; } var y = screenSpacePosition.y; if ( billboard.verticalOrigin === VerticalOrigin$1.BOTTOM || billboard.verticalOrigin === VerticalOrigin$1.BASELINE ) { y -= height; } else if (billboard.verticalOrigin === VerticalOrigin$1.CENTER) { y -= height * 0.5; } if (!defined(result)) { result = new BoundingRectangle(); } result.x = x; result.y = y; result.width = width; result.height = height; return result; }; /** * Determines if this billboard equals another billboard. Billboards are equal if all their properties * are equal. Billboards in different collections can be equal. * * @param {Billboard} other The billboard to compare for equality. * @returns {Boolean} true if the billboards are equal; otherwise, false. */ Billboard.prototype.equals = function (other) { return ( this === other || (defined(other) && this._id === other._id && Cartesian3.equals(this._position, other._position) && this._imageId === other._imageId && this._show === other._show && this._scale === other._scale && this._verticalOrigin === other._verticalOrigin && this._horizontalOrigin === other._horizontalOrigin && this._heightReference === other._heightReference && BoundingRectangle.equals(this._imageSubRegion, other._imageSubRegion) && Color.equals(this._color, other._color) && Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian2.equals(this._translate, other._translate) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && NearFarScalar.equals( this._translucencyByDistance, other._translucencyByDistance ) && NearFarScalar.equals( this._pixelOffsetScaleByDistance, other._pixelOffsetScaleByDistance ) && DistanceDisplayCondition.equals( this._distanceDisplayCondition, other._distanceDisplayCondition ) && this._disableDepthTestDistance === other._disableDepthTestDistance) ); }; Billboard.prototype._destroy = function () { if (defined(this._customData)) { this._billboardCollection._scene.globe._surface.removeTileCustomData( this._customData ); this._customData = undefined; } if (defined(this._removeCallbackFunc)) { this._removeCallbackFunc(); this._removeCallbackFunc = undefined; } this.image = undefined; this._pickId = this._pickId && this._pickId.destroy(); this._billboardCollection = undefined; }; /** * Determines how opaque and translucent parts of billboards, points, and labels are blended with the scene. * * @enum {Number} */ var BlendOption = { /** * The billboards, points, or labels in the collection are completely opaque. * @type {Number} * @constant */ OPAQUE: 0, /** * The billboards, points, or labels in the collection are completely translucent. * @type {Number} * @constant */ TRANSLUCENT: 1, /** * The billboards, points, or labels in the collection are both opaque and translucent. * @type {Number} * @constant */ OPAQUE_AND_TRANSLUCENT: 2, }; var BlendOption$1 = Object.freeze(BlendOption); /** * Settings for the generation of signed distance field glyphs * * @private */ var SDFSettings = { /** * The font size in pixels * * @type {Number} * @constant */ FONT_SIZE: 48.0, /** * Whitespace padding around glyphs. * * @type {Number} * @constant */ PADDING: 10.0, /** * How many pixels around the glyph shape to use for encoding distance * * @type {Number} * @constant */ RADIUS: 8.0, /** * How much of the radius (relative) is used for the inside part the glyph. * * @type {Number} * @constant */ CUTOFF: 0.25, }; var SDFSettings$1 = Object.freeze(SDFSettings); // The atlas is made up of regions of space called nodes that contain images or child nodes. function TextureAtlasNode( bottomLeft, topRight, childNode1, childNode2, imageIndex ) { this.bottomLeft = defaultValue(bottomLeft, Cartesian2.ZERO); this.topRight = defaultValue(topRight, Cartesian2.ZERO); this.childNode1 = childNode1; this.childNode2 = childNode2; this.imageIndex = imageIndex; } var defaultInitialSize = new Cartesian2(16.0, 16.0); /** * A TextureAtlas stores multiple images in one square texture and keeps * track of the texture coordinates for each image. TextureAtlas is dynamic, * meaning new images can be added at any point in time. * Texture coordinates are subject to change if the texture atlas resizes, so it is * important to check {@link TextureAtlas#getGUID} before using old values. * * @alias TextureAtlas * @constructor * * @param {Object} options Object with the following properties: * @param {Scene} options.context The context in which the texture gets created. * @param {PixelFormat} [options.pixelFormat=PixelFormat.RGBA] The pixel format of the texture. * @param {Number} [options.borderWidthInPixels=1] The amount of spacing between adjacent images in pixels. * @param {Cartesian2} [options.initialSize=new Cartesian2(16.0, 16.0)] The initial side lengths of the texture. * * @exception {DeveloperError} borderWidthInPixels must be greater than or equal to zero. * @exception {DeveloperError} initialSize must be greater than zero. * * @private */ function TextureAtlas(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var borderWidthInPixels = defaultValue(options.borderWidthInPixels, 1.0); var initialSize = defaultValue(options.initialSize, defaultInitialSize); //>>includeStart('debug', pragmas.debug); if (!defined(options.context)) { throw new DeveloperError("context is required."); } if (borderWidthInPixels < 0) { throw new DeveloperError( "borderWidthInPixels must be greater than or equal to zero." ); } if (initialSize.x < 1 || initialSize.y < 1) { throw new DeveloperError("initialSize must be greater than zero."); } //>>includeEnd('debug'); this._context = options.context; this._pixelFormat = defaultValue(options.pixelFormat, PixelFormat$1.RGBA); this._borderWidthInPixels = borderWidthInPixels; this._textureCoordinates = []; this._guid = createGuid(); this._idHash = {}; this._initialSize = initialSize; this._root = undefined; } Object.defineProperties(TextureAtlas.prototype, { /** * The amount of spacing between adjacent images in pixels. * @memberof TextureAtlas.prototype * @type {Number} */ borderWidthInPixels: { get: function () { return this._borderWidthInPixels; }, }, /** * An array of {@link BoundingRectangle} texture coordinate regions for all the images in the texture atlas. * The x and y values of the rectangle correspond to the bottom-left corner of the texture coordinate. * The coordinates are in the order that the corresponding images were added to the atlas. * @memberof TextureAtlas.prototype * @type {BoundingRectangle[]} */ textureCoordinates: { get: function () { return this._textureCoordinates; }, }, /** * The texture that all of the images are being written to. * @memberof TextureAtlas.prototype * @type {Texture} */ texture: { get: function () { if (!defined(this._texture)) { this._texture = new Texture({ context: this._context, width: this._initialSize.x, height: this._initialSize.y, pixelFormat: this._pixelFormat, }); } return this._texture; }, }, /** * The number of images in the texture atlas. This value increases * every time addImage or addImages is called. * Texture coordinates are subject to change if the texture atlas resizes, so it is * important to check {@link TextureAtlas#getGUID} before using old values. * @memberof TextureAtlas.prototype * @type {Number} */ numberOfImages: { get: function () { return this._textureCoordinates.length; }, }, /** * The atlas' globally unique identifier (GUID). * The GUID changes whenever the texture atlas is modified. * Classes that use a texture atlas should check if the GUID * has changed before processing the atlas data. * @memberof TextureAtlas.prototype * @type {String} */ guid: { get: function () { return this._guid; }, }, }); // Builds a larger texture and copies the old texture into the new one. function resizeAtlas(textureAtlas, image) { var context = textureAtlas._context; var numImages = textureAtlas.numberOfImages; var scalingFactor = 2.0; var borderWidthInPixels = textureAtlas._borderWidthInPixels; if (numImages > 0) { var oldAtlasWidth = textureAtlas._texture.width; var oldAtlasHeight = textureAtlas._texture.height; var atlasWidth = scalingFactor * (oldAtlasWidth + image.width + borderWidthInPixels); var atlasHeight = scalingFactor * (oldAtlasHeight + image.height + borderWidthInPixels); var widthRatio = oldAtlasWidth / atlasWidth; var heightRatio = oldAtlasHeight / atlasHeight; // Create new node structure, putting the old root node in the bottom left. var nodeBottomRight = new TextureAtlasNode( new Cartesian2(oldAtlasWidth + borderWidthInPixels, borderWidthInPixels), new Cartesian2(atlasWidth, oldAtlasHeight) ); var nodeBottomHalf = new TextureAtlasNode( new Cartesian2(), new Cartesian2(atlasWidth, oldAtlasHeight), textureAtlas._root, nodeBottomRight ); var nodeTopHalf = new TextureAtlasNode( new Cartesian2(borderWidthInPixels, oldAtlasHeight + borderWidthInPixels), new Cartesian2(atlasWidth, atlasHeight) ); var nodeMain = new TextureAtlasNode( new Cartesian2(), new Cartesian2(atlasWidth, atlasHeight), nodeBottomHalf, nodeTopHalf ); // Resize texture coordinates. for (var i = 0; i < textureAtlas._textureCoordinates.length; i++) { var texCoord = textureAtlas._textureCoordinates[i]; if (defined(texCoord)) { texCoord.x *= widthRatio; texCoord.y *= heightRatio; texCoord.width *= widthRatio; texCoord.height *= heightRatio; } } // Copy larger texture. var newTexture = new Texture({ context: textureAtlas._context, width: atlasWidth, height: atlasHeight, pixelFormat: textureAtlas._pixelFormat, }); var framebuffer = new Framebuffer({ context: context, colorTextures: [textureAtlas._texture], destroyAttachments: false, }); framebuffer._bind(); newTexture.copyFromFramebuffer(0, 0, 0, 0, atlasWidth, atlasHeight); framebuffer._unBind(); framebuffer.destroy(); textureAtlas._texture = textureAtlas._texture && textureAtlas._texture.destroy(); textureAtlas._texture = newTexture; textureAtlas._root = nodeMain; } else { // First image exceeds initialSize var initialWidth = scalingFactor * (image.width + 2 * borderWidthInPixels); var initialHeight = scalingFactor * (image.height + 2 * borderWidthInPixels); if (initialWidth < textureAtlas._initialSize.x) { initialWidth = textureAtlas._initialSize.x; } if (initialHeight < textureAtlas._initialSize.y) { initialHeight = textureAtlas._initialSize.y; } textureAtlas._texture = textureAtlas._texture && textureAtlas._texture.destroy(); textureAtlas._texture = new Texture({ context: textureAtlas._context, width: initialWidth, height: initialHeight, pixelFormat: textureAtlas._pixelFormat, }); textureAtlas._root = new TextureAtlasNode( new Cartesian2(borderWidthInPixels, borderWidthInPixels), new Cartesian2(initialWidth, initialHeight) ); } } // A recursive function that finds the best place to insert // a new image based on existing image 'nodes'. // Inspired by: http://blackpawn.com/texts/lightmaps/default.html function findNode(textureAtlas, node, image) { if (!defined(node)) { return undefined; } // If a leaf node if (!defined(node.childNode1) && !defined(node.childNode2)) { // Node already contains an image, don't add to it. if (defined(node.imageIndex)) { return undefined; } var nodeWidth = node.topRight.x - node.bottomLeft.x; var nodeHeight = node.topRight.y - node.bottomLeft.y; var widthDifference = nodeWidth - image.width; var heightDifference = nodeHeight - image.height; // Node is smaller than the image. if (widthDifference < 0 || heightDifference < 0) { return undefined; } // If the node is the same size as the image, return the node if (widthDifference === 0 && heightDifference === 0) { return node; } // Vertical split (childNode1 = left half, childNode2 = right half). if (widthDifference > heightDifference) { node.childNode1 = new TextureAtlasNode( new Cartesian2(node.bottomLeft.x, node.bottomLeft.y), new Cartesian2(node.bottomLeft.x + image.width, node.topRight.y) ); // Only make a second child if the border gives enough space. var childNode2BottomLeftX = node.bottomLeft.x + image.width + textureAtlas._borderWidthInPixels; if (childNode2BottomLeftX < node.topRight.x) { node.childNode2 = new TextureAtlasNode( new Cartesian2(childNode2BottomLeftX, node.bottomLeft.y), new Cartesian2(node.topRight.x, node.topRight.y) ); } } // Horizontal split (childNode1 = bottom half, childNode2 = top half). else { node.childNode1 = new TextureAtlasNode( new Cartesian2(node.bottomLeft.x, node.bottomLeft.y), new Cartesian2(node.topRight.x, node.bottomLeft.y + image.height) ); // Only make a second child if the border gives enough space. var childNode2BottomLeftY = node.bottomLeft.y + image.height + textureAtlas._borderWidthInPixels; if (childNode2BottomLeftY < node.topRight.y) { node.childNode2 = new TextureAtlasNode( new Cartesian2(node.bottomLeft.x, childNode2BottomLeftY), new Cartesian2(node.topRight.x, node.topRight.y) ); } } return findNode(textureAtlas, node.childNode1, image); } // If not a leaf node return ( findNode(textureAtlas, node.childNode1, image) || findNode(textureAtlas, node.childNode2, image) ); } // Adds image of given index to the texture atlas. Called from addImage and addImages. function addImage(textureAtlas, image, index) { var node = findNode(textureAtlas, textureAtlas._root, image); if (defined(node)) { // Found a node that can hold the image. node.imageIndex = index; // Add texture coordinate and write to texture var atlasWidth = textureAtlas._texture.width; var atlasHeight = textureAtlas._texture.height; var nodeWidth = node.topRight.x - node.bottomLeft.x; var nodeHeight = node.topRight.y - node.bottomLeft.y; var x = node.bottomLeft.x / atlasWidth; var y = node.bottomLeft.y / atlasHeight; var w = nodeWidth / atlasWidth; var h = nodeHeight / atlasHeight; textureAtlas._textureCoordinates[index] = new BoundingRectangle(x, y, w, h); textureAtlas._texture.copyFrom(image, node.bottomLeft.x, node.bottomLeft.y); } else { // No node found, must resize the texture atlas. resizeAtlas(textureAtlas, image); addImage(textureAtlas, image, index); } textureAtlas._guid = createGuid(); } /** * Adds an image to the atlas. If the image is already in the atlas, the atlas is unchanged and * the existing index is used. * * @param {String} id An identifier to detect whether the image already exists in the atlas. * @param {HTMLImageElement|HTMLCanvasElement|String|Resource|Promise|TextureAtlas.CreateImageCallback} image An image or canvas to add to the texture atlas, * or a URL to an Image, or a Promise for an image, or a function that creates an image. * @returns {Promise.} A Promise for the image index. */ TextureAtlas.prototype.addImage = function (id, image) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } if (!defined(image)) { throw new DeveloperError("image is required."); } //>>includeEnd('debug'); var indexPromise = this._idHash[id]; if (defined(indexPromise)) { // we're already aware of this source return indexPromise; } // not in atlas, create the promise for the index if (typeof image === "function") { // if image is a function, call it image = image(id); //>>includeStart('debug', pragmas.debug); if (!defined(image)) { throw new DeveloperError("image is required."); } //>>includeEnd('debug'); } else if (typeof image === "string" || image instanceof Resource) { // Get a resource var resource = Resource.createIfNeeded(image); image = resource.fetchImage(); } var that = this; indexPromise = when(image, function (image) { if (that.isDestroyed()) { return -1; } var index = that.numberOfImages; addImage(that, image, index); return index; }); // store the promise this._idHash[id] = indexPromise; return indexPromise; }; /** * Add a sub-region of an existing atlas image as additional image indices. * * @param {String} id The identifier of the existing image. * @param {BoundingRectangle} subRegion An {@link BoundingRectangle} sub-region measured in pixels from the bottom-left. * * @returns {Promise.} A Promise for the image index. */ TextureAtlas.prototype.addSubRegion = function (id, subRegion) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } if (!defined(subRegion)) { throw new DeveloperError("subRegion is required."); } //>>includeEnd('debug'); var indexPromise = this._idHash[id]; if (!defined(indexPromise)) { throw new RuntimeError( 'image with id "' + id + '" not found in the atlas.' ); } var that = this; return when(indexPromise, function (index) { if (index === -1) { // the atlas is destroyed return -1; } var atlasWidth = that._texture.width; var atlasHeight = that._texture.height; var numImages = that.numberOfImages; var baseRegion = that._textureCoordinates[index]; var x = baseRegion.x + subRegion.x / atlasWidth; var y = baseRegion.y + subRegion.y / atlasHeight; var w = subRegion.width / atlasWidth; var h = subRegion.height / atlasHeight; that._textureCoordinates.push(new BoundingRectangle(x, y, w, h)); that._guid = createGuid(); return numImages; }); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see TextureAtlas#destroy */ TextureAtlas.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * atlas = atlas && atlas.destroy(); * * @see TextureAtlas#isDestroyed */ TextureAtlas.prototype.destroy = function () { this._texture = this._texture && this._texture.destroy(); return destroyObject(this); }; var SHOW_INDEX$4 = Billboard.SHOW_INDEX; var POSITION_INDEX$4 = Billboard.POSITION_INDEX; var PIXEL_OFFSET_INDEX = Billboard.PIXEL_OFFSET_INDEX; var EYE_OFFSET_INDEX = Billboard.EYE_OFFSET_INDEX; var HORIZONTAL_ORIGIN_INDEX = Billboard.HORIZONTAL_ORIGIN_INDEX; var VERTICAL_ORIGIN_INDEX = Billboard.VERTICAL_ORIGIN_INDEX; var SCALE_INDEX = Billboard.SCALE_INDEX; var IMAGE_INDEX_INDEX = Billboard.IMAGE_INDEX_INDEX; var COLOR_INDEX$2 = Billboard.COLOR_INDEX; var ROTATION_INDEX = Billboard.ROTATION_INDEX; var ALIGNED_AXIS_INDEX = Billboard.ALIGNED_AXIS_INDEX; var SCALE_BY_DISTANCE_INDEX$2 = Billboard.SCALE_BY_DISTANCE_INDEX; var TRANSLUCENCY_BY_DISTANCE_INDEX$2 = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX; var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX; var DISTANCE_DISPLAY_CONDITION_INDEX$2 = Billboard.DISTANCE_DISPLAY_CONDITION; var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE; var TEXTURE_COORDINATE_BOUNDS = Billboard.TEXTURE_COORDINATE_BOUNDS; var SDF_INDEX = Billboard.SDF_INDEX; var NUMBER_OF_PROPERTIES$3 = Billboard.NUMBER_OF_PROPERTIES; var attributeLocations$4; var attributeLocationsBatched = { positionHighAndScale: 0, positionLowAndRotation: 1, compressedAttribute0: 2, // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates compressedAttribute1: 3, // aligned axis, translucency by distance, image width compressedAttribute2: 4, // image height, color, pick color, size in meters, valid aligned axis, 13 bits free eyeOffset: 5, // 4 bytes free scaleByDistance: 6, pixelOffsetScaleByDistance: 7, compressedAttribute3: 8, textureCoordinateBoundsOrLabelTranslate: 9, a_batchId: 10, sdf: 11, }; var attributeLocationsInstanced = { direction: 0, positionHighAndScale: 1, positionLowAndRotation: 2, // texture offset in w compressedAttribute0: 3, compressedAttribute1: 4, compressedAttribute2: 5, eyeOffset: 6, // texture range in w scaleByDistance: 7, pixelOffsetScaleByDistance: 8, compressedAttribute3: 9, textureCoordinateBoundsOrLabelTranslate: 10, a_batchId: 11, sdf: 12, }; /** * A renderable collection of billboards. Billboards are viewport-aligned * images positioned in the 3D scene. *

*
*
* Example billboards *
*

* Billboards are added and removed from the collection using {@link BillboardCollection#add} * and {@link BillboardCollection#remove}. Billboards in a collection automatically share textures * for images with the same identifier. * * @alias BillboardCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each billboard from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Scene} [options.scene] Must be passed in for billboards that use the height reference property or will be depth tested against the globe. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The billboard blending option. The default * is used for rendering both opaque and translucent billboards. However, if either all of the billboards are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x. * @param {Boolean} [options.show=true] Determines if the billboards in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many billboards, to * many collections with only a few billboards each. Organize collections so that billboards * with the same update frequency are in the same collection, i.e., billboards that do not * change should be in one collection; billboards that change every frame should be in another * collection; and so on. * * @see BillboardCollection#add * @see BillboardCollection#remove * @see Billboard * @see LabelCollection * * @demo {@link https://sandcastle.cesium.com/index.html?src=Billboards.html|Cesium Sandcastle Billboard Demo} * * @example * // Create a billboard collection with two billboards * var billboards = scene.primitives.add(new Cesium.BillboardCollection()); * billboards.add({ * position : new Cesium.Cartesian3(1.0, 2.0, 3.0), * image : 'url/to/image' * }); * billboards.add({ * position : new Cesium.Cartesian3(4.0, 5.0, 6.0), * image : 'url/to/another/image' * }); */ function BillboardCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._scene = options.scene; this._batchTable = options.batchTable; this._textureAtlas = undefined; this._textureAtlasGUID = undefined; this._destroyTextureAtlas = true; this._sp = undefined; this._spTranslucent = undefined; this._rsOpaque = undefined; this._rsTranslucent = undefined; this._vaf = undefined; this._billboards = []; this._billboardsToUpdate = []; this._billboardsToUpdateIndex = 0; this._billboardsRemoved = false; this._createVertexArray = false; this._shaderRotation = false; this._compiledShaderRotation = false; this._shaderAlignedAxis = false; this._compiledShaderAlignedAxis = false; this._shaderScaleByDistance = false; this._compiledShaderScaleByDistance = false; this._shaderTranslucencyByDistance = false; this._compiledShaderTranslucencyByDistance = false; this._shaderPixelOffsetScaleByDistance = false; this._compiledShaderPixelOffsetScaleByDistance = false; this._shaderDistanceDisplayCondition = false; this._compiledShaderDistanceDisplayCondition = false; this._shaderDisableDepthDistance = false; this._compiledShaderDisableDepthDistance = false; this._shaderClampToGround = false; this._compiledShaderClampToGround = false; this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES$3); this._maxSize = 0.0; this._maxEyeOffset = 0.0; this._maxScale = 1.0; this._maxPixelOffset = 0.0; this._allHorizontalCenter = true; this._allVerticalCenter = true; this._allSizedInMeters = true; this._baseVolume = new BoundingSphere(); this._baseVolumeWC = new BoundingSphere(); this._baseVolume2D = new BoundingSphere(); this._boundingVolume = new BoundingSphere(); this._boundingVolumeDirty = false; this._colorCommands = []; /** * Determines if billboards in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * The 4x4 transformation matrix that transforms each billboard in this collection from model to world coordinates. * When this is the identity matrix, the billboards are drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type {Matrix4} * @default {@link Matrix4.IDENTITY} * * * @example * var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); * billboards.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); * billboards.add({ * image : 'url/to/image', * position : new Cesium.Cartesian3(0.0, 0.0, 0.0) // center * }); * billboards.add({ * image : 'url/to/image', * position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0) // east * }); * billboards.add({ * image : 'url/to/image', * position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0) // north * }); * billboards.add({ * image : 'url/to/image', * position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0) // up * }); * * @see Transforms.eastNorthUpToFixedFrame */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the texture atlas for this BillboardCollection as a fullscreen quad. *

* * @type {Boolean} * * @default false */ this.debugShowTextureAtlas = defaultValue( options.debugShowTextureAtlas, false ); /** * The billboard blending option. The default is used for rendering both opaque and translucent billboards. * However, if either all of the billboards are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve * performance by up to 2x. * @type {BlendOption} * @default BlendOption.OPAQUE_AND_TRANSLUCENT */ this.blendOption = defaultValue( options.blendOption, BlendOption$1.OPAQUE_AND_TRANSLUCENT ); this._blendOption = undefined; this._mode = SceneMode$1.SCENE3D; // The buffer usage for each attribute is determined based on the usage of the attribute over time. this._buffersUsage = [ BufferUsage$1.STATIC_DRAW, // SHOW_INDEX BufferUsage$1.STATIC_DRAW, // POSITION_INDEX BufferUsage$1.STATIC_DRAW, // PIXEL_OFFSET_INDEX BufferUsage$1.STATIC_DRAW, // EYE_OFFSET_INDEX BufferUsage$1.STATIC_DRAW, // HORIZONTAL_ORIGIN_INDEX BufferUsage$1.STATIC_DRAW, // VERTICAL_ORIGIN_INDEX BufferUsage$1.STATIC_DRAW, // SCALE_INDEX BufferUsage$1.STATIC_DRAW, // IMAGE_INDEX_INDEX BufferUsage$1.STATIC_DRAW, // COLOR_INDEX BufferUsage$1.STATIC_DRAW, // ROTATION_INDEX BufferUsage$1.STATIC_DRAW, // ALIGNED_AXIS_INDEX BufferUsage$1.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX BufferUsage$1.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX BufferUsage$1.STATIC_DRAW, // PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX BufferUsage$1.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX BufferUsage$1.STATIC_DRAW, // TEXTURE_COORDINATE_BOUNDS ]; this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints var that = this; this._uniforms = { u_atlas: function () { return that._textureAtlas.texture; }, u_highlightColor: function () { return that._highlightColor; }, }; var scene = this._scene; if (defined(scene) && defined(scene.terrainProviderChanged)) { this._removeCallbackFunc = scene.terrainProviderChanged.addEventListener( function () { var billboards = this._billboards; var length = billboards.length; for (var i = 0; i < length; ++i) { if (defined(billboards[i])) { billboards[i]._updateClamping(); } } }, this ); } } Object.defineProperties(BillboardCollection.prototype, { /** * Returns the number of billboards in this collection. This is commonly used with * {@link BillboardCollection#get} to iterate over all the billboards * in the collection. * @memberof BillboardCollection.prototype * @type {Number} */ length: { get: function () { removeBillboards(this); return this._billboards.length; }, }, /** * Gets or sets the textureAtlas. * @memberof BillboardCollection.prototype * @type {TextureAtlas} * @private */ textureAtlas: { get: function () { return this._textureAtlas; }, set: function (value) { if (this._textureAtlas !== value) { this._textureAtlas = this._destroyTextureAtlas && this._textureAtlas && this._textureAtlas.destroy(); this._textureAtlas = value; this._createVertexArray = true; // New per-billboard texture coordinates } }, }, /** * Gets or sets a value which determines if the texture atlas is * destroyed when the collection is destroyed. * * If the texture atlas is used by more than one collection, set this to false, * and explicitly destroy the atlas to avoid attempting to destroy it multiple times. * * @memberof BillboardCollection.prototype * @type {Boolean} * @private * * @example * // Set destroyTextureAtlas * // Destroy a billboard collection but not its texture atlas. * * var atlas = new TextureAtlas({ * scene : scene, * images : images * }); * billboards.textureAtlas = atlas; * billboards.destroyTextureAtlas = false; * billboards = billboards.destroy(); * console.log(atlas.isDestroyed()); // False */ destroyTextureAtlas: { get: function () { return this._destroyTextureAtlas; }, set: function (value) { this._destroyTextureAtlas = value; }, }, }); function destroyBillboards(billboards) { var length = billboards.length; for (var i = 0; i < length; ++i) { if (billboards[i]) { billboards[i]._destroy(); } } } /** * Creates and adds a billboard with the specified initial properties to the collection. * The added billboard is returned so it can be modified or removed from the collection later. * * @param {Object}[options] A template describing the billboard's properties as shown in Example 1. * @returns {Billboard} The billboard that was added to the collection. * * @performance Calling add is expected constant time. However, the collection's vertex buffer * is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For * best performance, add as many billboards as possible before calling update. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Example 1: Add a billboard, specifying all the default values. * var b = billboards.add({ * show : true, * position : Cesium.Cartesian3.ZERO, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, * heightReference : Cesium.HeightReference.NONE, * horizontalOrigin : Cesium.HorizontalOrigin.CENTER, * verticalOrigin : Cesium.VerticalOrigin.CENTER, * scale : 1.0, * image : 'url/to/image', * imageSubRegion : undefined, * color : Cesium.Color.WHITE, * id : undefined, * rotation : 0.0, * alignedAxis : Cesium.Cartesian3.ZERO, * width : undefined, * height : undefined, * scaleByDistance : undefined, * translucencyByDistance : undefined, * pixelOffsetScaleByDistance : undefined, * sizeInMeters : false, * distanceDisplayCondition : undefined * }); * * @example * // Example 2: Specify only the billboard's cartographic position. * var b = billboards.add({ * position : Cesium.Cartesian3.fromDegrees(longitude, latitude, height) * }); * * @see BillboardCollection#remove * @see BillboardCollection#removeAll */ BillboardCollection.prototype.add = function (options) { var b = new Billboard(options, this); b._index = this._billboards.length; this._billboards.push(b); this._createVertexArray = true; return b; }; /** * Removes a billboard from the collection. * * @param {Billboard} billboard The billboard to remove. * @returns {Boolean} true if the billboard was removed; false if the billboard was not found in the collection. * * @performance Calling remove is expected constant time. However, the collection's vertex buffer * is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For * best performance, remove as many billboards as possible before calling update. * If you intend to temporarily hide a billboard, it is usually more efficient to call * {@link Billboard#show} instead of removing and re-adding the billboard. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * var b = billboards.add(...); * billboards.remove(b); // Returns true * * @see BillboardCollection#add * @see BillboardCollection#removeAll * @see Billboard#show */ BillboardCollection.prototype.remove = function (billboard) { if (this.contains(billboard)) { this._billboards[billboard._index] = null; // Removed later this._billboardsRemoved = true; this._createVertexArray = true; billboard._destroy(); return true; } return false; }; /** * Removes all billboards from the collection. * * @performance O(n). It is more efficient to remove all the billboards * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * billboards.add(...); * billboards.add(...); * billboards.removeAll(); * * @see BillboardCollection#add * @see BillboardCollection#remove */ BillboardCollection.prototype.removeAll = function () { destroyBillboards(this._billboards); this._billboards = []; this._billboardsToUpdate = []; this._billboardsToUpdateIndex = 0; this._billboardsRemoved = false; this._createVertexArray = true; }; function removeBillboards(billboardCollection) { if (billboardCollection._billboardsRemoved) { billboardCollection._billboardsRemoved = false; var newBillboards = []; var billboards = billboardCollection._billboards; var length = billboards.length; for (var i = 0, j = 0; i < length; ++i) { var billboard = billboards[i]; if (billboard) { billboard._index = j++; newBillboards.push(billboard); } } billboardCollection._billboards = newBillboards; } } BillboardCollection.prototype._updateBillboard = function ( billboard, propertyChanged ) { if (!billboard._dirty) { this._billboardsToUpdate[this._billboardsToUpdateIndex++] = billboard; } ++this._propertiesChanged[propertyChanged]; }; /** * Check whether this collection contains a given billboard. * * @param {Billboard} [billboard] The billboard to check for. * @returns {Boolean} true if this collection contains the billboard, false otherwise. * * @see BillboardCollection#get */ BillboardCollection.prototype.contains = function (billboard) { return defined(billboard) && billboard._billboardCollection === this; }; /** * Returns the billboard in the collection at the specified index. Indices are zero-based * and increase as billboards are added. Removing a billboard shifts all billboards after * it to the left, changing their indices. This function is commonly used with * {@link BillboardCollection#length} to iterate over all the billboards * in the collection. * * @param {Number} index The zero-based index of the billboard. * @returns {Billboard} The billboard at the specified index. * * @performance Expected constant time. If billboards were removed from the collection and * {@link BillboardCollection#update} was not called, an implicit O(n) * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Toggle the show property of every billboard in the collection * var len = billboards.length; * for (var i = 0; i < len; ++i) { * var b = billboards.get(i); * b.show = !b.show; * } * * @see BillboardCollection#length */ BillboardCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); removeBillboards(this); return this._billboards[index]; }; var getIndexBuffer; function getIndexBufferBatched(context) { var sixteenK = 16 * 1024; var indexBuffer = context.cache.billboardCollection_indexBufferBatched; if (defined(indexBuffer)) { return indexBuffer; } // Subtract 6 because the last index is reserverd for primitive restart. // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18 var length = sixteenK * 6 - 6; var indices = new Uint16Array(length); for (var i = 0, j = 0; i < length; i += 6, j += 4) { indices[i] = j; indices[i + 1] = j + 1; indices[i + 2] = j + 2; indices[i + 3] = j + 0; indices[i + 4] = j + 2; indices[i + 5] = j + 3; } // PERFORMANCE_IDEA: Should we reference count billboard collections, and eventually delete this? // Is this too much memory to allocate up front? Should we dynamically grow it? indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: indices, usage: BufferUsage$1.STATIC_DRAW, indexDatatype: IndexDatatype$1.UNSIGNED_SHORT, }); indexBuffer.vertexArrayDestroyable = false; context.cache.billboardCollection_indexBufferBatched = indexBuffer; return indexBuffer; } function getIndexBufferInstanced(context) { var indexBuffer = context.cache.billboardCollection_indexBufferInstanced; if (defined(indexBuffer)) { return indexBuffer; } indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: new Uint16Array([0, 1, 2, 0, 2, 3]), usage: BufferUsage$1.STATIC_DRAW, indexDatatype: IndexDatatype$1.UNSIGNED_SHORT, }); indexBuffer.vertexArrayDestroyable = false; context.cache.billboardCollection_indexBufferInstanced = indexBuffer; return indexBuffer; } function getVertexBufferInstanced(context) { var vertexBuffer = context.cache.billboardCollection_vertexBufferInstanced; if (defined(vertexBuffer)) { return vertexBuffer; } vertexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: new Float32Array([0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]), usage: BufferUsage$1.STATIC_DRAW, }); vertexBuffer.vertexArrayDestroyable = false; context.cache.billboardCollection_vertexBufferInstanced = vertexBuffer; return vertexBuffer; } BillboardCollection.prototype.computeNewBuffersUsage = function () { var buffersUsage = this._buffersUsage; var usageChanged = false; var properties = this._propertiesChanged; for (var k = 0; k < NUMBER_OF_PROPERTIES$3; ++k) { var newUsage = properties[k] === 0 ? BufferUsage$1.STATIC_DRAW : BufferUsage$1.STREAM_DRAW; usageChanged = usageChanged || buffersUsage[k] !== newUsage; buffersUsage[k] = newUsage; } return usageChanged; }; function createVAF$1( context, numberOfBillboards, buffersUsage, instanced, batchTable, sdf ) { var attributes = [ { index: attributeLocations$4.positionHighAndScale, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[POSITION_INDEX$4], }, { index: attributeLocations$4.positionLowAndRotation, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[POSITION_INDEX$4], }, { index: attributeLocations$4.compressedAttribute0, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[PIXEL_OFFSET_INDEX], }, { index: attributeLocations$4.compressedAttribute1, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX$2], }, { index: attributeLocations$4.compressedAttribute2, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[COLOR_INDEX$2], }, { index: attributeLocations$4.eyeOffset, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[EYE_OFFSET_INDEX], }, { index: attributeLocations$4.scaleByDistance, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[SCALE_BY_DISTANCE_INDEX$2], }, { index: attributeLocations$4.pixelOffsetScaleByDistance, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX], }, { index: attributeLocations$4.compressedAttribute3, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX$2], }, { index: attributeLocations$4.textureCoordinateBoundsOrLabelTranslate, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[TEXTURE_COORDINATE_BOUNDS], }, ]; // Instancing requires one non-instanced attribute. if (instanced) { attributes.push({ index: attributeLocations$4.direction, componentsPerAttribute: 2, componentDatatype: ComponentDatatype$1.FLOAT, vertexBuffer: getVertexBufferInstanced(context), }); } if (defined(batchTable)) { attributes.push({ index: attributeLocations$4.a_batchId, componentsPerAttribute: 1, componentDatatype: ComponentDatatype$1.FLOAT, bufferUsage: BufferUsage$1.STATIC_DRAW, }); } if (sdf) { attributes.push({ index: attributeLocations$4.sdf, componentsPerAttribute: 2, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[SDF_INDEX], }); } // When instancing is enabled, only one vertex is needed for each billboard. var sizeInVertices = instanced ? numberOfBillboards : 4 * numberOfBillboards; return new VertexArrayFacade(context, attributes, sizeInVertices, instanced); } /////////////////////////////////////////////////////////////////////////// // Four vertices per billboard. Each has the same position, etc., but a different screen-space direction vector. // PERFORMANCE_IDEA: Save memory if a property is the same for all billboards, use a latched attribute state, // instead of storing it in a vertex buffer. var writePositionScratch$1 = new EncodedCartesian3(); function writePositionScaleAndRotation( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var positionHighWriter = vafWriters[attributeLocations$4.positionHighAndScale]; var positionLowWriter = vafWriters[attributeLocations$4.positionLowAndRotation]; var position = billboard._getActualPosition(); if (billboardCollection._mode === SceneMode$1.SCENE3D) { BoundingSphere.expand( billboardCollection._baseVolume, position, billboardCollection._baseVolume ); billboardCollection._boundingVolumeDirty = true; } EncodedCartesian3.fromCartesian(position, writePositionScratch$1); var scale = billboard.scale; var rotation = billboard.rotation; if (rotation !== 0.0) { billboardCollection._shaderRotation = true; } billboardCollection._maxScale = Math.max( billboardCollection._maxScale, scale ); var high = writePositionScratch$1.high; var low = writePositionScratch$1.low; if (billboardCollection._instanced) { i = billboard._index; positionHighWriter(i, high.x, high.y, high.z, scale); positionLowWriter(i, low.x, low.y, low.z, rotation); } else { i = billboard._index * 4; positionHighWriter(i + 0, high.x, high.y, high.z, scale); positionHighWriter(i + 1, high.x, high.y, high.z, scale); positionHighWriter(i + 2, high.x, high.y, high.z, scale); positionHighWriter(i + 3, high.x, high.y, high.z, scale); positionLowWriter(i + 0, low.x, low.y, low.z, rotation); positionLowWriter(i + 1, low.x, low.y, low.z, rotation); positionLowWriter(i + 2, low.x, low.y, low.z, rotation); positionLowWriter(i + 3, low.x, low.y, low.z, rotation); } } var scratchCartesian2$3 = new Cartesian2(); var UPPER_BOUND = 32768.0; // 2^15 var LEFT_SHIFT16$1 = 65536.0; // 2^16 var LEFT_SHIFT12 = 4096.0; // 2^12 var LEFT_SHIFT8$1 = 256.0; // 2^8 var LEFT_SHIFT7 = 128.0; var LEFT_SHIFT5 = 32.0; var LEFT_SHIFT3 = 8.0; var LEFT_SHIFT2 = 4.0; var RIGHT_SHIFT8 = 1.0 / 256.0; var LOWER_LEFT = 0.0; var LOWER_RIGHT = 2.0; var UPPER_RIGHT = 3.0; var UPPER_LEFT = 1.0; function writeCompressedAttrib0$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.compressedAttribute0]; var pixelOffset = billboard.pixelOffset; var pixelOffsetX = pixelOffset.x; var pixelOffsetY = pixelOffset.y; var translate = billboard._translate; var translateX = translate.x; var translateY = translate.y; billboardCollection._maxPixelOffset = Math.max( billboardCollection._maxPixelOffset, Math.abs(pixelOffsetX + translateX), Math.abs(-pixelOffsetY + translateY) ); var horizontalOrigin = billboard.horizontalOrigin; var verticalOrigin = billboard._verticalOrigin; var show = billboard.show && billboard.clusterShow; // If the color alpha is zero, do not show this billboard. This lets us avoid providing // color during the pick pass and also eliminates a discard in the fragment shader. if (billboard.color.alpha === 0.0) { show = false; } // Raw billboards don't distinguish between BASELINE and BOTTOM, only LabelCollection does that. if (verticalOrigin === VerticalOrigin$1.BASELINE) { verticalOrigin = VerticalOrigin$1.BOTTOM; } billboardCollection._allHorizontalCenter = billboardCollection._allHorizontalCenter && horizontalOrigin === HorizontalOrigin$1.CENTER; billboardCollection._allVerticalCenter = billboardCollection._allVerticalCenter && verticalOrigin === VerticalOrigin$1.CENTER; var bottomLeftX = 0; var bottomLeftY = 0; var width = 0; var height = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); bottomLeftX = imageRectangle.x; bottomLeftY = imageRectangle.y; width = imageRectangle.width; height = imageRectangle.height; } var topRightX = bottomLeftX + width; var topRightY = bottomLeftY + height; var compressed0 = Math.floor( CesiumMath.clamp(pixelOffsetX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND ) * LEFT_SHIFT7; compressed0 += (horizontalOrigin + 1.0) * LEFT_SHIFT5; compressed0 += (verticalOrigin + 1.0) * LEFT_SHIFT3; compressed0 += (show ? 1.0 : 0.0) * LEFT_SHIFT2; var compressed1 = Math.floor( CesiumMath.clamp(pixelOffsetY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND ) * LEFT_SHIFT8$1; var compressed2 = Math.floor( CesiumMath.clamp(translateX, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND ) * LEFT_SHIFT8$1; var tempTanslateY = (CesiumMath.clamp(translateY, -UPPER_BOUND, UPPER_BOUND) + UPPER_BOUND) * RIGHT_SHIFT8; var upperTranslateY = Math.floor(tempTanslateY); var lowerTranslateY = Math.floor( (tempTanslateY - upperTranslateY) * LEFT_SHIFT8$1 ); compressed1 += upperTranslateY; compressed2 += lowerTranslateY; scratchCartesian2$3.x = bottomLeftX; scratchCartesian2$3.y = bottomLeftY; var compressedTexCoordsLL = AttributeCompression.compressTextureCoordinates( scratchCartesian2$3 ); scratchCartesian2$3.x = topRightX; var compressedTexCoordsLR = AttributeCompression.compressTextureCoordinates( scratchCartesian2$3 ); scratchCartesian2$3.y = topRightY; var compressedTexCoordsUR = AttributeCompression.compressTextureCoordinates( scratchCartesian2$3 ); scratchCartesian2$3.x = bottomLeftX; var compressedTexCoordsUL = AttributeCompression.compressTextureCoordinates( scratchCartesian2$3 ); if (billboardCollection._instanced) { i = billboard._index; writer(i, compressed0, compressed1, compressed2, compressedTexCoordsLL); } else { i = billboard._index * 4; writer( i + 0, compressed0 + LOWER_LEFT, compressed1, compressed2, compressedTexCoordsLL ); writer( i + 1, compressed0 + LOWER_RIGHT, compressed1, compressed2, compressedTexCoordsLR ); writer( i + 2, compressed0 + UPPER_RIGHT, compressed1, compressed2, compressedTexCoordsUR ); writer( i + 3, compressed0 + UPPER_LEFT, compressed1, compressed2, compressedTexCoordsUL ); } } function writeCompressedAttrib1$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.compressedAttribute1]; var alignedAxis = billboard.alignedAxis; if (!Cartesian3.equals(alignedAxis, Cartesian3.ZERO)) { billboardCollection._shaderAlignedAxis = true; } var near = 0.0; var nearValue = 1.0; var far = 1.0; var farValue = 1.0; var translucency = billboard.translucencyByDistance; if (defined(translucency)) { near = translucency.near; nearValue = translucency.nearValue; far = translucency.far; farValue = translucency.farValue; if (nearValue !== 1.0 || farValue !== 1.0) { // translucency by distance calculation in shader need not be enabled // until a billboard with near and far !== 1.0 is found billboardCollection._shaderTranslucencyByDistance = true; } } var width = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); width = imageRectangle.width; } var textureWidth = billboardCollection._textureAtlas.texture.width; var imageWidth = Math.round( defaultValue(billboard.width, textureWidth * width) ); billboardCollection._maxSize = Math.max( billboardCollection._maxSize, imageWidth ); var compressed0 = CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT16$1); var compressed1 = 0.0; if ( Math.abs(Cartesian3.magnitudeSquared(alignedAxis) - 1.0) < CesiumMath.EPSILON6 ) { compressed1 = AttributeCompression.octEncodeFloat(alignedAxis); } nearValue = CesiumMath.clamp(nearValue, 0.0, 1.0); nearValue = nearValue === 1.0 ? 255.0 : (nearValue * 255.0) | 0; compressed0 = compressed0 * LEFT_SHIFT8$1 + nearValue; farValue = CesiumMath.clamp(farValue, 0.0, 1.0); farValue = farValue === 1.0 ? 255.0 : (farValue * 255.0) | 0; compressed1 = compressed1 * LEFT_SHIFT8$1 + farValue; if (billboardCollection._instanced) { i = billboard._index; writer(i, compressed0, compressed1, near, far); } else { i = billboard._index * 4; writer(i + 0, compressed0, compressed1, near, far); writer(i + 1, compressed0, compressed1, near, far); writer(i + 2, compressed0, compressed1, near, far); writer(i + 3, compressed0, compressed1, near, far); } } function writeCompressedAttrib2( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.compressedAttribute2]; var color = billboard.color; var pickColor = !defined(billboardCollection._batchTable) ? billboard.getPickId(frameState.context).color : Color.WHITE; var sizeInMeters = billboard.sizeInMeters ? 1.0 : 0.0; var validAlignedAxis = Math.abs(Cartesian3.magnitudeSquared(billboard.alignedAxis) - 1.0) < CesiumMath.EPSILON6 ? 1.0 : 0.0; billboardCollection._allSizedInMeters = billboardCollection._allSizedInMeters && sizeInMeters === 1.0; var height = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); height = imageRectangle.height; } var dimensions = billboardCollection._textureAtlas.texture.dimensions; var imageHeight = Math.round( defaultValue(billboard.height, dimensions.y * height) ); billboardCollection._maxSize = Math.max( billboardCollection._maxSize, imageHeight ); var labelHorizontalOrigin = defaultValue( billboard._labelHorizontalOrigin, -2 ); labelHorizontalOrigin += 2; var compressed3 = imageHeight * LEFT_SHIFT2 + labelHorizontalOrigin; var red = Color.floatToByte(color.red); var green = Color.floatToByte(color.green); var blue = Color.floatToByte(color.blue); var compressed0 = red * LEFT_SHIFT16$1 + green * LEFT_SHIFT8$1 + blue; red = Color.floatToByte(pickColor.red); green = Color.floatToByte(pickColor.green); blue = Color.floatToByte(pickColor.blue); var compressed1 = red * LEFT_SHIFT16$1 + green * LEFT_SHIFT8$1 + blue; var compressed2 = Color.floatToByte(color.alpha) * LEFT_SHIFT16$1 + Color.floatToByte(pickColor.alpha) * LEFT_SHIFT8$1; compressed2 += sizeInMeters * 2.0 + validAlignedAxis; if (billboardCollection._instanced) { i = billboard._index; writer(i, compressed0, compressed1, compressed2, compressed3); } else { i = billboard._index * 4; writer(i + 0, compressed0, compressed1, compressed2, compressed3); writer(i + 1, compressed0, compressed1, compressed2, compressed3); writer(i + 2, compressed0, compressed1, compressed2, compressed3); writer(i + 3, compressed0, compressed1, compressed2, compressed3); } } function writeEyeOffset( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.eyeOffset]; var eyeOffset = billboard.eyeOffset; // For billboards that are clamped to ground, move it slightly closer to the camera var eyeOffsetZ = eyeOffset.z; if (billboard._heightReference !== HeightReference$1.NONE) { eyeOffsetZ *= 1.005; } billboardCollection._maxEyeOffset = Math.max( billboardCollection._maxEyeOffset, Math.abs(eyeOffset.x), Math.abs(eyeOffset.y), Math.abs(eyeOffsetZ) ); if (billboardCollection._instanced) { var width = 0; var height = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); width = imageRectangle.width; height = imageRectangle.height; } scratchCartesian2$3.x = width; scratchCartesian2$3.y = height; var compressedTexCoordsRange = AttributeCompression.compressTextureCoordinates( scratchCartesian2$3 ); i = billboard._index; writer(i, eyeOffset.x, eyeOffset.y, eyeOffsetZ, compressedTexCoordsRange); } else { i = billboard._index * 4; writer(i + 0, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0); writer(i + 1, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0); writer(i + 2, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0); writer(i + 3, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0); } } function writeScaleByDistance$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.scaleByDistance]; var near = 0.0; var nearValue = 1.0; var far = 1.0; var farValue = 1.0; var scale = billboard.scaleByDistance; if (defined(scale)) { near = scale.near; nearValue = scale.nearValue; far = scale.far; farValue = scale.farValue; if (nearValue !== 1.0 || farValue !== 1.0) { // scale by distance calculation in shader need not be enabled // until a billboard with near and far !== 1.0 is found billboardCollection._shaderScaleByDistance = true; } } if (billboardCollection._instanced) { i = billboard._index; writer(i, near, nearValue, far, farValue); } else { i = billboard._index * 4; writer(i + 0, near, nearValue, far, farValue); writer(i + 1, near, nearValue, far, farValue); writer(i + 2, near, nearValue, far, farValue); writer(i + 3, near, nearValue, far, farValue); } } function writePixelOffsetScaleByDistance( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.pixelOffsetScaleByDistance]; var near = 0.0; var nearValue = 1.0; var far = 1.0; var farValue = 1.0; var pixelOffsetScale = billboard.pixelOffsetScaleByDistance; if (defined(pixelOffsetScale)) { near = pixelOffsetScale.near; nearValue = pixelOffsetScale.nearValue; far = pixelOffsetScale.far; farValue = pixelOffsetScale.farValue; if (nearValue !== 1.0 || farValue !== 1.0) { // pixelOffsetScale by distance calculation in shader need not be enabled // until a billboard with near and far !== 1.0 is found billboardCollection._shaderPixelOffsetScaleByDistance = true; } } if (billboardCollection._instanced) { i = billboard._index; writer(i, near, nearValue, far, farValue); } else { i = billboard._index * 4; writer(i + 0, near, nearValue, far, farValue); writer(i + 1, near, nearValue, far, farValue); writer(i + 2, near, nearValue, far, farValue); writer(i + 3, near, nearValue, far, farValue); } } function writeCompressedAttribute3( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { var i; var writer = vafWriters[attributeLocations$4.compressedAttribute3]; var near = 0.0; var far = Number.MAX_VALUE; var distanceDisplayCondition = billboard.distanceDisplayCondition; if (defined(distanceDisplayCondition)) { near = distanceDisplayCondition.near; far = distanceDisplayCondition.far; near *= near; far *= far; billboardCollection._shaderDistanceDisplayCondition = true; } var disableDepthTestDistance = billboard.disableDepthTestDistance; var clampToGround = billboard.heightReference === HeightReference$1.CLAMP_TO_GROUND && frameState.context.depthTexture; if (!defined(disableDepthTestDistance)) { disableDepthTestDistance = clampToGround ? 5000.0 : 0.0; } disableDepthTestDistance *= disableDepthTestDistance; if (clampToGround || disableDepthTestDistance > 0.0) { billboardCollection._shaderDisableDepthDistance = true; if (disableDepthTestDistance === Number.POSITIVE_INFINITY) { disableDepthTestDistance = -1.0; } } var imageHeight; var imageWidth; if (!defined(billboard._labelDimensions)) { var height = 0; var width = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); height = imageRectangle.height; width = imageRectangle.width; } imageHeight = Math.round( defaultValue( billboard.height, billboardCollection._textureAtlas.texture.dimensions.y * height ) ); var textureWidth = billboardCollection._textureAtlas.texture.width; imageWidth = Math.round( defaultValue(billboard.width, textureWidth * width) ); } else { imageWidth = billboard._labelDimensions.x; imageHeight = billboard._labelDimensions.y; } var w = Math.floor(CesiumMath.clamp(imageWidth, 0.0, LEFT_SHIFT12)); var h = Math.floor(CesiumMath.clamp(imageHeight, 0.0, LEFT_SHIFT12)); var dimensions = w * LEFT_SHIFT12 + h; if (billboardCollection._instanced) { i = billboard._index; writer(i, near, far, disableDepthTestDistance, dimensions); } else { i = billboard._index * 4; writer(i + 0, near, far, disableDepthTestDistance, dimensions); writer(i + 1, near, far, disableDepthTestDistance, dimensions); writer(i + 2, near, far, disableDepthTestDistance, dimensions); writer(i + 3, near, far, disableDepthTestDistance, dimensions); } } function writeTextureCoordinateBoundsOrLabelTranslate( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { if (billboard.heightReference === HeightReference$1.CLAMP_TO_GROUND) { var scene = billboardCollection._scene; var context = frameState.context; var globeTranslucent = frameState.globeTranslucencyState.translucent; var depthTestAgainstTerrain = defined(scene.globe) && scene.globe.depthTestAgainstTerrain; // Only do manual depth test if the globe is opaque and writes depth billboardCollection._shaderClampToGround = context.depthTexture && !globeTranslucent && depthTestAgainstTerrain; } var i; var writer = vafWriters[attributeLocations$4.textureCoordinateBoundsOrLabelTranslate]; if (ContextLimits.maximumVertexTextureImageUnits > 0) { //write _labelTranslate, used by depth testing in the vertex shader var translateX = 0; var translateY = 0; if (defined(billboard._labelTranslate)) { translateX = billboard._labelTranslate.x; translateY = billboard._labelTranslate.y; } if (billboardCollection._instanced) { i = billboard._index; writer(i, translateX, translateY, 0.0, 0.0); } else { i = billboard._index * 4; writer(i + 0, translateX, translateY, 0.0, 0.0); writer(i + 1, translateX, translateY, 0.0, 0.0); writer(i + 2, translateX, translateY, 0.0, 0.0); writer(i + 3, translateX, translateY, 0.0, 0.0); } return; } //write texture coordinate bounds, used by depth testing in fragment shader var minX = 0; var minY = 0; var width = 0; var height = 0; var index = billboard._imageIndex; if (index !== -1) { var imageRectangle = textureAtlasCoordinates[index]; //>>includeStart('debug', pragmas.debug); if (!defined(imageRectangle)) { throw new DeveloperError("Invalid billboard image index: " + index); } //>>includeEnd('debug'); minX = imageRectangle.x; minY = imageRectangle.y; width = imageRectangle.width; height = imageRectangle.height; } var maxX = minX + width; var maxY = minY + height; if (billboardCollection._instanced) { i = billboard._index; writer(i, minX, minY, maxX, maxY); } else { i = billboard._index * 4; writer(i + 0, minX, minY, maxX, maxY); writer(i + 1, minX, minY, maxX, maxY); writer(i + 2, minX, minY, maxX, maxY); writer(i + 3, minX, minY, maxX, maxY); } } function writeBatchId( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { if (!defined(billboardCollection._batchTable)) { return; } var writer = vafWriters[attributeLocations$4.a_batchId]; var id = billboard._batchIndex; var i; if (billboardCollection._instanced) { i = billboard._index; writer(i, id); } else { i = billboard._index * 4; writer(i + 0, id); writer(i + 1, id); writer(i + 2, id); writer(i + 3, id); } } function writeSDF( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { if (!billboardCollection._sdf) { return; } var i; var writer = vafWriters[attributeLocations$4.sdf]; var outlineColor = billboard.outlineColor; var outlineWidth = billboard.outlineWidth; var red = Color.floatToByte(outlineColor.red); var green = Color.floatToByte(outlineColor.green); var blue = Color.floatToByte(outlineColor.blue); var compressed0 = red * LEFT_SHIFT16$1 + green * LEFT_SHIFT8$1 + blue; // Compute the relative outline distance var outlineDistance = outlineWidth / SDFSettings$1.RADIUS; var compressed1 = Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT16$1 + Color.floatToByte(outlineDistance) * LEFT_SHIFT8$1; if (billboardCollection._instanced) { i = billboard._index; writer(i, compressed0, compressed1); } else { i = billboard._index * 4; writer(i + 0, compressed0 + LOWER_LEFT, compressed1); writer(i + 1, compressed0 + LOWER_RIGHT, compressed1); writer(i + 2, compressed0 + UPPER_RIGHT, compressed1); writer(i + 3, compressed0 + UPPER_LEFT, compressed1); } } function writeBillboard( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ) { writePositionScaleAndRotation( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeCompressedAttrib0$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeCompressedAttrib1$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeCompressedAttrib2( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeEyeOffset( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeScaleByDistance$1( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writePixelOffsetScaleByDistance( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeCompressedAttribute3( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeTextureCoordinateBoundsOrLabelTranslate( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeBatchId( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); writeSDF( billboardCollection, frameState, textureAtlasCoordinates, vafWriters, billboard ); } function recomputeActualPositions$1( billboardCollection, billboards, length, frameState, modelMatrix, recomputeBoundingVolume ) { var boundingVolume; if (frameState.mode === SceneMode$1.SCENE3D) { boundingVolume = billboardCollection._baseVolume; billboardCollection._boundingVolumeDirty = true; } else { boundingVolume = billboardCollection._baseVolume2D; } var positions = []; for (var i = 0; i < length; ++i) { var billboard = billboards[i]; var position = billboard.position; var actualPosition = Billboard._computeActualPosition( billboard, position, frameState, modelMatrix ); if (defined(actualPosition)) { billboard._setActualPosition(actualPosition); if (recomputeBoundingVolume) { positions.push(actualPosition); } else { BoundingSphere.expand(boundingVolume, actualPosition, boundingVolume); } } } if (recomputeBoundingVolume) { BoundingSphere.fromPoints(positions, boundingVolume); } } function updateMode$2(billboardCollection, frameState) { var mode = frameState.mode; var billboards = billboardCollection._billboards; var billboardsToUpdate = billboardCollection._billboardsToUpdate; var modelMatrix = billboardCollection._modelMatrix; if ( billboardCollection._createVertexArray || billboardCollection._mode !== mode || (mode !== SceneMode$1.SCENE3D && !Matrix4.equals(modelMatrix, billboardCollection.modelMatrix)) ) { billboardCollection._mode = mode; Matrix4.clone(billboardCollection.modelMatrix, modelMatrix); billboardCollection._createVertexArray = true; if ( mode === SceneMode$1.SCENE3D || mode === SceneMode$1.SCENE2D || mode === SceneMode$1.COLUMBUS_VIEW ) { recomputeActualPositions$1( billboardCollection, billboards, billboards.length, frameState, modelMatrix, true ); } } else if (mode === SceneMode$1.MORPHING) { recomputeActualPositions$1( billboardCollection, billboards, billboards.length, frameState, modelMatrix, true ); } else if (mode === SceneMode$1.SCENE2D || mode === SceneMode$1.COLUMBUS_VIEW) { recomputeActualPositions$1( billboardCollection, billboardsToUpdate, billboardCollection._billboardsToUpdateIndex, frameState, modelMatrix, false ); } } function updateBoundingVolume$1(collection, frameState, boundingVolume) { var pixelScale = 1.0; if (!collection._allSizedInMeters || collection._maxPixelOffset !== 0.0) { pixelScale = frameState.camera.getPixelSize( boundingVolume, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight ); } var size = pixelScale * collection._maxScale * collection._maxSize * 2.0; if (collection._allHorizontalCenter && collection._allVerticalCenter) { size *= 0.5; } var offset = pixelScale * collection._maxPixelOffset + collection._maxEyeOffset; boundingVolume.radius += size + offset; } function createDebugCommand(billboardCollection, context) { var fs; fs = "uniform sampler2D billboard_texture; \n" + "varying vec2 v_textureCoordinates; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(billboard_texture, v_textureCoordinates); \n" + "} \n"; var drawCommand = context.createViewportQuadCommand(fs, { uniformMap: { billboard_texture: function () { return billboardCollection._textureAtlas.texture; }, }, }); drawCommand.pass = Pass$1.OVERLAY; return drawCommand; } var scratchWriterArray$1 = []; /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {RuntimeError} image with id must be in the atlas. */ BillboardCollection.prototype.update = function (frameState) { removeBillboards(this); if (!this.show) { return; } var billboards = this._billboards; var billboardsLength = billboards.length; var context = frameState.context; this._instanced = context.instancedArrays; attributeLocations$4 = this._instanced ? attributeLocationsInstanced : attributeLocationsBatched; getIndexBuffer = this._instanced ? getIndexBufferInstanced : getIndexBufferBatched; var textureAtlas = this._textureAtlas; if (!defined(textureAtlas)) { textureAtlas = this._textureAtlas = new TextureAtlas({ context: context, }); for (var ii = 0; ii < billboardsLength; ++ii) { billboards[ii]._loadImage(); } } var textureAtlasCoordinates = textureAtlas.textureCoordinates; if (textureAtlasCoordinates.length === 0) { // Can't write billboard vertices until we have texture coordinates // provided by a texture atlas return; } updateMode$2(this, frameState); billboards = this._billboards; billboardsLength = billboards.length; var billboardsToUpdate = this._billboardsToUpdate; var billboardsToUpdateLength = this._billboardsToUpdateIndex; var properties = this._propertiesChanged; var textureAtlasGUID = textureAtlas.guid; var createVertexArray = this._createVertexArray || this._textureAtlasGUID !== textureAtlasGUID; this._textureAtlasGUID = textureAtlasGUID; var vafWriters; var pass = frameState.passes; var picking = pass.pick; // PERFORMANCE_IDEA: Round robin multiple buffers. if (createVertexArray || (!picking && this.computeNewBuffersUsage())) { this._createVertexArray = false; for (var k = 0; k < NUMBER_OF_PROPERTIES$3; ++k) { properties[k] = 0; } this._vaf = this._vaf && this._vaf.destroy(); if (billboardsLength > 0) { // PERFORMANCE_IDEA: Instead of creating a new one, resize like std::vector. this._vaf = createVAF$1( context, billboardsLength, this._buffersUsage, this._instanced, this._batchTable, this._sdf ); vafWriters = this._vaf.writers; // Rewrite entire buffer if billboards were added or removed. for (var i = 0; i < billboardsLength; ++i) { var billboard = this._billboards[i]; billboard._dirty = false; // In case it needed an update. writeBillboard( this, frameState, textureAtlasCoordinates, vafWriters, billboard ); } // Different billboard collections share the same index buffer. this._vaf.commit(getIndexBuffer(context)); } this._billboardsToUpdateIndex = 0; } else if (billboardsToUpdateLength > 0) { // Billboards were modified, but none were added or removed. var writers = scratchWriterArray$1; writers.length = 0; if ( properties[POSITION_INDEX$4] || properties[ROTATION_INDEX] || properties[SCALE_INDEX] ) { writers.push(writePositionScaleAndRotation); } if ( properties[IMAGE_INDEX_INDEX] || properties[PIXEL_OFFSET_INDEX] || properties[HORIZONTAL_ORIGIN_INDEX] || properties[VERTICAL_ORIGIN_INDEX] || properties[SHOW_INDEX$4] ) { writers.push(writeCompressedAttrib0$1); if (this._instanced) { writers.push(writeEyeOffset); } } if ( properties[IMAGE_INDEX_INDEX] || properties[ALIGNED_AXIS_INDEX] || properties[TRANSLUCENCY_BY_DISTANCE_INDEX$2] ) { writers.push(writeCompressedAttrib1$1); writers.push(writeCompressedAttrib2); } if (properties[IMAGE_INDEX_INDEX] || properties[COLOR_INDEX$2]) { writers.push(writeCompressedAttrib2); } if (properties[EYE_OFFSET_INDEX]) { writers.push(writeEyeOffset); } if (properties[SCALE_BY_DISTANCE_INDEX$2]) { writers.push(writeScaleByDistance$1); } if (properties[PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX]) { writers.push(writePixelOffsetScaleByDistance); } if ( properties[DISTANCE_DISPLAY_CONDITION_INDEX$2] || properties[DISABLE_DEPTH_DISTANCE] || properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX$4] ) { writers.push(writeCompressedAttribute3); } if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX$4]) { writers.push(writeTextureCoordinateBoundsOrLabelTranslate); } if (properties[SDF_INDEX]) { writers.push(writeSDF); } var numWriters = writers.length; vafWriters = this._vaf.writers; if (billboardsToUpdateLength / billboardsLength > 0.1) { // If more than 10% of billboard change, rewrite the entire buffer. // PERFORMANCE_IDEA: I totally made up 10% :). for (var m = 0; m < billboardsToUpdateLength; ++m) { var b = billboardsToUpdate[m]; b._dirty = false; for (var n = 0; n < numWriters; ++n) { writers[n](this, frameState, textureAtlasCoordinates, vafWriters, b); } } this._vaf.commit(getIndexBuffer(context)); } else { for (var h = 0; h < billboardsToUpdateLength; ++h) { var bb = billboardsToUpdate[h]; bb._dirty = false; for (var o = 0; o < numWriters; ++o) { writers[o](this, frameState, textureAtlasCoordinates, vafWriters, bb); } if (this._instanced) { this._vaf.subCommit(bb._index, 1); } else { this._vaf.subCommit(bb._index * 4, 4); } } this._vaf.endSubCommits(); } this._billboardsToUpdateIndex = 0; } // If the number of total billboards ever shrinks considerably // Truncate billboardsToUpdate so that we free memory that we're // not going to be using. if (billboardsToUpdateLength > billboardsLength * 1.5) { billboardsToUpdate.length = billboardsLength; } if (!defined(this._vaf) || !defined(this._vaf.va)) { return; } if (this._boundingVolumeDirty) { this._boundingVolumeDirty = false; BoundingSphere.transform( this._baseVolume, this.modelMatrix, this._baseVolumeWC ); } var boundingVolume; var modelMatrix = Matrix4.IDENTITY; if (frameState.mode === SceneMode$1.SCENE3D) { modelMatrix = this.modelMatrix; boundingVolume = BoundingSphere.clone( this._baseVolumeWC, this._boundingVolume ); } else { boundingVolume = BoundingSphere.clone( this._baseVolume2D, this._boundingVolume ); } updateBoundingVolume$1(this, frameState, boundingVolume); var blendOptionChanged = this._blendOption !== this.blendOption; this._blendOption = this.blendOption; if (blendOptionChanged) { if ( this._blendOption === BlendOption$1.OPAQUE || this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT ) { this._rsOpaque = RenderState.fromCache({ depthTest: { enabled: true, func: WebGLConstants$1.LESS, }, depthMask: true, }); } else { this._rsOpaque = undefined; } // If OPAQUE_AND_TRANSLUCENT is in use, only the opaque pass gets the benefit of the depth buffer, // not the translucent pass. Otherwise, if the TRANSLUCENT pass is on its own, it turns on // a depthMask in lieu of full depth sorting (because it has opaque-ish fragments that look bad in OIT). // When the TRANSLUCENT depth mask is in use, label backgrounds require the depth func to be LEQUAL. var useTranslucentDepthMask = this._blendOption === BlendOption$1.TRANSLUCENT; if ( this._blendOption === BlendOption$1.TRANSLUCENT || this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT ) { this._rsTranslucent = RenderState.fromCache({ depthTest: { enabled: true, func: useTranslucentDepthMask ? WebGLConstants$1.LEQUAL : WebGLConstants$1.LESS, }, depthMask: useTranslucentDepthMask, blending: BlendingState$1.ALPHA_BLEND, }); } else { this._rsTranslucent = undefined; } } this._shaderDisableDepthDistance = this._shaderDisableDepthDistance || frameState.minimumDisableDepthTestDistance !== 0.0; var vsSource; var fsSource; var vs; var fs; var vertDefines; var supportVSTextureReads = ContextLimits.maximumVertexTextureImageUnits > 0; if ( blendOptionChanged || this._shaderRotation !== this._compiledShaderRotation || this._shaderAlignedAxis !== this._compiledShaderAlignedAxis || this._shaderScaleByDistance !== this._compiledShaderScaleByDistance || this._shaderTranslucencyByDistance !== this._compiledShaderTranslucencyByDistance || this._shaderPixelOffsetScaleByDistance !== this._compiledShaderPixelOffsetScaleByDistance || this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayCondition || this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance || this._shaderClampToGround !== this._compiledShaderClampToGround || this._sdf !== this._compiledSDF ) { vsSource = BillboardCollectionVS; fsSource = BillboardCollectionFS; vertDefines = []; if (defined(this._batchTable)) { vertDefines.push("VECTOR_TILE"); vsSource = this._batchTable.getVertexShaderCallback( false, "a_batchId", undefined )(vsSource); fsSource = this._batchTable.getFragmentShaderCallback( false, undefined )(fsSource); } vs = new ShaderSource({ defines: vertDefines, sources: [vsSource], }); if (this._instanced) { vs.defines.push("INSTANCED"); } if (this._shaderRotation) { vs.defines.push("ROTATION"); } if (this._shaderAlignedAxis) { vs.defines.push("ALIGNED_AXIS"); } if (this._shaderScaleByDistance) { vs.defines.push("EYE_DISTANCE_SCALING"); } if (this._shaderTranslucencyByDistance) { vs.defines.push("EYE_DISTANCE_TRANSLUCENCY"); } if (this._shaderPixelOffsetScaleByDistance) { vs.defines.push("EYE_DISTANCE_PIXEL_OFFSET"); } if (this._shaderDistanceDisplayCondition) { vs.defines.push("DISTANCE_DISPLAY_CONDITION"); } if (this._shaderDisableDepthDistance) { vs.defines.push("DISABLE_DEPTH_DISTANCE"); } if (this._shaderClampToGround) { if (supportVSTextureReads) { vs.defines.push("VERTEX_DEPTH_CHECK"); } else { vs.defines.push("FRAGMENT_DEPTH_CHECK"); } } var sdfEdge = 1.0 - SDFSettings$1.CUTOFF; if (this._sdf) { vs.defines.push("SDF"); } var vectorFragDefine = defined(this._batchTable) ? "VECTOR_TILE" : ""; if (this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT) { fs = new ShaderSource({ defines: ["OPAQUE", vectorFragDefine], sources: [fsSource], }); if (this._shaderClampToGround) { if (supportVSTextureReads) { fs.defines.push("VERTEX_DEPTH_CHECK"); } else { fs.defines.push("FRAGMENT_DEPTH_CHECK"); } } if (this._sdf) { fs.defines.push("SDF"); fs.defines.push("SDF_EDGE " + sdfEdge); } this._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: this._sp, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$4, }); fs = new ShaderSource({ defines: ["TRANSLUCENT", vectorFragDefine], sources: [fsSource], }); if (this._shaderClampToGround) { if (supportVSTextureReads) { fs.defines.push("VERTEX_DEPTH_CHECK"); } else { fs.defines.push("FRAGMENT_DEPTH_CHECK"); } } if (this._sdf) { fs.defines.push("SDF"); fs.defines.push("SDF_EDGE " + sdfEdge); } this._spTranslucent = ShaderProgram.replaceCache({ context: context, shaderProgram: this._spTranslucent, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$4, }); } if (this._blendOption === BlendOption$1.OPAQUE) { fs = new ShaderSource({ defines: [vectorFragDefine], sources: [fsSource], }); if (this._shaderClampToGround) { if (supportVSTextureReads) { fs.defines.push("VERTEX_DEPTH_CHECK"); } else { fs.defines.push("FRAGMENT_DEPTH_CHECK"); } } if (this._sdf) { fs.defines.push("SDF"); fs.defines.push("SDF_EDGE " + sdfEdge); } this._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: this._sp, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$4, }); } if (this._blendOption === BlendOption$1.TRANSLUCENT) { fs = new ShaderSource({ defines: [vectorFragDefine], sources: [fsSource], }); if (this._shaderClampToGround) { if (supportVSTextureReads) { fs.defines.push("VERTEX_DEPTH_CHECK"); } else { fs.defines.push("FRAGMENT_DEPTH_CHECK"); } } if (this._sdf) { fs.defines.push("SDF"); fs.defines.push("SDF_EDGE " + sdfEdge); } this._spTranslucent = ShaderProgram.replaceCache({ context: context, shaderProgram: this._spTranslucent, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$4, }); } this._compiledShaderRotation = this._shaderRotation; this._compiledShaderAlignedAxis = this._shaderAlignedAxis; this._compiledShaderScaleByDistance = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; this._compiledShaderClampToGround = this._shaderClampToGround; this._compiledSDF = this._sdf; } var commandList = frameState.commandList; if (pass.render || pass.pick) { var colorList = this._colorCommands; var opaque = this._blendOption === BlendOption$1.OPAQUE; var opaqueAndTranslucent = this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT; var va = this._vaf.va; var vaLength = va.length; var uniforms = this._uniforms; var pickId; if (defined(this._batchTable)) { uniforms = this._batchTable.getUniformMapCallback()(uniforms); pickId = this._batchTable.getPickId(); } else { pickId = "v_pickColor"; } colorList.length = vaLength; var totalLength = opaqueAndTranslucent ? vaLength * 2 : vaLength; for (var j = 0; j < totalLength; ++j) { var command = colorList[j]; if (!defined(command)) { command = colorList[j] = new DrawCommand(); } var opaqueCommand = opaque || (opaqueAndTranslucent && j % 2 === 0); command.pass = opaqueCommand || !opaqueAndTranslucent ? Pass$1.OPAQUE : Pass$1.TRANSLUCENT; command.owner = this; var index = opaqueAndTranslucent ? Math.floor(j / 2.0) : j; command.boundingVolume = boundingVolume; command.modelMatrix = modelMatrix; command.count = va[index].indicesCount; command.shaderProgram = opaqueCommand ? this._sp : this._spTranslucent; command.uniformMap = uniforms; command.vertexArray = va[index].va; command.renderState = opaqueCommand ? this._rsOpaque : this._rsTranslucent; command.debugShowBoundingVolume = this.debugShowBoundingVolume; command.pickId = pickId; if (this._instanced) { command.count = 6; command.instanceCount = billboardsLength; } commandList.push(command); } if (this.debugShowTextureAtlas) { if (!defined(this.debugCommand)) { this.debugCommand = createDebugCommand(this, frameState.context); } commandList.push(this.debugCommand); } } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see BillboardCollection#destroy */ BillboardCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * billboards = billboards && billboards.destroy(); * * @see BillboardCollection#isDestroyed */ BillboardCollection.prototype.destroy = function () { if (defined(this._removeCallbackFunc)) { this._removeCallbackFunc(); this._removeCallbackFunc = undefined; } this._textureAtlas = this._destroyTextureAtlas && this._textureAtlas && this._textureAtlas.destroy(); this._sp = this._sp && this._sp.destroy(); this._spTranslucent = this._spTranslucent && this._spTranslucent.destroy(); this._vaf = this._vaf && this._vaf.destroy(); destroyBillboards(this._billboards); return destroyObject(this); }; /** * Creates a {@link createBillboardPointCallback.CanvasFunction} that will create a canvas with a point. * * @param {Number} centerAlpha The alpha of the center of the point. The value must be in the range [0.0, 1.0]. * @param {String} cssColor The CSS color string. * @param {String} cssOutlineColor The CSS color of the point outline. * @param {Number} cssOutlineWidth The width of the outline in pixels. * @param {Number} pixelSize The size of the point in pixels. * @return {createBillboardPointCallback.CanvasFunction} The function that will return a canvas with the point drawn on it. * * @private */ function createBillboardPointCallback( centerAlpha, cssColor, cssOutlineColor, cssOutlineWidth, pixelSize ) { return function () { var canvas = document.createElement("canvas"); var length = pixelSize + 2 * cssOutlineWidth; canvas.height = canvas.width = length; var context2D = canvas.getContext("2d"); context2D.clearRect(0, 0, length, length); if (cssOutlineWidth !== 0) { context2D.beginPath(); context2D.arc(length / 2, length / 2, length / 2, 0, 2 * Math.PI, true); context2D.closePath(); context2D.fillStyle = cssOutlineColor; context2D.fill(); // Punch a hole in the center if needed. if (centerAlpha < 1.0) { context2D.save(); context2D.globalCompositeOperation = "destination-out"; context2D.beginPath(); context2D.arc( length / 2, length / 2, pixelSize / 2, 0, 2 * Math.PI, true ); context2D.closePath(); context2D.fillStyle = "black"; context2D.fill(); context2D.restore(); } } context2D.beginPath(); context2D.arc(length / 2, length / 2, pixelSize / 2, 0, 2 * Math.PI, true); context2D.closePath(); context2D.fillStyle = cssColor; context2D.fill(); return canvas; }; } /** * A point feature of a {@link Cesium3DTileset}. *

* Provides access to a feature's properties stored in the tile's batch table, as well * as the ability to show/hide a feature and change its point properties *

*

* Modifications to a Cesium3DTilePointFeature object have the lifetime of the tile's * content. If the tile's content is unloaded, e.g., due to it going out of view and needing * to free space in the cache for visible tiles, listen to the {@link Cesium3DTileset#tileUnload} event to save any * modifications. Also listen to the {@link Cesium3DTileset#tileVisible} event to reapply any modifications. *

*

* Do not construct this directly. Access it through {@link Cesium3DTileContent#getFeature} * or picking using {@link Scene#pick} and {@link Scene#pickPosition}. *

* * @alias Cesium3DTilePointFeature * @constructor * * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. * * @example * // On mouse over, display all the properties for a feature in the console log. * handler.setInputAction(function(movement) { * var feature = scene.pick(movement.endPosition); * if (feature instanceof Cesium.Cesium3DTilePointFeature) { * var propertyNames = feature.getPropertyNames(); * var length = propertyNames.length; * for (var i = 0; i < length; ++i) { * var propertyName = propertyNames[i]; * console.log(propertyName + ': ' + feature.getProperty(propertyName)); * } * } * }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); */ function Cesium3DTilePointFeature( content, batchId, billboard, label, polyline ) { this._content = content; this._billboard = billboard; this._label = label; this._polyline = polyline; this._batchId = batchId; this._billboardImage = undefined; this._billboardColor = undefined; this._billboardOutlineColor = undefined; this._billboardOutlineWidth = undefined; this._billboardSize = undefined; this._pointSize = undefined; this._color = undefined; this._pointSize = undefined; this._pointOutlineColor = undefined; this._pointOutlineWidth = undefined; this._heightOffset = undefined; this._pickIds = new Array(3); setBillboardImage(this); } var scratchCartographic$a = new Cartographic(); Object.defineProperties(Cesium3DTilePointFeature.prototype, { /** * Gets or sets if the feature will be shown. This is set for all features * when a style's show is evaluated. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Boolean} * * @default true */ show: { get: function () { return this._label.show; }, set: function (value) { this._label.show = value; this._billboard.show = value; this._polyline.show = value; }, }, /** * Gets or sets the color of the point of this feature. *

* Only applied when image is undefined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ color: { get: function () { return this._color; }, set: function (value) { this._color = Color.clone(value, this._color); setBillboardImage(this); }, }, /** * Gets or sets the point size of this feature. *

* Only applied when image is undefined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Number} */ pointSize: { get: function () { return this._pointSize; }, set: function (value) { this._pointSize = value; setBillboardImage(this); }, }, /** * Gets or sets the point outline color of this feature. *

* Only applied when image is undefined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ pointOutlineColor: { get: function () { return this._pointOutlineColor; }, set: function (value) { this._pointOutlineColor = Color.clone(value, this._pointOutlineColor); setBillboardImage(this); }, }, /** * Gets or sets the point outline width in pixels of this feature. *

* Only applied when image is undefined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Number} */ pointOutlineWidth: { get: function () { return this._pointOutlineWidth; }, set: function (value) { this._pointOutlineWidth = value; setBillboardImage(this); }, }, /** * Gets or sets the label color of this feature. *

* The color will be applied to the label if labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ labelColor: { get: function () { return this._label.fillColor; }, set: function (value) { this._label.fillColor = value; this._polyline.show = this._label.show && value.alpha > 0.0; }, }, /** * Gets or sets the label outline color of this feature. *

* The outline color will be applied to the label if labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ labelOutlineColor: { get: function () { return this._label.outlineColor; }, set: function (value) { this._label.outlineColor = value; }, }, /** * Gets or sets the outline width in pixels of this feature. *

* The outline width will be applied to the point if labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Number} */ labelOutlineWidth: { get: function () { return this._label.outlineWidth; }, set: function (value) { this._label.outlineWidth = value; }, }, /** * Gets or sets the font of this feature. *

* Only applied when the labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {String} */ font: { get: function () { return this._label.font; }, set: function (value) { this._label.font = value; }, }, /** * Gets or sets the fill and outline style of this feature. *

* Only applied when labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {LabelStyle} */ labelStyle: { get: function () { return this._label.style; }, set: function (value) { this._label.style = value; }, }, /** * Gets or sets the text for this feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {String} */ labelText: { get: function () { return this._label.text; }, set: function (value) { if (!defined(value)) { value = ""; } this._label.text = value; }, }, /** * Gets or sets the background color of the text for this feature. *

* Only applied when labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ backgroundColor: { get: function () { return this._label.backgroundColor; }, set: function (value) { this._label.backgroundColor = value; }, }, /** * Gets or sets the background padding of the text for this feature. *

* Only applied when labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Cartesian2} */ backgroundPadding: { get: function () { return this._label.backgroundPadding; }, set: function (value) { this._label.backgroundPadding = value; }, }, /** * Gets or sets whether to display the background of the text for this feature. *

* Only applied when labelText is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Boolean} */ backgroundEnabled: { get: function () { return this._label.showBackground; }, set: function (value) { this._label.showBackground = value; }, }, /** * Gets or sets the near and far scaling properties for this feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {NearFarScalar} */ scaleByDistance: { get: function () { return this._label.scaleByDistance; }, set: function (value) { this._label.scaleByDistance = value; this._billboard.scaleByDistance = value; }, }, /** * Gets or sets the near and far translucency properties for this feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {NearFarScalar} */ translucencyByDistance: { get: function () { return this._label.translucencyByDistance; }, set: function (value) { this._label.translucencyByDistance = value; this._billboard.translucencyByDistance = value; }, }, /** * Gets or sets the condition specifying at what distance from the camera that this feature will be displayed. * * @memberof Cesium3DTilePointFeature.prototype * * @type {DistanceDisplayCondition} */ distanceDisplayCondition: { get: function () { return this._label.distanceDisplayCondition; }, set: function (value) { this._label.distanceDisplayCondition = value; this._polyline.distanceDisplayCondition = value; this._billboard.distanceDisplayCondition = value; }, }, /** * Gets or sets the height offset in meters of this feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Number} */ heightOffset: { get: function () { return this._heightOffset; }, set: function (value) { var offset = defaultValue(this._heightOffset, 0.0); var ellipsoid = this._content.tileset.ellipsoid; var cart = ellipsoid.cartesianToCartographic( this._billboard.position, scratchCartographic$a ); cart.height = cart.height - offset + value; var newPosition = ellipsoid.cartographicToCartesian(cart); this._billboard.position = newPosition; this._label.position = this._billboard.position; this._polyline.positions = [this._polyline.positions[0], newPosition]; this._heightOffset = value; }, }, /** * Gets or sets whether the anchor line is displayed. *

* Only applied when heightOffset is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Boolean} */ anchorLineEnabled: { get: function () { return this._polyline.show; }, set: function (value) { this._polyline.show = value; }, }, /** * Gets or sets the color for the anchor line. *

* Only applied when heightOffset is defined. *

* * @memberof Cesium3DTilePointFeature.prototype * * @type {Color} */ anchorLineColor: { get: function () { return this._polyline.material.uniforms.color; }, set: function (value) { this._polyline.material.uniforms.color = Color.clone( value, this._polyline.material.uniforms.color ); }, }, /** * Gets or sets the image of this feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {String} */ image: { get: function () { return this._billboardImage; }, set: function (value) { var imageChanged = this._billboardImage !== value; this._billboardImage = value; if (imageChanged) { setBillboardImage(this); } }, }, /** * Gets or sets the distance where depth testing will be disabled. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Number} */ disableDepthTestDistance: { get: function () { return this._label.disableDepthTestDistance; }, set: function (value) { this._label.disableDepthTestDistance = value; this._billboard.disableDepthTestDistance = value; }, }, /** * Gets or sets the horizontal origin of this point, which determines if the point is * to the left, center, or right of its anchor position. * * @memberof Cesium3DTilePointFeature.prototype * * @type {HorizontalOrigin} */ horizontalOrigin: { get: function () { return this._billboard.horizontalOrigin; }, set: function (value) { this._billboard.horizontalOrigin = value; }, }, /** * Gets or sets the vertical origin of this point, which determines if the point is * to the bottom, center, or top of its anchor position. * * @memberof Cesium3DTilePointFeature.prototype * * @type {VerticalOrigin} */ verticalOrigin: { get: function () { return this._billboard.verticalOrigin; }, set: function (value) { this._billboard.verticalOrigin = value; }, }, /** * Gets or sets the horizontal origin of this point's text, which determines if the point's text is * to the left, center, or right of its anchor position. * * @memberof Cesium3DTilePointFeature.prototype * * @type {HorizontalOrigin} */ labelHorizontalOrigin: { get: function () { return this._label.horizontalOrigin; }, set: function (value) { this._label.horizontalOrigin = value; }, }, /** * Get or sets the vertical origin of this point's text, which determines if the point's text is * to the bottom, center, top, or baseline of it's anchor point. * * @memberof Cesium3DTilePointFeature.prototype * * @type {VerticalOrigin} */ labelVerticalOrigin: { get: function () { return this._label.verticalOrigin; }, set: function (value) { this._label.verticalOrigin = value; }, }, /** * Gets the content of the tile containing the feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Cesium3DTileContent} * * @readonly * @private */ content: { get: function () { return this._content; }, }, /** * Gets the tileset containing the feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Cesium3DTileset} * * @readonly */ tileset: { get: function () { return this._content.tileset; }, }, /** * All objects returned by {@link Scene#pick} have a primitive property. This returns * the tileset containing the feature. * * @memberof Cesium3DTilePointFeature.prototype * * @type {Cesium3DTileset} * * @readonly */ primitive: { get: function () { return this._content.tileset; }, }, /** * @private */ pickIds: { get: function () { var ids = this._pickIds; ids[0] = this._billboard.pickId; ids[1] = this._label.pickId; ids[2] = this._polyline.pickId; return ids; }, }, }); Cesium3DTilePointFeature.defaultColor = Color.WHITE; Cesium3DTilePointFeature.defaultPointOutlineColor = Color.BLACK; Cesium3DTilePointFeature.defaultPointOutlineWidth = 0.0; Cesium3DTilePointFeature.defaultPointSize = 8.0; function setBillboardImage(feature) { var b = feature._billboard; if (defined(feature._billboardImage) && feature._billboardImage !== b.image) { b.image = feature._billboardImage; return; } if (defined(feature._billboardImage)) { return; } var newColor = defaultValue( feature._color, Cesium3DTilePointFeature.defaultColor ); var newOutlineColor = defaultValue( feature._pointOutlineColor, Cesium3DTilePointFeature.defaultPointOutlineColor ); var newOutlineWidth = defaultValue( feature._pointOutlineWidth, Cesium3DTilePointFeature.defaultPointOutlineWidth ); var newPointSize = defaultValue( feature._pointSize, Cesium3DTilePointFeature.defaultPointSize ); var currentColor = feature._billboardColor; var currentOutlineColor = feature._billboardOutlineColor; var currentOutlineWidth = feature._billboardOutlineWidth; var currentPointSize = feature._billboardSize; if ( Color.equals(newColor, currentColor) && Color.equals(newOutlineColor, currentOutlineColor) && newOutlineWidth === currentOutlineWidth && newPointSize === currentPointSize ) { return; } feature._billboardColor = Color.clone(newColor, feature._billboardColor); feature._billboardOutlineColor = Color.clone( newOutlineColor, feature._billboardOutlineColor ); feature._billboardOutlineWidth = newOutlineWidth; feature._billboardSize = newPointSize; var centerAlpha = newColor.alpha; var cssColor = newColor.toCssColorString(); var cssOutlineColor = newOutlineColor.toCssColorString(); var textureId = JSON.stringify([ cssColor, newPointSize, cssOutlineColor, newOutlineWidth, ]); b.setImage( textureId, createBillboardPointCallback( centerAlpha, cssColor, cssOutlineColor, newOutlineWidth, newPointSize ) ); } /** * Returns whether the feature contains this property. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String} name The case-sensitive name of the property. * @returns {Boolean} Whether the feature contains this property. */ Cesium3DTilePointFeature.prototype.hasProperty = function (name) { return this._content.batchTable.hasProperty(this._batchId, name); }; /** * Returns an array of property names for the feature. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String[]} [results] An array into which to store the results. * @returns {String[]} The names of the feature's properties. */ Cesium3DTilePointFeature.prototype.getPropertyNames = function (results) { return this._content.batchTable.getPropertyNames(this._batchId, results); }; /** * Returns a copy of the value of the feature's property with the given name. This includes properties from this feature's * class and inherited classes when using a batch table hierarchy. * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/extensions/3DTILES_batch_table_hierarchy} * * @param {String} name The case-sensitive name of the property. * @returns {*} The value of the property or undefined if the property does not exist. * * @example * // Display all the properties for a feature in the console log. * var propertyNames = feature.getPropertyNames(); * var length = propertyNames.length; * for (var i = 0; i < length; ++i) { * var propertyName = propertyNames[i]; * console.log(propertyName + ': ' + feature.getProperty(propertyName)); * } */ Cesium3DTilePointFeature.prototype.getProperty = function (name) { return this._content.batchTable.getProperty(this._batchId, name); }; /** * Sets the value of the feature's property with the given name. *

* If a property with the given name doesn't exist, it is created. *

* * @param {String} name The case-sensitive name of the property. * @param {*} value The value of the property that will be copied. * * @exception {DeveloperError} Inherited batch table hierarchy property is read only. * * @example * var height = feature.getProperty('Height'); // e.g., the height of a building * * @example * var name = 'clicked'; * if (feature.getProperty(name)) { * console.log('already clicked'); * } else { * feature.setProperty(name, true); * console.log('first click'); * } */ Cesium3DTilePointFeature.prototype.setProperty = function (name, value) { this._content.batchTable.setProperty(this._batchId, name, value); // PERFORMANCE_IDEA: Probably overkill, but maybe only mark the tile dirty if the // property is in one of the style's expressions or - if it can be done quickly - // if the new property value changed the result of an expression. this._content.featurePropertiesDirty = true; }; /** * Returns whether the feature's class name equals className. Unlike {@link Cesium3DTileFeature#isClass} * this function only checks the feature's exact class and not inherited classes. *

* This function returns false if no batch table hierarchy is present. *

* * @param {String} className The name to check against. * @returns {Boolean} Whether the feature's class name equals className * * @private */ Cesium3DTilePointFeature.prototype.isExactClass = function (className) { return this._content.batchTable.isExactClass(this._batchId, className); }; /** * Returns whether the feature's class or any inherited classes are named className. *

* This function returns false if no batch table hierarchy is present. *

* * @param {String} className The name to check against. * @returns {Boolean} Whether the feature's class or inherited classes are named className * * @private */ Cesium3DTilePointFeature.prototype.isClass = function (className) { return this._content.batchTable.isClass(this._batchId, className); }; /** * Returns the feature's class name. *

* This function returns undefined if no batch table hierarchy is present. *

* * @returns {String} The feature's class name. * * @private */ Cesium3DTilePointFeature.prototype.getExactClassName = function () { return this._content.batchTable.getExactClassName(this._batchId); }; /* * https://github.com/dy/bitmap-sdf * Calculate SDF for image/bitmap/bw data * This project is a fork of MapBox's TinySDF that works directly on an input Canvas instead of generating the glyphs themselves. */ var INF = 1e20; function clamp(value, min, max) { return min < max ? (value < min ? min : value > max ? max : value) : (value < max ? max : value > min ? min : value) } function calcSDF(src, options) { if (!options) options = {}; var cutoff = options.cutoff == null ? 0.25 : options.cutoff; var radius = options.radius == null ? 8 : options.radius; var channel = options.channel || 0; var w, h, size, data, intData, stride, ctx, canvas, imgData, i, l; // handle image container if (ArrayBuffer.isView(src) || Array.isArray(src)) { if (!options.width || !options.height) throw Error('For raw data width and height should be provided by options') w = options.width, h = options.height; data = src; if (!options.stride) stride = Math.floor(src.length / w / h); else stride = options.stride; } else { if (window.HTMLCanvasElement && src instanceof window.HTMLCanvasElement) { canvas = src; ctx = canvas.getContext('2d'); w = canvas.width, h = canvas.height; imgData = ctx.getImageData(0, 0, w, h); data = imgData.data; stride = 4; } else if (window.CanvasRenderingContext2D && src instanceof window.CanvasRenderingContext2D) { canvas = src.canvas; ctx = src; w = canvas.width, h = canvas.height; imgData = ctx.getImageData(0, 0, w, h); data = imgData.data; stride = 4; } else if (window.ImageData && src instanceof window.ImageData) { imgData = src; w = src.width, h = src.height; data = imgData.data; stride = 4; } } size = Math.max(w, h); //convert int data to floats if ((window.Uint8ClampedArray && data instanceof window.Uint8ClampedArray) || (window.Uint8Array && data instanceof window.Uint8Array)) { intData = data; data = Array(w * h); for (i = 0, l = intData.length; i < l; i++) { data[i] = intData[i * stride + channel] / 255; } } else { if (stride !== 1) throw Error('Raw data can have only 1 value per pixel') } // temporary arrays for the distance transform var gridOuter = Array(w * h); var gridInner = Array(w * h); var f = Array(size); var d = Array(size); var z = Array(size + 1); var v = Array(size); for (i = 0, l = w * h; i < l; i++) { var a = data[i]; gridOuter[i] = a === 1 ? 0 : a === 0 ? INF : Math.pow(Math.max(0, 0.5 - a), 2); gridInner[i] = a === 1 ? INF : a === 0 ? 0 : Math.pow(Math.max(0, a - 0.5), 2); } edt(gridOuter, w, h, f, d, v, z); edt(gridInner, w, h, f, d, v, z); var dist = window.Float32Array ? new Float32Array(w * h) : new Array(w * h); for (i = 0, l = w * h; i < l; i++) { dist[i] = clamp(1 - ((gridOuter[i] - gridInner[i]) / radius + cutoff), 0, 1); } return dist } // 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/ function edt(data, width, height, f, d, v, z) { for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { f[y] = data[y * width + x]; } edt1d(f, d, v, z, height); for (y = 0; y < height; y++) { data[y * width + x] = d[y]; } } for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { f[x] = data[y * width + x]; } edt1d(f, d, v, z, width); for (x = 0; x < width; x++) { data[y * width + x] = Math.sqrt(d[x]); } } } // 1D squared distance transform function edt1d(f, d, v, z, n) { v[0] = 0; z[0] = -INF; z[1] = +INF; for (var q = 1, k = 0; q < n; q++) { var s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); while (s <= z[k]) { k--; s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); } k++; v[k] = q; z[k] = s; z[k + 1] = +INF; } for (q = 0, k = 0; q < n; q++) { while (z[k + 1] < q) k++; d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; } } /** * Describes how to draw a label. * * @enum {Number} * * @see Label#style */ var LabelStyle = { /** * Fill the text of the label, but do not outline. * * @type {Number} * @constant */ FILL: 0, /** * Outline the text of the label, but do not fill. * * @type {Number} * @constant */ OUTLINE: 1, /** * Fill and outline the text of the label. * * @type {Number} * @constant */ FILL_AND_OUTLINE: 2, }; var LabelStyle$1 = Object.freeze(LabelStyle); var fontInfoCache = {}; var fontInfoCacheLength = 0; var fontInfoCacheMaxSize = 256; var defaultBackgroundColor$2 = new Color(0.165, 0.165, 0.165, 0.8); var defaultBackgroundPadding$1 = new Cartesian2(7, 5); var textTypes = Object.freeze({ LTR: 0, RTL: 1, WEAK: 2, BRACKETS: 3, }); function rebindAllGlyphs$1(label) { if (!label._rebindAllGlyphs && !label._repositionAllGlyphs) { // only push label if it's not already been marked dirty label._labelCollection._labelsToUpdate.push(label); } label._rebindAllGlyphs = true; } function repositionAllGlyphs$1(label) { if (!label._rebindAllGlyphs && !label._repositionAllGlyphs) { // only push label if it's not already been marked dirty label._labelCollection._labelsToUpdate.push(label); } label._repositionAllGlyphs = true; } function getCSSValue(element, property) { return document.defaultView .getComputedStyle(element, null) .getPropertyValue(property); } function parseFont(label) { var fontInfo = fontInfoCache[label._font]; if (!defined(fontInfo)) { var div = document.createElement("div"); div.style.position = "absolute"; div.style.opacity = 0; div.style.font = label._font; document.body.appendChild(div); var lineHeight = parseFloat(getCSSValue(div, "line-height")); if (isNaN(lineHeight)) { // line-height isn't a number, i.e. 'normal'; apply default line-spacing lineHeight = undefined; } fontInfo = { family: getCSSValue(div, "font-family"), size: getCSSValue(div, "font-size").replace("px", ""), style: getCSSValue(div, "font-style"), weight: getCSSValue(div, "font-weight"), lineHeight: lineHeight, }; document.body.removeChild(div); if (fontInfoCacheLength < fontInfoCacheMaxSize) { fontInfoCache[label._font] = fontInfo; fontInfoCacheLength++; } } label._fontFamily = fontInfo.family; label._fontSize = fontInfo.size; label._fontStyle = fontInfo.style; label._fontWeight = fontInfo.weight; label._lineHeight = fontInfo.lineHeight; } /** * A Label draws viewport-aligned text positioned in the 3D scene. This constructor * should not be used directly, instead create labels by calling {@link LabelCollection#add}. * * @alias Label * @internalConstructor * @class * * @exception {DeveloperError} translucencyByDistance.far must be greater than translucencyByDistance.near * @exception {DeveloperError} pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near * @exception {DeveloperError} distanceDisplayCondition.far must be greater than distanceDisplayCondition.near * * @see LabelCollection * @see LabelCollection#add * * @demo {@link https://sandcastle.cesium.com/index.html?src=Labels.html|Cesium Sandcastle Labels Demo} */ function Label(options, labelCollection) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if ( defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0 ) { throw new DeveloperError( "disableDepthTestDistance must be greater than 0.0." ); } //>>includeEnd('debug'); var translucencyByDistance = options.translucencyByDistance; var pixelOffsetScaleByDistance = options.pixelOffsetScaleByDistance; var scaleByDistance = options.scaleByDistance; var distanceDisplayCondition = options.distanceDisplayCondition; if (defined(translucencyByDistance)) { //>>includeStart('debug', pragmas.debug); if (translucencyByDistance.far <= translucencyByDistance.near) { throw new DeveloperError( "translucencyByDistance.far must be greater than translucencyByDistance.near." ); } //>>includeEnd('debug'); translucencyByDistance = NearFarScalar.clone(translucencyByDistance); } if (defined(pixelOffsetScaleByDistance)) { //>>includeStart('debug', pragmas.debug); if (pixelOffsetScaleByDistance.far <= pixelOffsetScaleByDistance.near) { throw new DeveloperError( "pixelOffsetScaleByDistance.far must be greater than pixelOffsetScaleByDistance.near." ); } //>>includeEnd('debug'); pixelOffsetScaleByDistance = NearFarScalar.clone( pixelOffsetScaleByDistance ); } if (defined(scaleByDistance)) { //>>includeStart('debug', pragmas.debug); if (scaleByDistance.far <= scaleByDistance.near) { throw new DeveloperError( "scaleByDistance.far must be greater than scaleByDistance.near." ); } //>>includeEnd('debug'); scaleByDistance = NearFarScalar.clone(scaleByDistance); } if (defined(distanceDisplayCondition)) { //>>includeStart('debug', pragmas.debug); if (distanceDisplayCondition.far <= distanceDisplayCondition.near) { throw new DeveloperError( "distanceDisplayCondition.far must be greater than distanceDisplayCondition.near." ); } //>>includeEnd('debug'); distanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition ); } this._renderedText = undefined; this._text = undefined; this._show = defaultValue(options.show, true); this._font = defaultValue(options.font, "30px sans-serif"); this._fillColor = Color.clone(defaultValue(options.fillColor, Color.WHITE)); this._outlineColor = Color.clone( defaultValue(options.outlineColor, Color.BLACK) ); this._outlineWidth = defaultValue(options.outlineWidth, 1.0); this._showBackground = defaultValue(options.showBackground, false); this._backgroundColor = Color.clone( defaultValue(options.backgroundColor, defaultBackgroundColor$2) ); this._backgroundPadding = Cartesian2.clone( defaultValue(options.backgroundPadding, defaultBackgroundPadding$1) ); this._style = defaultValue(options.style, LabelStyle$1.FILL); this._verticalOrigin = defaultValue( options.verticalOrigin, VerticalOrigin$1.BASELINE ); this._horizontalOrigin = defaultValue( options.horizontalOrigin, HorizontalOrigin$1.LEFT ); this._pixelOffset = Cartesian2.clone( defaultValue(options.pixelOffset, Cartesian2.ZERO) ); this._eyeOffset = Cartesian3.clone( defaultValue(options.eyeOffset, Cartesian3.ZERO) ); this._position = Cartesian3.clone( defaultValue(options.position, Cartesian3.ZERO) ); this._scale = defaultValue(options.scale, 1.0); this._id = options.id; this._translucencyByDistance = translucencyByDistance; this._pixelOffsetScaleByDistance = pixelOffsetScaleByDistance; this._scaleByDistance = scaleByDistance; this._heightReference = defaultValue( options.heightReference, HeightReference$1.NONE ); this._distanceDisplayCondition = distanceDisplayCondition; this._disableDepthTestDistance = options.disableDepthTestDistance; this._labelCollection = labelCollection; this._glyphs = []; this._backgroundBillboard = undefined; this._batchIndex = undefined; // Used only by Vector3DTilePoints and BillboardCollection this._rebindAllGlyphs = true; this._repositionAllGlyphs = true; this._actualClampedPosition = undefined; this._removeCallbackFunc = undefined; this._mode = undefined; this._clusterShow = true; this.text = defaultValue(options.text, ""); this._relativeSize = 1.0; parseFont(this); this._updateClamping(); } Object.defineProperties(Label.prototype, { /** * Determines if this label will be shown. Use this to hide or show a label, instead * of removing it and re-adding it to the collection. * @memberof Label.prototype * @type {Boolean} * @default true */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._show !== value) { this._show = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var billboard = glyphs[i].billboard; if (defined(billboard)) { billboard.show = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.show = value; } } }, }, /** * Gets or sets the Cartesian position of this label. * @memberof Label.prototype * @type {Cartesian3} */ position: { get: function () { return this._position; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var position = this._position; if (!Cartesian3.equals(position, value)) { Cartesian3.clone(value, position); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var billboard = glyphs[i].billboard; if (defined(billboard)) { billboard.position = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.position = value; } this._updateClamping(); } }, }, /** * Gets or sets the height reference of this billboard. * @memberof Label.prototype * @type {HeightReference} * @default HeightReference.NONE */ heightReference: { get: function () { return this._heightReference; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (value !== this._heightReference) { this._heightReference = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var billboard = glyphs[i].billboard; if (defined(billboard)) { billboard.heightReference = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.heightReference = value; } repositionAllGlyphs$1(this); this._updateClamping(); } }, }, /** * Gets or sets the text of this label. * @memberof Label.prototype * @type {String} */ text: { get: function () { return this._text; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._text !== value) { this._text = value; this._renderedText = Label.enableRightToLeftDetection ? reverseRtl(value) : value; rebindAllGlyphs$1(this); } }, }, /** * Gets or sets the font used to draw this label. Fonts are specified using the same syntax as the CSS 'font' property. * @memberof Label.prototype * @type {String} * @default '30px sans-serif' * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles|HTML canvas 2D context text styles} */ font: { get: function () { return this._font; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._font !== value) { this._font = value; rebindAllGlyphs$1(this); parseFont(this); } }, }, /** * Gets or sets the fill color of this label. * @memberof Label.prototype * @type {Color} * @default Color.WHITE * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ fillColor: { get: function () { return this._fillColor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var fillColor = this._fillColor; if (!Color.equals(fillColor, value)) { Color.clone(value, fillColor); rebindAllGlyphs$1(this); } }, }, /** * Gets or sets the outline color of this label. * @memberof Label.prototype * @type {Color} * @default Color.BLACK * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ outlineColor: { get: function () { return this._outlineColor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var outlineColor = this._outlineColor; if (!Color.equals(outlineColor, value)) { Color.clone(value, outlineColor); rebindAllGlyphs$1(this); } }, }, /** * Gets or sets the outline width of this label. * @memberof Label.prototype * @type {Number} * @default 1.0 * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ outlineWidth: { get: function () { return this._outlineWidth; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._outlineWidth !== value) { this._outlineWidth = value; rebindAllGlyphs$1(this); } }, }, /** * Determines if a background behind this label will be shown. * @memberof Label.prototype * @default false * @type {Boolean} */ showBackground: { get: function () { return this._showBackground; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._showBackground !== value) { this._showBackground = value; rebindAllGlyphs$1(this); } }, }, /** * Gets or sets the background color of this label. * @memberof Label.prototype * @type {Color} * @default new Color(0.165, 0.165, 0.165, 0.8) */ backgroundColor: { get: function () { return this._backgroundColor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var backgroundColor = this._backgroundColor; if (!Color.equals(backgroundColor, value)) { Color.clone(value, backgroundColor); var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.color = backgroundColor; } } }, }, /** * Gets or sets the background padding, in pixels, of this label. The x value * controls horizontal padding, and the y value controls vertical padding. * @memberof Label.prototype * @type {Cartesian2} * @default new Cartesian2(7, 5) */ backgroundPadding: { get: function () { return this._backgroundPadding; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var backgroundPadding = this._backgroundPadding; if (!Cartesian2.equals(backgroundPadding, value)) { Cartesian2.clone(value, backgroundPadding); repositionAllGlyphs$1(this); } }, }, /** * Gets or sets the style of this label. * @memberof Label.prototype * @type {LabelStyle} * @default LabelStyle.FILL */ style: { get: function () { return this._style; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._style !== value) { this._style = value; rebindAllGlyphs$1(this); } }, }, /** * Gets or sets the pixel offset in screen space from the origin of this label. This is commonly used * to align multiple labels and billboards at the same position, e.g., an image and text. The * screen space origin is the top, left corner of the canvas; x increases from * left to right, and y increases from top to bottom. *

*
* * * *
default
l.pixeloffset = new Cartesian2(25, 75);
* The label's origin is indicated by the yellow point. *
* @memberof Label.prototype * @type {Cartesian2} * @default Cartesian2.ZERO */ pixelOffset: { get: function () { return this._pixelOffset; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var pixelOffset = this._pixelOffset; if (!Cartesian2.equals(pixelOffset, value)) { Cartesian2.clone(value, pixelOffset); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.pixelOffset = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.pixelOffset = value; } } }, }, /** * Gets or sets near and far translucency properties of a Label based on the Label's distance from the camera. * A label's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's translucency remains clamped to the nearest bound. If undefined, * translucencyByDistance will be disabled. * @memberof Label.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a label's translucencyByDistance to 1.0 when the * // camera is 1500 meters from the label and disappear as * // the camera distance approaches 8.0e6 meters. * text.translucencyByDistance = new Cesium.NearFarScalar(1.5e2, 1.0, 8.0e6, 0.0); * * @example * // Example 2. * // disable translucency by distance * text.translucencyByDistance = undefined; */ translucencyByDistance: { get: function () { return this._translucencyByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var translucencyByDistance = this._translucencyByDistance; if (!NearFarScalar.equals(translucencyByDistance, value)) { this._translucencyByDistance = NearFarScalar.clone( value, translucencyByDistance ); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.translucencyByDistance = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.translucencyByDistance = value; } } }, }, /** * Gets or sets near and far pixel offset scaling properties of a Label based on the Label's distance from the camera. * A label's pixel offset will be scaled between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's pixel offset scaling remains clamped to the nearest bound. If undefined, * pixelOffsetScaleByDistance will be disabled. * @memberof Label.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a label's pixel offset scale to 0.0 when the * // camera is 1500 meters from the label and scale pixel offset to 10.0 pixels * // in the y direction the camera distance approaches 8.0e6 meters. * text.pixelOffset = new Cesium.Cartesian2(0.0, 1.0); * text.pixelOffsetScaleByDistance = new Cesium.NearFarScalar(1.5e2, 0.0, 8.0e6, 10.0); * * @example * // Example 2. * // disable pixel offset by distance * text.pixelOffsetScaleByDistance = undefined; */ pixelOffsetScaleByDistance: { get: function () { return this._pixelOffsetScaleByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var pixelOffsetScaleByDistance = this._pixelOffsetScaleByDistance; if (!NearFarScalar.equals(pixelOffsetScaleByDistance, value)) { this._pixelOffsetScaleByDistance = NearFarScalar.clone( value, pixelOffsetScaleByDistance ); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.pixelOffsetScaleByDistance = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.pixelOffsetScaleByDistance = value; } } }, }, /** * Gets or sets near and far scaling properties of a Label based on the label's distance from the camera. * A label's scale will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the label's scale remains clamped to the nearest bound. If undefined, * scaleByDistance will be disabled. * @memberof Label.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a label's scaleByDistance to scale by 1.5 when the * // camera is 1500 meters from the label and disappear as * // the camera distance approaches 8.0e6 meters. * label.scaleByDistance = new Cesium.NearFarScalar(1.5e2, 1.5, 8.0e6, 0.0); * * @example * // Example 2. * // disable scaling by distance * label.scaleByDistance = undefined; */ scaleByDistance: { get: function () { return this._scaleByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var scaleByDistance = this._scaleByDistance; if (!NearFarScalar.equals(scaleByDistance, value)) { this._scaleByDistance = NearFarScalar.clone(value, scaleByDistance); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.scaleByDistance = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.scaleByDistance = value; } } }, }, /** * Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. Eye coordinates is a left-handed * coordinate system, where x points towards the viewer's right, y points up, and * z points into the screen. Eye coordinates use the same scale as world and model coordinates, * which is typically meters. *

* An eye offset is commonly used to arrange multiple label or objects at the same position, e.g., to * arrange a label above its corresponding 3D model. *

* Below, the label is positioned at the center of the Earth but an eye offset makes it always * appear on top of the Earth regardless of the viewer's or Earth's orientation. *

*
* * * *
* l.eyeOffset = new Cartesian3(0.0, 8000000.0, 0.0);

*
* @memberof Label.prototype * @type {Cartesian3} * @default Cartesian3.ZERO */ eyeOffset: { get: function () { return this._eyeOffset; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var eyeOffset = this._eyeOffset; if (!Cartesian3.equals(eyeOffset, value)) { Cartesian3.clone(value, eyeOffset); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.eyeOffset = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.eyeOffset = value; } } }, }, /** * Gets or sets the horizontal origin of this label, which determines if the label is drawn * to the left, center, or right of its anchor position. *

*
*
*
* @memberof Label.prototype * @type {HorizontalOrigin} * @default HorizontalOrigin.LEFT * @example * // Use a top, right origin * l.horizontalOrigin = Cesium.HorizontalOrigin.RIGHT; * l.verticalOrigin = Cesium.VerticalOrigin.TOP; */ horizontalOrigin: { get: function () { return this._horizontalOrigin; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._horizontalOrigin !== value) { this._horizontalOrigin = value; repositionAllGlyphs$1(this); } }, }, /** * Gets or sets the vertical origin of this label, which determines if the label is * to the above, below, or at the center of its anchor position. *

*
*
*
* @memberof Label.prototype * @type {VerticalOrigin} * @default VerticalOrigin.BASELINE * @example * // Use a top, right origin * l.horizontalOrigin = Cesium.HorizontalOrigin.RIGHT; * l.verticalOrigin = Cesium.VerticalOrigin.TOP; */ verticalOrigin: { get: function () { return this._verticalOrigin; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._verticalOrigin !== value) { this._verticalOrigin = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.verticalOrigin = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.verticalOrigin = value; } repositionAllGlyphs$1(this); } }, }, /** * Gets or sets the uniform scale that is multiplied with the label's size in pixels. * A scale of 1.0 does not change the size of the label; a scale greater than * 1.0 enlarges the label; a positive scale less than 1.0 shrinks * the label. *

* Applying a large scale value may pixelate the label. To make text larger without pixelation, * use a larger font size when calling {@link Label#font} instead. *

*
*
* From left to right in the above image, the scales are 0.5, 1.0, * and 2.0. *
* @memberof Label.prototype * @type {Number} * @default 1.0 */ scale: { get: function () { return this._scale; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._scale !== value) { this._scale = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.scale = value * this._relativeSize; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.scale = value * this._relativeSize; } repositionAllGlyphs$1(this); } }, }, /** * Gets the total scale of the label, which is the label's scale multiplied by the computed relative size * of the desired font compared to the generated glyph size. * @memberof Label.prototype * @type {Number} * @default 1.0 */ totalScale: { get: function () { return this._scale * this._relativeSize; }, }, /** * Gets or sets the condition specifying at what distance from the camera that this label will be displayed. * @memberof Label.prototype * @type {DistanceDisplayCondition} * @default undefined */ distanceDisplayCondition: { get: function () { return this._distanceDisplayCondition; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError("far must be greater than near"); } //>>includeEnd('debug'); if ( !DistanceDisplayCondition.equals(value, this._distanceDisplayCondition) ) { this._distanceDisplayCondition = DistanceDisplayCondition.clone( value, this._distanceDisplayCondition ); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.distanceDisplayCondition = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.distanceDisplayCondition = value; } } }, }, /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof Label.prototype * @type {Number} */ disableDepthTestDistance: { get: function () { return this._disableDepthTestDistance; }, set: function (value) { if (this._disableDepthTestDistance !== value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value < 0.0) { throw new DeveloperError( "disableDepthTestDistance must be greater than 0.0." ); } //>>includeEnd('debug'); this._disableDepthTestDistance = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.disableDepthTestDistance = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.disableDepthTestDistance = value; } } }, }, /** * Gets or sets the user-defined value returned when the label is picked. * @memberof Label.prototype * @type {*} */ id: { get: function () { return this._id; }, set: function (value) { if (this._id !== value) { this._id = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.id = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.id = value; } } }, }, /** * @private */ pickId: { get: function () { if (this._glyphs.length === 0 || !defined(this._glyphs[0].billboard)) { return undefined; } return this._glyphs[0].billboard.pickId; }, }, /** * Keeps track of the position of the label based on the height reference. * @memberof Label.prototype * @type {Cartesian3} * @private */ _clampedPosition: { get: function () { return this._actualClampedPosition; }, set: function (value) { this._actualClampedPosition = Cartesian3.clone( value, this._actualClampedPosition ); var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { // Set all the private values here, because we already clamped to ground // so we don't want to do it again for every glyph glyph.billboard._clampedPosition = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard._clampedPosition = value; } }, }, /** * Determines whether or not this label will be shown or hidden because it was clustered. * @memberof Label.prototype * @type {Boolean} * @default true * @private */ clusterShow: { get: function () { return this._clusterShow; }, set: function (value) { if (this._clusterShow !== value) { this._clusterShow = value; var glyphs = this._glyphs; for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { glyph.billboard.clusterShow = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { backgroundBillboard.clusterShow = value; } } }, }, }); Label.prototype._updateClamping = function () { Billboard._updateClamping(this._labelCollection, this); }; /** * Computes the screen-space position of the label's origin, taking into account eye and pixel offsets. * The screen space origin is the top, left corner of the canvas; x increases from * left to right, and y increases from top to bottom. * * @param {Scene} scene The scene the label is in. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The screen-space position of the label. * * * @example * console.log(l.computeScreenSpacePosition(scene).toString()); * * @see Label#eyeOffset * @see Label#pixelOffset */ Label.prototype.computeScreenSpacePosition = function (scene, result) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError("scene is required."); } //>>includeEnd('debug'); if (!defined(result)) { result = new Cartesian2(); } var labelCollection = this._labelCollection; var modelMatrix = labelCollection.modelMatrix; var actualPosition = defined(this._actualClampedPosition) ? this._actualClampedPosition : this._position; var windowCoordinates = Billboard._computeScreenSpacePosition( modelMatrix, actualPosition, this._eyeOffset, this._pixelOffset, scene, result ); return windowCoordinates; }; /** * Gets a label's screen space bounding box centered around screenSpacePosition. * @param {Label} label The label to get the screen space bounding box for. * @param {Cartesian2} screenSpacePosition The screen space center of the label. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The screen space bounding box. * * @private */ Label.getScreenSpaceBoundingBox = function ( label, screenSpacePosition, result ) { var x = 0; var y = 0; var width = 0; var height = 0; var scale = label.totalScale; var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { x = screenSpacePosition.x + backgroundBillboard._translate.x; y = screenSpacePosition.y - backgroundBillboard._translate.y; width = backgroundBillboard.width * scale; height = backgroundBillboard.height * scale; if ( label.verticalOrigin === VerticalOrigin$1.BOTTOM || label.verticalOrigin === VerticalOrigin$1.BASELINE ) { y -= height; } else if (label.verticalOrigin === VerticalOrigin$1.CENTER) { y -= height * 0.5; } } else { x = Number.POSITIVE_INFINITY; y = Number.POSITIVE_INFINITY; var maxX = 0; var maxY = 0; var glyphs = label._glyphs; var length = glyphs.length; for (var i = 0; i < length; ++i) { var glyph = glyphs[i]; var billboard = glyph.billboard; if (!defined(billboard)) { continue; } var glyphX = screenSpacePosition.x + billboard._translate.x; var glyphY = screenSpacePosition.y - billboard._translate.y; var glyphWidth = glyph.dimensions.width * scale; var glyphHeight = glyph.dimensions.height * scale; if ( label.verticalOrigin === VerticalOrigin$1.BOTTOM || label.verticalOrigin === VerticalOrigin$1.BASELINE ) { glyphY -= glyphHeight; } else if (label.verticalOrigin === VerticalOrigin$1.CENTER) { glyphY -= glyphHeight * 0.5; } if (label._verticalOrigin === VerticalOrigin$1.TOP) { glyphY += SDFSettings$1.PADDING * scale; } else if ( label._verticalOrigin === VerticalOrigin$1.BOTTOM || label._verticalOrigin === VerticalOrigin$1.BASELINE ) { glyphY -= SDFSettings$1.PADDING * scale; } x = Math.min(x, glyphX); y = Math.min(y, glyphY); maxX = Math.max(maxX, glyphX + glyphWidth); maxY = Math.max(maxY, glyphY + glyphHeight); } width = maxX - x; height = maxY - y; } if (!defined(result)) { result = new BoundingRectangle(); } result.x = x; result.y = y; result.width = width; result.height = height; return result; }; /** * Determines if this label equals another label. Labels are equal if all their properties * are equal. Labels in different collections can be equal. * * @param {Label} other The label to compare for equality. * @returns {Boolean} true if the labels are equal; otherwise, false. */ Label.prototype.equals = function (other) { return ( this === other || (defined(other) && this._show === other._show && this._scale === other._scale && this._outlineWidth === other._outlineWidth && this._showBackground === other._showBackground && this._style === other._style && this._verticalOrigin === other._verticalOrigin && this._horizontalOrigin === other._horizontalOrigin && this._heightReference === other._heightReference && this._renderedText === other._renderedText && this._font === other._font && Cartesian3.equals(this._position, other._position) && Color.equals(this._fillColor, other._fillColor) && Color.equals(this._outlineColor, other._outlineColor) && Color.equals(this._backgroundColor, other._backgroundColor) && Cartesian2.equals(this._backgroundPadding, other._backgroundPadding) && Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals( this._translucencyByDistance, other._translucencyByDistance ) && NearFarScalar.equals( this._pixelOffsetScaleByDistance, other._pixelOffsetScaleByDistance ) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && DistanceDisplayCondition.equals( this._distanceDisplayCondition, other._distanceDisplayCondition ) && this._disableDepthTestDistance === other._disableDepthTestDistance && this._id === other._id) ); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ Label.prototype.isDestroyed = function () { return false; }; /** * Determines whether or not run the algorithm, that match the text of the label to right-to-left languages * @memberof Label * @type {Boolean} * @default false * * @example * // Example 1. * // Set a label's rightToLeft before init * Cesium.Label.enableRightToLeftDetection = true; * var myLabelEntity = viewer.entities.add({ * label: { * id: 'my label', * text: 'זה טקסט בעברית \n ועכשיו יורדים שורה', * } * }); * * @example * // Example 2. * var myLabelEntity = viewer.entities.add({ * label: { * id: 'my label', * text: 'English text' * } * }); * // Set a label's rightToLeft after init * Cesium.Label.enableRightToLeftDetection = true; * myLabelEntity.text = 'טקסט חדש'; */ Label.enableRightToLeftDetection = false; function convertTextToTypes(text, rtlChars) { var ltrChars = /[a-zA-Z0-9]/; var bracketsChars = /[()[\]{}<>]/; var parsedText = []; var word = ""; var lastType = textTypes.LTR; var currentType = ""; var textLength = text.length; for (var textIndex = 0; textIndex < textLength; ++textIndex) { var character = text.charAt(textIndex); if (rtlChars.test(character)) { currentType = textTypes.RTL; } else if (ltrChars.test(character)) { currentType = textTypes.LTR; } else if (bracketsChars.test(character)) { currentType = textTypes.BRACKETS; } else { currentType = textTypes.WEAK; } if (textIndex === 0) { lastType = currentType; } if (lastType === currentType && currentType !== textTypes.BRACKETS) { word += character; } else { if (word !== "") { parsedText.push({ Type: lastType, Word: word }); } lastType = currentType; word = character; } } parsedText.push({ Type: currentType, Word: word }); return parsedText; } function reverseWord(word) { return word.split("").reverse().join(""); } function spliceWord(result, pointer, word) { return result.slice(0, pointer) + word + result.slice(pointer); } function reverseBrackets(bracket) { switch (bracket) { case "(": return ")"; case ")": return "("; case "[": return "]"; case "]": return "["; case "{": return "}"; case "}": return "{"; case "<": return ">"; case ">": return "<"; } } //To add another language, simply add its Unicode block range(s) to the below regex. var hebrew = "\u05D0-\u05EA"; var arabic = "\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF"; var rtlChars = new RegExp("[" + hebrew + arabic + "]"); /** * * @param {String} value the text to parse and reorder * @returns {String} the text as rightToLeft direction * @private */ function reverseRtl(value) { var texts = value.split("\n"); var result = ""; for (var i = 0; i < texts.length; i++) { var text = texts[i]; // first character of the line is a RTL character, so need to manage different cases var rtlDir = rtlChars.test(text.charAt(0)); var parsedText = convertTextToTypes(text, rtlChars); var splicePointer = 0; var line = ""; for (var wordIndex = 0; wordIndex < parsedText.length; ++wordIndex) { var subText = parsedText[wordIndex]; var reverse = subText.Type === textTypes.BRACKETS ? reverseBrackets(subText.Word) : reverseWord(subText.Word); if (rtlDir) { if (subText.Type === textTypes.RTL) { line = reverse + line; splicePointer = 0; } else if (subText.Type === textTypes.LTR) { line = spliceWord(line, splicePointer, subText.Word); splicePointer += subText.Word.length; } else if ( subText.Type === textTypes.WEAK || subText.Type === textTypes.BRACKETS ) { // current word is weak, last one was bracket if ( subText.Type === textTypes.WEAK && parsedText[wordIndex - 1].Type === textTypes.BRACKETS ) { line = reverse + line; } // current word is weak or bracket, last one was rtl else if (parsedText[wordIndex - 1].Type === textTypes.RTL) { line = reverse + line; splicePointer = 0; } // current word is weak or bracket, there is at least one more word else if (parsedText.length > wordIndex + 1) { // next word is rtl if (parsedText[wordIndex + 1].Type === textTypes.RTL) { line = reverse + line; splicePointer = 0; } else { line = spliceWord(line, splicePointer, subText.Word); splicePointer += subText.Word.length; } } // current word is weak or bracket, and it the last in this line else { line = spliceWord(line, 0, reverse); } } } // ltr line, rtl word else if (subText.Type === textTypes.RTL) { line = spliceWord(line, splicePointer, reverse); } // ltr line, ltr word else if (subText.Type === textTypes.LTR) { line += subText.Word; splicePointer = line.length; } // ltr line, weak or bracket word else if ( subText.Type === textTypes.WEAK || subText.Type === textTypes.BRACKETS ) { // not first word in line if (wordIndex > 0) { // last word was rtl if (parsedText[wordIndex - 1].Type === textTypes.RTL) { // there is at least one more word if (parsedText.length > wordIndex + 1) { // next word is rtl if (parsedText[wordIndex + 1].Type === textTypes.RTL) { line = spliceWord(line, splicePointer, reverse); } else { line += subText.Word; splicePointer = line.length; } } else { line += subText.Word; } } else { line += subText.Word; splicePointer = line.length; } } else { line += subText.Word; splicePointer = line.length; } } } result += line; if (i < texts.length - 1) { result += "\n"; } } return result; } /* Breaks a Javascript string into individual user-perceived "characters" called extended grapheme clusters by implementing the Unicode UAX-29 standard, version 10.0.0 Usage: var splitter = new GraphemeSplitter(); //returns an array of strings, one string for each grapheme cluster var graphemes = splitter.splitGraphemes(string); */ function GraphemeSplitter(){ var CR = 0, LF = 1, Control = 2, Extend = 3, Regional_Indicator = 4, SpacingMark = 5, L = 6, V = 7, T = 8, LV = 9, LVT = 10, Other = 11, Prepend = 12, E_Base = 13, E_Modifier = 14, ZWJ = 15, Glue_After_Zwj = 16, E_Base_GAZ = 17; // BreakTypes var NotBreak = 0, BreakStart = 1, Break = 2, BreakLastRegional = 3, BreakPenultimateRegional = 4; function isSurrogate(str, pos) { return 0xd800 <= str.charCodeAt(pos) && str.charCodeAt(pos) <= 0xdbff && 0xdc00 <= str.charCodeAt(pos + 1) && str.charCodeAt(pos + 1) <= 0xdfff; } // Private function, gets a Unicode code point from a JavaScript UTF-16 string // handling surrogate pairs appropriately function codePointAt(str, idx){ if(idx === undefined){ idx = 0; } var code = str.charCodeAt(idx); // if a high surrogate if (0xD800 <= code && code <= 0xDBFF && idx < str.length - 1){ var hi = code; var low = str.charCodeAt(idx + 1); if (0xDC00 <= low && low <= 0xDFFF){ return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; } return hi; } // if a low surrogate if (0xDC00 <= code && code <= 0xDFFF && idx >= 1){ var hi = str.charCodeAt(idx - 1); var low = code; if (0xD800 <= hi && hi <= 0xDBFF){ return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; } return low; } //just return the char if an unmatched surrogate half or a //single-char codepoint return code; } // Private function, returns whether a break is allowed between the // two given grapheme breaking classes function shouldBreak(start, mid, end){ var all = [start].concat(mid).concat([end]); var previous = all[all.length - 2]; var next = end; // Lookahead termintor for: // GB10. (E_Base | EBG) Extend* ? E_Modifier var eModifierIndex = all.lastIndexOf(E_Modifier); if(eModifierIndex > 1 && all.slice(1, eModifierIndex).every(function(c){return c == Extend}) && [Extend, E_Base, E_Base_GAZ].indexOf(start) == -1){ return Break } // Lookahead termintor for: // GB12. ^ (RI RI)* RI ? RI // GB13. [^RI] (RI RI)* RI ? RI var rIIndex = all.lastIndexOf(Regional_Indicator); if(rIIndex > 0 && all.slice(1, rIIndex).every(function(c){return c == Regional_Indicator}) && [Prepend, Regional_Indicator].indexOf(previous) == -1) { if(all.filter(function(c){return c == Regional_Indicator}).length % 2 == 1) { return BreakLastRegional } else { return BreakPenultimateRegional } } // GB3. CR X LF if(previous == CR && next == LF){ return NotBreak; } // GB4. (Control|CR|LF) ÷ else if(previous == Control || previous == CR || previous == LF){ if(next == E_Modifier && mid.every(function(c){return c == Extend})){ return Break } else { return BreakStart } } // GB5. ÷ (Control|CR|LF) else if(next == Control || next == CR || next == LF){ return BreakStart; } // GB6. L X (L|V|LV|LVT) else if(previous == L && (next == L || next == V || next == LV || next == LVT)){ return NotBreak; } // GB7. (LV|V) X (V|T) else if((previous == LV || previous == V) && (next == V || next == T)){ return NotBreak; } // GB8. (LVT|T) X (T) else if((previous == LVT || previous == T) && next == T){ return NotBreak; } // GB9. X (Extend|ZWJ) else if (next == Extend || next == ZWJ){ return NotBreak; } // GB9a. X SpacingMark else if(next == SpacingMark){ return NotBreak; } // GB9b. Prepend X else if (previous == Prepend){ return NotBreak; } // GB10. (E_Base | EBG) Extend* ? E_Modifier var previousNonExtendIndex = all.indexOf(Extend) != -1 ? all.lastIndexOf(Extend) - 1 : all.length - 2; if([E_Base, E_Base_GAZ].indexOf(all[previousNonExtendIndex]) != -1 && all.slice(previousNonExtendIndex + 1, -1).every(function(c){return c == Extend}) && next == E_Modifier){ return NotBreak; } // GB11. ZWJ ? (Glue_After_Zwj | EBG) if(previous == ZWJ && [Glue_After_Zwj, E_Base_GAZ].indexOf(next) != -1) { return NotBreak; } // GB12. ^ (RI RI)* RI ? RI // GB13. [^RI] (RI RI)* RI ? RI if(mid.indexOf(Regional_Indicator) != -1) { return Break; } if(previous == Regional_Indicator && next == Regional_Indicator) { return NotBreak; } // GB999. Any ? Any return BreakStart; } // Returns the next grapheme break in the string after the given index this.nextBreak = function(string, index){ if(index === undefined){ index = 0; } if(index < 0){ return 0; } if(index >= string.length - 1){ return string.length; } var prev = getGraphemeBreakProperty(codePointAt(string, index)); var mid = []; for (var i = index + 1; i < string.length; i++) { // check for already processed low surrogates if(isSurrogate(string, i - 1)){ continue; } var next = getGraphemeBreakProperty(codePointAt(string, i)); if(shouldBreak(prev, mid, next)){ return i; } mid.push(next); } return string.length; }; // Breaks the given string into an array of grapheme cluster strings this.splitGraphemes = function(str){ var res = []; var index = 0; var brk; while((brk = this.nextBreak(str, index)) < str.length){ res.push(str.slice(index, brk)); index = brk; } if(index < str.length){ res.push(str.slice(index)); } return res; }; // Returns the iterator of grapheme clusters there are in the given string this.iterateGraphemes = function(str) { var index = 0; var res = { next: (function() { var value; var brk; if ((brk = this.nextBreak(str, index)) < str.length) { value = str.slice(index, brk); index = brk; return { value: value, done: false }; } if (index < str.length) { value = str.slice(index); index = str.length; return { value: value, done: false }; } return { value: undefined, done: true }; }).bind(this) }; // ES2015 @@iterator method (iterable) for spread syntax and for...of statement if (typeof Symbol !== 'undefined' && Symbol.iterator) { res[Symbol.iterator] = function() {return res}; } return res; }; // Returns the number of grapheme clusters there are in the given string this.countGraphemes = function(str){ var count = 0; var index = 0; var brk; while((brk = this.nextBreak(str, index)) < str.length){ index = brk; count++; } if(index < str.length){ count++; } return count; }; //given a Unicode code point, determines this symbol's grapheme break property function getGraphemeBreakProperty(code){ //grapheme break property for Unicode 10.0.0, //taken from http://www.unicode.org/Public/10.0.0/ucd/auxiliary/GraphemeBreakProperty.txt //and adapted to JavaScript rules if( (0x0600 <= code && code <= 0x0605) || // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE 0x06DD == code || // Cf ARABIC END OF AYAH 0x070F == code || // Cf SYRIAC ABBREVIATION MARK 0x08E2 == code || // Cf ARABIC DISPUTED END OF AYAH 0x0D4E == code || // Lo MALAYALAM LETTER DOT REPH 0x110BD == code || // Cf KAITHI NUMBER SIGN (0x111C2 <= code && code <= 0x111C3) || // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA 0x11A3A == code || // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA (0x11A86 <= code && code <= 0x11A89) || // Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA 0x11D46 == code // Lo MASARAM GONDI REPHA ){ return Prepend; } if( 0x000D == code // Cc ){ return CR; } if( 0x000A == code // Cc ){ return LF; } if( (0x0000 <= code && code <= 0x0009) || // Cc [10] .. (0x000B <= code && code <= 0x000C) || // Cc [2] .. (0x000E <= code && code <= 0x001F) || // Cc [18] .. (0x007F <= code && code <= 0x009F) || // Cc [33] .. 0x00AD == code || // Cf SOFT HYPHEN 0x061C == code || // Cf ARABIC LETTER MARK 0x180E == code || // Cf MONGOLIAN VOWEL SEPARATOR 0x200B == code || // Cf ZERO WIDTH SPACE (0x200E <= code && code <= 0x200F) || // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK 0x2028 == code || // Zl LINE SEPARATOR 0x2029 == code || // Zp PARAGRAPH SEPARATOR (0x202A <= code && code <= 0x202E) || // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE (0x2060 <= code && code <= 0x2064) || // Cf [5] WORD JOINER..INVISIBLE PLUS 0x2065 == code || // Cn (0x2066 <= code && code <= 0x206F) || // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES (0xD800 <= code && code <= 0xDFFF) || // Cs [2048] .. 0xFEFF == code || // Cf ZERO WIDTH NO-BREAK SPACE (0xFFF0 <= code && code <= 0xFFF8) || // Cn [9] .. (0xFFF9 <= code && code <= 0xFFFB) || // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR (0x1BCA0 <= code && code <= 0x1BCA3) || // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP (0x1D173 <= code && code <= 0x1D17A) || // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE 0xE0000 == code || // Cn 0xE0001 == code || // Cf LANGUAGE TAG (0xE0002 <= code && code <= 0xE001F) || // Cn [30] .. (0xE0080 <= code && code <= 0xE00FF) || // Cn [128] .. (0xE01F0 <= code && code <= 0xE0FFF) // Cn [3600] .. ){ return Control; } if( (0x0300 <= code && code <= 0x036F) || // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X (0x0483 <= code && code <= 0x0487) || // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE (0x0488 <= code && code <= 0x0489) || // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN (0x0591 <= code && code <= 0x05BD) || // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG 0x05BF == code || // Mn HEBREW POINT RAFE (0x05C1 <= code && code <= 0x05C2) || // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT (0x05C4 <= code && code <= 0x05C5) || // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT 0x05C7 == code || // Mn HEBREW POINT QAMATS QATAN (0x0610 <= code && code <= 0x061A) || // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA (0x064B <= code && code <= 0x065F) || // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW 0x0670 == code || // Mn ARABIC LETTER SUPERSCRIPT ALEF (0x06D6 <= code && code <= 0x06DC) || // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN (0x06DF <= code && code <= 0x06E4) || // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA (0x06E7 <= code && code <= 0x06E8) || // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON (0x06EA <= code && code <= 0x06ED) || // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM 0x0711 == code || // Mn SYRIAC LETTER SUPERSCRIPT ALAPH (0x0730 <= code && code <= 0x074A) || // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH (0x07A6 <= code && code <= 0x07B0) || // Mn [11] THAANA ABAFILI..THAANA SUKUN (0x07EB <= code && code <= 0x07F3) || // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE (0x0816 <= code && code <= 0x0819) || // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH (0x081B <= code && code <= 0x0823) || // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A (0x0825 <= code && code <= 0x0827) || // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U (0x0829 <= code && code <= 0x082D) || // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA (0x0859 <= code && code <= 0x085B) || // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK (0x08D4 <= code && code <= 0x08E1) || // Mn [14] ARABIC SMALL HIGH WORD AR-RUB..ARABIC SMALL HIGH SIGN SAFHA (0x08E3 <= code && code <= 0x0902) || // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA 0x093A == code || // Mn DEVANAGARI VOWEL SIGN OE 0x093C == code || // Mn DEVANAGARI SIGN NUKTA (0x0941 <= code && code <= 0x0948) || // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI 0x094D == code || // Mn DEVANAGARI SIGN VIRAMA (0x0951 <= code && code <= 0x0957) || // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE (0x0962 <= code && code <= 0x0963) || // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL 0x0981 == code || // Mn BENGALI SIGN CANDRABINDU 0x09BC == code || // Mn BENGALI SIGN NUKTA 0x09BE == code || // Mc BENGALI VOWEL SIGN AA (0x09C1 <= code && code <= 0x09C4) || // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR 0x09CD == code || // Mn BENGALI SIGN VIRAMA 0x09D7 == code || // Mc BENGALI AU LENGTH MARK (0x09E2 <= code && code <= 0x09E3) || // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL (0x0A01 <= code && code <= 0x0A02) || // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI 0x0A3C == code || // Mn GURMUKHI SIGN NUKTA (0x0A41 <= code && code <= 0x0A42) || // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU (0x0A47 <= code && code <= 0x0A48) || // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI (0x0A4B <= code && code <= 0x0A4D) || // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA 0x0A51 == code || // Mn GURMUKHI SIGN UDAAT (0x0A70 <= code && code <= 0x0A71) || // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK 0x0A75 == code || // Mn GURMUKHI SIGN YAKASH (0x0A81 <= code && code <= 0x0A82) || // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA 0x0ABC == code || // Mn GUJARATI SIGN NUKTA (0x0AC1 <= code && code <= 0x0AC5) || // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E (0x0AC7 <= code && code <= 0x0AC8) || // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI 0x0ACD == code || // Mn GUJARATI SIGN VIRAMA (0x0AE2 <= code && code <= 0x0AE3) || // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL (0x0AFA <= code && code <= 0x0AFF) || // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE 0x0B01 == code || // Mn ORIYA SIGN CANDRABINDU 0x0B3C == code || // Mn ORIYA SIGN NUKTA 0x0B3E == code || // Mc ORIYA VOWEL SIGN AA 0x0B3F == code || // Mn ORIYA VOWEL SIGN I (0x0B41 <= code && code <= 0x0B44) || // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR 0x0B4D == code || // Mn ORIYA SIGN VIRAMA 0x0B56 == code || // Mn ORIYA AI LENGTH MARK 0x0B57 == code || // Mc ORIYA AU LENGTH MARK (0x0B62 <= code && code <= 0x0B63) || // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL 0x0B82 == code || // Mn TAMIL SIGN ANUSVARA 0x0BBE == code || // Mc TAMIL VOWEL SIGN AA 0x0BC0 == code || // Mn TAMIL VOWEL SIGN II 0x0BCD == code || // Mn TAMIL SIGN VIRAMA 0x0BD7 == code || // Mc TAMIL AU LENGTH MARK 0x0C00 == code || // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE (0x0C3E <= code && code <= 0x0C40) || // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II (0x0C46 <= code && code <= 0x0C48) || // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI (0x0C4A <= code && code <= 0x0C4D) || // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA (0x0C55 <= code && code <= 0x0C56) || // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK (0x0C62 <= code && code <= 0x0C63) || // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0x0C81 == code || // Mn KANNADA SIGN CANDRABINDU 0x0CBC == code || // Mn KANNADA SIGN NUKTA 0x0CBF == code || // Mn KANNADA VOWEL SIGN I 0x0CC2 == code || // Mc KANNADA VOWEL SIGN UU 0x0CC6 == code || // Mn KANNADA VOWEL SIGN E (0x0CCC <= code && code <= 0x0CCD) || // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA (0x0CD5 <= code && code <= 0x0CD6) || // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK (0x0CE2 <= code && code <= 0x0CE3) || // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL (0x0D00 <= code && code <= 0x0D01) || // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU (0x0D3B <= code && code <= 0x0D3C) || // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA 0x0D3E == code || // Mc MALAYALAM VOWEL SIGN AA (0x0D41 <= code && code <= 0x0D44) || // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR 0x0D4D == code || // Mn MALAYALAM SIGN VIRAMA 0x0D57 == code || // Mc MALAYALAM AU LENGTH MARK (0x0D62 <= code && code <= 0x0D63) || // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL 0x0DCA == code || // Mn SINHALA SIGN AL-LAKUNA 0x0DCF == code || // Mc SINHALA VOWEL SIGN AELA-PILLA (0x0DD2 <= code && code <= 0x0DD4) || // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA 0x0DD6 == code || // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA 0x0DDF == code || // Mc SINHALA VOWEL SIGN GAYANUKITTA 0x0E31 == code || // Mn THAI CHARACTER MAI HAN-AKAT (0x0E34 <= code && code <= 0x0E3A) || // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU (0x0E47 <= code && code <= 0x0E4E) || // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN 0x0EB1 == code || // Mn LAO VOWEL SIGN MAI KAN (0x0EB4 <= code && code <= 0x0EB9) || // Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU (0x0EBB <= code && code <= 0x0EBC) || // Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO (0x0EC8 <= code && code <= 0x0ECD) || // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA (0x0F18 <= code && code <= 0x0F19) || // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS 0x0F35 == code || // Mn TIBETAN MARK NGAS BZUNG NYI ZLA 0x0F37 == code || // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS 0x0F39 == code || // Mn TIBETAN MARK TSA -PHRU (0x0F71 <= code && code <= 0x0F7E) || // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO (0x0F80 <= code && code <= 0x0F84) || // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA (0x0F86 <= code && code <= 0x0F87) || // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS (0x0F8D <= code && code <= 0x0F97) || // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA (0x0F99 <= code && code <= 0x0FBC) || // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA 0x0FC6 == code || // Mn TIBETAN SYMBOL PADMA GDAN (0x102D <= code && code <= 0x1030) || // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU (0x1032 <= code && code <= 0x1037) || // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW (0x1039 <= code && code <= 0x103A) || // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT (0x103D <= code && code <= 0x103E) || // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA (0x1058 <= code && code <= 0x1059) || // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL (0x105E <= code && code <= 0x1060) || // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA (0x1071 <= code && code <= 0x1074) || // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE 0x1082 == code || // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA (0x1085 <= code && code <= 0x1086) || // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y 0x108D == code || // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE 0x109D == code || // Mn MYANMAR VOWEL SIGN AITON AI (0x135D <= code && code <= 0x135F) || // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK (0x1712 <= code && code <= 0x1714) || // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA (0x1732 <= code && code <= 0x1734) || // Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD (0x1752 <= code && code <= 0x1753) || // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U (0x1772 <= code && code <= 0x1773) || // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U (0x17B4 <= code && code <= 0x17B5) || // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA (0x17B7 <= code && code <= 0x17BD) || // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA 0x17C6 == code || // Mn KHMER SIGN NIKAHIT (0x17C9 <= code && code <= 0x17D3) || // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT 0x17DD == code || // Mn KHMER SIGN ATTHACAN (0x180B <= code && code <= 0x180D) || // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE (0x1885 <= code && code <= 0x1886) || // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA 0x18A9 == code || // Mn MONGOLIAN LETTER ALI GALI DAGALGA (0x1920 <= code && code <= 0x1922) || // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U (0x1927 <= code && code <= 0x1928) || // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O 0x1932 == code || // Mn LIMBU SMALL LETTER ANUSVARA (0x1939 <= code && code <= 0x193B) || // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I (0x1A17 <= code && code <= 0x1A18) || // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U 0x1A1B == code || // Mn BUGINESE VOWEL SIGN AE 0x1A56 == code || // Mn TAI THAM CONSONANT SIGN MEDIAL LA (0x1A58 <= code && code <= 0x1A5E) || // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA 0x1A60 == code || // Mn TAI THAM SIGN SAKOT 0x1A62 == code || // Mn TAI THAM VOWEL SIGN MAI SAT (0x1A65 <= code && code <= 0x1A6C) || // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW (0x1A73 <= code && code <= 0x1A7C) || // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN 0x1A7F == code || // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT (0x1AB0 <= code && code <= 0x1ABD) || // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 0x1ABE == code || // Me COMBINING PARENTHESES OVERLAY (0x1B00 <= code && code <= 0x1B03) || // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 0x1B34 == code || // Mn BALINESE SIGN REREKAN (0x1B36 <= code && code <= 0x1B3A) || // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA 0x1B3C == code || // Mn BALINESE VOWEL SIGN LA LENGA 0x1B42 == code || // Mn BALINESE VOWEL SIGN PEPET (0x1B6B <= code && code <= 0x1B73) || // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG (0x1B80 <= code && code <= 0x1B81) || // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR (0x1BA2 <= code && code <= 0x1BA5) || // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU (0x1BA8 <= code && code <= 0x1BA9) || // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG (0x1BAB <= code && code <= 0x1BAD) || // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA 0x1BE6 == code || // Mn BATAK SIGN TOMPI (0x1BE8 <= code && code <= 0x1BE9) || // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE 0x1BED == code || // Mn BATAK VOWEL SIGN KARO O (0x1BEF <= code && code <= 0x1BF1) || // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H (0x1C2C <= code && code <= 0x1C33) || // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T (0x1C36 <= code && code <= 0x1C37) || // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA (0x1CD0 <= code && code <= 0x1CD2) || // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA (0x1CD4 <= code && code <= 0x1CE0) || // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA (0x1CE2 <= code && code <= 0x1CE8) || // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL 0x1CED == code || // Mn VEDIC SIGN TIRYAK 0x1CF4 == code || // Mn VEDIC TONE CANDRA ABOVE (0x1CF8 <= code && code <= 0x1CF9) || // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE (0x1DC0 <= code && code <= 0x1DF9) || // Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW (0x1DFB <= code && code <= 0x1DFF) || // Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW 0x200C == code || // Cf ZERO WIDTH NON-JOINER (0x20D0 <= code && code <= 0x20DC) || // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE (0x20DD <= code && code <= 0x20E0) || // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH 0x20E1 == code || // Mn COMBINING LEFT RIGHT ARROW ABOVE (0x20E2 <= code && code <= 0x20E4) || // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE (0x20E5 <= code && code <= 0x20F0) || // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE (0x2CEF <= code && code <= 0x2CF1) || // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS 0x2D7F == code || // Mn TIFINAGH CONSONANT JOINER (0x2DE0 <= code && code <= 0x2DFF) || // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS (0x302A <= code && code <= 0x302D) || // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK (0x302E <= code && code <= 0x302F) || // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK (0x3099 <= code && code <= 0x309A) || // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 0xA66F == code || // Mn COMBINING CYRILLIC VZMET (0xA670 <= code && code <= 0xA672) || // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN (0xA674 <= code && code <= 0xA67D) || // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK (0xA69E <= code && code <= 0xA69F) || // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E (0xA6F0 <= code && code <= 0xA6F1) || // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS 0xA802 == code || // Mn SYLOTI NAGRI SIGN DVISVARA 0xA806 == code || // Mn SYLOTI NAGRI SIGN HASANTA 0xA80B == code || // Mn SYLOTI NAGRI SIGN ANUSVARA (0xA825 <= code && code <= 0xA826) || // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E (0xA8C4 <= code && code <= 0xA8C5) || // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU (0xA8E0 <= code && code <= 0xA8F1) || // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA (0xA926 <= code && code <= 0xA92D) || // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU (0xA947 <= code && code <= 0xA951) || // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R (0xA980 <= code && code <= 0xA982) || // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR 0xA9B3 == code || // Mn JAVANESE SIGN CECAK TELU (0xA9B6 <= code && code <= 0xA9B9) || // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT 0xA9BC == code || // Mn JAVANESE VOWEL SIGN PEPET 0xA9E5 == code || // Mn MYANMAR SIGN SHAN SAW (0xAA29 <= code && code <= 0xAA2E) || // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE (0xAA31 <= code && code <= 0xAA32) || // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE (0xAA35 <= code && code <= 0xAA36) || // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA 0xAA43 == code || // Mn CHAM CONSONANT SIGN FINAL NG 0xAA4C == code || // Mn CHAM CONSONANT SIGN FINAL M 0xAA7C == code || // Mn MYANMAR SIGN TAI LAING TONE-2 0xAAB0 == code || // Mn TAI VIET MAI KANG (0xAAB2 <= code && code <= 0xAAB4) || // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U (0xAAB7 <= code && code <= 0xAAB8) || // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA (0xAABE <= code && code <= 0xAABF) || // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK 0xAAC1 == code || // Mn TAI VIET TONE MAI THO (0xAAEC <= code && code <= 0xAAED) || // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI 0xAAF6 == code || // Mn MEETEI MAYEK VIRAMA 0xABE5 == code || // Mn MEETEI MAYEK VOWEL SIGN ANAP 0xABE8 == code || // Mn MEETEI MAYEK VOWEL SIGN UNAP 0xABED == code || // Mn MEETEI MAYEK APUN IYEK 0xFB1E == code || // Mn HEBREW POINT JUDEO-SPANISH VARIKA (0xFE00 <= code && code <= 0xFE0F) || // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 (0xFE20 <= code && code <= 0xFE2F) || // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF (0xFF9E <= code && code <= 0xFF9F) || // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK 0x101FD == code || // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE 0x102E0 == code || // Mn COPTIC EPACT THOUSANDS MARK (0x10376 <= code && code <= 0x1037A) || // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII (0x10A01 <= code && code <= 0x10A03) || // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R (0x10A05 <= code && code <= 0x10A06) || // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O (0x10A0C <= code && code <= 0x10A0F) || // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA (0x10A38 <= code && code <= 0x10A3A) || // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW 0x10A3F == code || // Mn KHAROSHTHI VIRAMA (0x10AE5 <= code && code <= 0x10AE6) || // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW 0x11001 == code || // Mn BRAHMI SIGN ANUSVARA (0x11038 <= code && code <= 0x11046) || // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA (0x1107F <= code && code <= 0x11081) || // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA (0x110B3 <= code && code <= 0x110B6) || // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI (0x110B9 <= code && code <= 0x110BA) || // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA (0x11100 <= code && code <= 0x11102) || // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA (0x11127 <= code && code <= 0x1112B) || // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU (0x1112D <= code && code <= 0x11134) || // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA 0x11173 == code || // Mn MAHAJANI SIGN NUKTA (0x11180 <= code && code <= 0x11181) || // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA (0x111B6 <= code && code <= 0x111BE) || // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O (0x111CA <= code && code <= 0x111CC) || // Mn [3] SHARADA SIGN NUKTA..SHARADA EXTRA SHORT VOWEL MARK (0x1122F <= code && code <= 0x11231) || // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI 0x11234 == code || // Mn KHOJKI SIGN ANUSVARA (0x11236 <= code && code <= 0x11237) || // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA 0x1123E == code || // Mn KHOJKI SIGN SUKUN 0x112DF == code || // Mn KHUDAWADI SIGN ANUSVARA (0x112E3 <= code && code <= 0x112EA) || // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA (0x11300 <= code && code <= 0x11301) || // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU 0x1133C == code || // Mn GRANTHA SIGN NUKTA 0x1133E == code || // Mc GRANTHA VOWEL SIGN AA 0x11340 == code || // Mn GRANTHA VOWEL SIGN II 0x11357 == code || // Mc GRANTHA AU LENGTH MARK (0x11366 <= code && code <= 0x1136C) || // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX (0x11370 <= code && code <= 0x11374) || // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA (0x11438 <= code && code <= 0x1143F) || // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI (0x11442 <= code && code <= 0x11444) || // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA 0x11446 == code || // Mn NEWA SIGN NUKTA 0x114B0 == code || // Mc TIRHUTA VOWEL SIGN AA (0x114B3 <= code && code <= 0x114B8) || // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL 0x114BA == code || // Mn TIRHUTA VOWEL SIGN SHORT E 0x114BD == code || // Mc TIRHUTA VOWEL SIGN SHORT O (0x114BF <= code && code <= 0x114C0) || // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA (0x114C2 <= code && code <= 0x114C3) || // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA 0x115AF == code || // Mc SIDDHAM VOWEL SIGN AA (0x115B2 <= code && code <= 0x115B5) || // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR (0x115BC <= code && code <= 0x115BD) || // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA (0x115BF <= code && code <= 0x115C0) || // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA (0x115DC <= code && code <= 0x115DD) || // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU (0x11633 <= code && code <= 0x1163A) || // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI 0x1163D == code || // Mn MODI SIGN ANUSVARA (0x1163F <= code && code <= 0x11640) || // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA 0x116AB == code || // Mn TAKRI SIGN ANUSVARA 0x116AD == code || // Mn TAKRI VOWEL SIGN AA (0x116B0 <= code && code <= 0x116B5) || // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU 0x116B7 == code || // Mn TAKRI SIGN NUKTA (0x1171D <= code && code <= 0x1171F) || // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA (0x11722 <= code && code <= 0x11725) || // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU (0x11727 <= code && code <= 0x1172B) || // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER (0x11A01 <= code && code <= 0x11A06) || // Mn [6] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL SIGN O (0x11A09 <= code && code <= 0x11A0A) || // Mn [2] ZANABAZAR SQUARE VOWEL SIGN REVERSED I..ZANABAZAR SQUARE VOWEL LENGTH MARK (0x11A33 <= code && code <= 0x11A38) || // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA (0x11A3B <= code && code <= 0x11A3E) || // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA 0x11A47 == code || // Mn ZANABAZAR SQUARE SUBJOINER (0x11A51 <= code && code <= 0x11A56) || // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE (0x11A59 <= code && code <= 0x11A5B) || // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK (0x11A8A <= code && code <= 0x11A96) || // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA (0x11A98 <= code && code <= 0x11A99) || // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER (0x11C30 <= code && code <= 0x11C36) || // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L (0x11C38 <= code && code <= 0x11C3D) || // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 0x11C3F == code || // Mn BHAIKSUKI SIGN VIRAMA (0x11C92 <= code && code <= 0x11CA7) || // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA (0x11CAA <= code && code <= 0x11CB0) || // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA (0x11CB2 <= code && code <= 0x11CB3) || // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E (0x11CB5 <= code && code <= 0x11CB6) || // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU (0x11D31 <= code && code <= 0x11D36) || // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R 0x11D3A == code || // Mn MASARAM GONDI VOWEL SIGN E (0x11D3C <= code && code <= 0x11D3D) || // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O (0x11D3F <= code && code <= 0x11D45) || // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA 0x11D47 == code || // Mn MASARAM GONDI RA-KARA (0x16AF0 <= code && code <= 0x16AF4) || // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE (0x16B30 <= code && code <= 0x16B36) || // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM (0x16F8F <= code && code <= 0x16F92) || // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW (0x1BC9D <= code && code <= 0x1BC9E) || // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK 0x1D165 == code || // Mc MUSICAL SYMBOL COMBINING STEM (0x1D167 <= code && code <= 0x1D169) || // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 (0x1D16E <= code && code <= 0x1D172) || // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 (0x1D17B <= code && code <= 0x1D182) || // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE (0x1D185 <= code && code <= 0x1D18B) || // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE (0x1D1AA <= code && code <= 0x1D1AD) || // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO (0x1D242 <= code && code <= 0x1D244) || // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME (0x1DA00 <= code && code <= 0x1DA36) || // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN (0x1DA3B <= code && code <= 0x1DA6C) || // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT 0x1DA75 == code || // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS 0x1DA84 == code || // Mn SIGNWRITING LOCATION HEAD NECK (0x1DA9B <= code && code <= 0x1DA9F) || // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 (0x1DAA1 <= code && code <= 0x1DAAF) || // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 (0x1E000 <= code && code <= 0x1E006) || // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE (0x1E008 <= code && code <= 0x1E018) || // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU (0x1E01B <= code && code <= 0x1E021) || // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI (0x1E023 <= code && code <= 0x1E024) || // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS (0x1E026 <= code && code <= 0x1E02A) || // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA (0x1E8D0 <= code && code <= 0x1E8D6) || // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS (0x1E944 <= code && code <= 0x1E94A) || // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA (0xE0020 <= code && code <= 0xE007F) || // Cf [96] TAG SPACE..CANCEL TAG (0xE0100 <= code && code <= 0xE01EF) // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 ){ return Extend; } if( (0x1F1E6 <= code && code <= 0x1F1FF) // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z ){ return Regional_Indicator; } if( 0x0903 == code || // Mc DEVANAGARI SIGN VISARGA 0x093B == code || // Mc DEVANAGARI VOWEL SIGN OOE (0x093E <= code && code <= 0x0940) || // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II (0x0949 <= code && code <= 0x094C) || // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU (0x094E <= code && code <= 0x094F) || // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW (0x0982 <= code && code <= 0x0983) || // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA (0x09BF <= code && code <= 0x09C0) || // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II (0x09C7 <= code && code <= 0x09C8) || // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI (0x09CB <= code && code <= 0x09CC) || // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU 0x0A03 == code || // Mc GURMUKHI SIGN VISARGA (0x0A3E <= code && code <= 0x0A40) || // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II 0x0A83 == code || // Mc GUJARATI SIGN VISARGA (0x0ABE <= code && code <= 0x0AC0) || // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II 0x0AC9 == code || // Mc GUJARATI VOWEL SIGN CANDRA O (0x0ACB <= code && code <= 0x0ACC) || // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU (0x0B02 <= code && code <= 0x0B03) || // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA 0x0B40 == code || // Mc ORIYA VOWEL SIGN II (0x0B47 <= code && code <= 0x0B48) || // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI (0x0B4B <= code && code <= 0x0B4C) || // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU 0x0BBF == code || // Mc TAMIL VOWEL SIGN I (0x0BC1 <= code && code <= 0x0BC2) || // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU (0x0BC6 <= code && code <= 0x0BC8) || // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI (0x0BCA <= code && code <= 0x0BCC) || // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU (0x0C01 <= code && code <= 0x0C03) || // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA (0x0C41 <= code && code <= 0x0C44) || // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR (0x0C82 <= code && code <= 0x0C83) || // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA 0x0CBE == code || // Mc KANNADA VOWEL SIGN AA (0x0CC0 <= code && code <= 0x0CC1) || // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U (0x0CC3 <= code && code <= 0x0CC4) || // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR (0x0CC7 <= code && code <= 0x0CC8) || // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI (0x0CCA <= code && code <= 0x0CCB) || // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO (0x0D02 <= code && code <= 0x0D03) || // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA (0x0D3F <= code && code <= 0x0D40) || // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II (0x0D46 <= code && code <= 0x0D48) || // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI (0x0D4A <= code && code <= 0x0D4C) || // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU (0x0D82 <= code && code <= 0x0D83) || // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA (0x0DD0 <= code && code <= 0x0DD1) || // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA (0x0DD8 <= code && code <= 0x0DDE) || // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA (0x0DF2 <= code && code <= 0x0DF3) || // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA 0x0E33 == code || // Lo THAI CHARACTER SARA AM 0x0EB3 == code || // Lo LAO VOWEL SIGN AM (0x0F3E <= code && code <= 0x0F3F) || // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES 0x0F7F == code || // Mc TIBETAN SIGN RNAM BCAD 0x1031 == code || // Mc MYANMAR VOWEL SIGN E (0x103B <= code && code <= 0x103C) || // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA (0x1056 <= code && code <= 0x1057) || // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR 0x1084 == code || // Mc MYANMAR VOWEL SIGN SHAN E 0x17B6 == code || // Mc KHMER VOWEL SIGN AA (0x17BE <= code && code <= 0x17C5) || // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU (0x17C7 <= code && code <= 0x17C8) || // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU (0x1923 <= code && code <= 0x1926) || // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU (0x1929 <= code && code <= 0x192B) || // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA (0x1930 <= code && code <= 0x1931) || // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA (0x1933 <= code && code <= 0x1938) || // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA (0x1A19 <= code && code <= 0x1A1A) || // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O 0x1A55 == code || // Mc TAI THAM CONSONANT SIGN MEDIAL RA 0x1A57 == code || // Mc TAI THAM CONSONANT SIGN LA TANG LAI (0x1A6D <= code && code <= 0x1A72) || // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI 0x1B04 == code || // Mc BALINESE SIGN BISAH 0x1B35 == code || // Mc BALINESE VOWEL SIGN TEDUNG 0x1B3B == code || // Mc BALINESE VOWEL SIGN RA REPA TEDUNG (0x1B3D <= code && code <= 0x1B41) || // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG (0x1B43 <= code && code <= 0x1B44) || // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG 0x1B82 == code || // Mc SUNDANESE SIGN PANGWISAD 0x1BA1 == code || // Mc SUNDANESE CONSONANT SIGN PAMINGKAL (0x1BA6 <= code && code <= 0x1BA7) || // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG 0x1BAA == code || // Mc SUNDANESE SIGN PAMAAEH 0x1BE7 == code || // Mc BATAK VOWEL SIGN E (0x1BEA <= code && code <= 0x1BEC) || // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O 0x1BEE == code || // Mc BATAK VOWEL SIGN U (0x1BF2 <= code && code <= 0x1BF3) || // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN (0x1C24 <= code && code <= 0x1C2B) || // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU (0x1C34 <= code && code <= 0x1C35) || // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG 0x1CE1 == code || // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA (0x1CF2 <= code && code <= 0x1CF3) || // Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA 0x1CF7 == code || // Mc VEDIC SIGN ATIKRAMA (0xA823 <= code && code <= 0xA824) || // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I 0xA827 == code || // Mc SYLOTI NAGRI VOWEL SIGN OO (0xA880 <= code && code <= 0xA881) || // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA (0xA8B4 <= code && code <= 0xA8C3) || // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU (0xA952 <= code && code <= 0xA953) || // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA 0xA983 == code || // Mc JAVANESE SIGN WIGNYAN (0xA9B4 <= code && code <= 0xA9B5) || // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG (0xA9BA <= code && code <= 0xA9BB) || // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE (0xA9BD <= code && code <= 0xA9C0) || // Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON (0xAA2F <= code && code <= 0xAA30) || // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI (0xAA33 <= code && code <= 0xAA34) || // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA 0xAA4D == code || // Mc CHAM CONSONANT SIGN FINAL H 0xAAEB == code || // Mc MEETEI MAYEK VOWEL SIGN II (0xAAEE <= code && code <= 0xAAEF) || // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU 0xAAF5 == code || // Mc MEETEI MAYEK VOWEL SIGN VISARGA (0xABE3 <= code && code <= 0xABE4) || // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP (0xABE6 <= code && code <= 0xABE7) || // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP (0xABE9 <= code && code <= 0xABEA) || // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG 0xABEC == code || // Mc MEETEI MAYEK LUM IYEK 0x11000 == code || // Mc BRAHMI SIGN CANDRABINDU 0x11002 == code || // Mc BRAHMI SIGN VISARGA 0x11082 == code || // Mc KAITHI SIGN VISARGA (0x110B0 <= code && code <= 0x110B2) || // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II (0x110B7 <= code && code <= 0x110B8) || // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU 0x1112C == code || // Mc CHAKMA VOWEL SIGN E 0x11182 == code || // Mc SHARADA SIGN VISARGA (0x111B3 <= code && code <= 0x111B5) || // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II (0x111BF <= code && code <= 0x111C0) || // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA (0x1122C <= code && code <= 0x1122E) || // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II (0x11232 <= code && code <= 0x11233) || // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU 0x11235 == code || // Mc KHOJKI SIGN VIRAMA (0x112E0 <= code && code <= 0x112E2) || // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II (0x11302 <= code && code <= 0x11303) || // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA 0x1133F == code || // Mc GRANTHA VOWEL SIGN I (0x11341 <= code && code <= 0x11344) || // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR (0x11347 <= code && code <= 0x11348) || // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI (0x1134B <= code && code <= 0x1134D) || // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA (0x11362 <= code && code <= 0x11363) || // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL (0x11435 <= code && code <= 0x11437) || // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II (0x11440 <= code && code <= 0x11441) || // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU 0x11445 == code || // Mc NEWA SIGN VISARGA (0x114B1 <= code && code <= 0x114B2) || // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II 0x114B9 == code || // Mc TIRHUTA VOWEL SIGN E (0x114BB <= code && code <= 0x114BC) || // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O 0x114BE == code || // Mc TIRHUTA VOWEL SIGN AU 0x114C1 == code || // Mc TIRHUTA SIGN VISARGA (0x115B0 <= code && code <= 0x115B1) || // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II (0x115B8 <= code && code <= 0x115BB) || // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU 0x115BE == code || // Mc SIDDHAM SIGN VISARGA (0x11630 <= code && code <= 0x11632) || // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II (0x1163B <= code && code <= 0x1163C) || // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU 0x1163E == code || // Mc MODI SIGN VISARGA 0x116AC == code || // Mc TAKRI SIGN VISARGA (0x116AE <= code && code <= 0x116AF) || // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II 0x116B6 == code || // Mc TAKRI SIGN VIRAMA (0x11720 <= code && code <= 0x11721) || // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA 0x11726 == code || // Mc AHOM VOWEL SIGN E (0x11A07 <= code && code <= 0x11A08) || // Mc [2] ZANABAZAR SQUARE VOWEL SIGN AI..ZANABAZAR SQUARE VOWEL SIGN AU 0x11A39 == code || // Mc ZANABAZAR SQUARE SIGN VISARGA (0x11A57 <= code && code <= 0x11A58) || // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU 0x11A97 == code || // Mc SOYOMBO SIGN VISARGA 0x11C2F == code || // Mc BHAIKSUKI VOWEL SIGN AA 0x11C3E == code || // Mc BHAIKSUKI SIGN VISARGA 0x11CA9 == code || // Mc MARCHEN SUBJOINED LETTER YA 0x11CB1 == code || // Mc MARCHEN VOWEL SIGN I 0x11CB4 == code || // Mc MARCHEN VOWEL SIGN O (0x16F51 <= code && code <= 0x16F7E) || // Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG 0x1D166 == code || // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM 0x1D16D == code // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT ){ return SpacingMark; } if( (0x1100 <= code && code <= 0x115F) || // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER (0xA960 <= code && code <= 0xA97C) // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH ){ return L; } if( (0x1160 <= code && code <= 0x11A7) || // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE (0xD7B0 <= code && code <= 0xD7C6) // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E ){ return V; } if( (0x11A8 <= code && code <= 0x11FF) || // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN (0xD7CB <= code && code <= 0xD7FB) // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH ){ return T; } if( 0xAC00 == code || // Lo HANGUL SYLLABLE GA 0xAC1C == code || // Lo HANGUL SYLLABLE GAE 0xAC38 == code || // Lo HANGUL SYLLABLE GYA 0xAC54 == code || // Lo HANGUL SYLLABLE GYAE 0xAC70 == code || // Lo HANGUL SYLLABLE GEO 0xAC8C == code || // Lo HANGUL SYLLABLE GE 0xACA8 == code || // Lo HANGUL SYLLABLE GYEO 0xACC4 == code || // Lo HANGUL SYLLABLE GYE 0xACE0 == code || // Lo HANGUL SYLLABLE GO 0xACFC == code || // Lo HANGUL SYLLABLE GWA 0xAD18 == code || // Lo HANGUL SYLLABLE GWAE 0xAD34 == code || // Lo HANGUL SYLLABLE GOE 0xAD50 == code || // Lo HANGUL SYLLABLE GYO 0xAD6C == code || // Lo HANGUL SYLLABLE GU 0xAD88 == code || // Lo HANGUL SYLLABLE GWEO 0xADA4 == code || // Lo HANGUL SYLLABLE GWE 0xADC0 == code || // Lo HANGUL SYLLABLE GWI 0xADDC == code || // Lo HANGUL SYLLABLE GYU 0xADF8 == code || // Lo HANGUL SYLLABLE GEU 0xAE14 == code || // Lo HANGUL SYLLABLE GYI 0xAE30 == code || // Lo HANGUL SYLLABLE GI 0xAE4C == code || // Lo HANGUL SYLLABLE GGA 0xAE68 == code || // Lo HANGUL SYLLABLE GGAE 0xAE84 == code || // Lo HANGUL SYLLABLE GGYA 0xAEA0 == code || // Lo HANGUL SYLLABLE GGYAE 0xAEBC == code || // Lo HANGUL SYLLABLE GGEO 0xAED8 == code || // Lo HANGUL SYLLABLE GGE 0xAEF4 == code || // Lo HANGUL SYLLABLE GGYEO 0xAF10 == code || // Lo HANGUL SYLLABLE GGYE 0xAF2C == code || // Lo HANGUL SYLLABLE GGO 0xAF48 == code || // Lo HANGUL SYLLABLE GGWA 0xAF64 == code || // Lo HANGUL SYLLABLE GGWAE 0xAF80 == code || // Lo HANGUL SYLLABLE GGOE 0xAF9C == code || // Lo HANGUL SYLLABLE GGYO 0xAFB8 == code || // Lo HANGUL SYLLABLE GGU 0xAFD4 == code || // Lo HANGUL SYLLABLE GGWEO 0xAFF0 == code || // Lo HANGUL SYLLABLE GGWE 0xB00C == code || // Lo HANGUL SYLLABLE GGWI 0xB028 == code || // Lo HANGUL SYLLABLE GGYU 0xB044 == code || // Lo HANGUL SYLLABLE GGEU 0xB060 == code || // Lo HANGUL SYLLABLE GGYI 0xB07C == code || // Lo HANGUL SYLLABLE GGI 0xB098 == code || // Lo HANGUL SYLLABLE NA 0xB0B4 == code || // Lo HANGUL SYLLABLE NAE 0xB0D0 == code || // Lo HANGUL SYLLABLE NYA 0xB0EC == code || // Lo HANGUL SYLLABLE NYAE 0xB108 == code || // Lo HANGUL SYLLABLE NEO 0xB124 == code || // Lo HANGUL SYLLABLE NE 0xB140 == code || // Lo HANGUL SYLLABLE NYEO 0xB15C == code || // Lo HANGUL SYLLABLE NYE 0xB178 == code || // Lo HANGUL SYLLABLE NO 0xB194 == code || // Lo HANGUL SYLLABLE NWA 0xB1B0 == code || // Lo HANGUL SYLLABLE NWAE 0xB1CC == code || // Lo HANGUL SYLLABLE NOE 0xB1E8 == code || // Lo HANGUL SYLLABLE NYO 0xB204 == code || // Lo HANGUL SYLLABLE NU 0xB220 == code || // Lo HANGUL SYLLABLE NWEO 0xB23C == code || // Lo HANGUL SYLLABLE NWE 0xB258 == code || // Lo HANGUL SYLLABLE NWI 0xB274 == code || // Lo HANGUL SYLLABLE NYU 0xB290 == code || // Lo HANGUL SYLLABLE NEU 0xB2AC == code || // Lo HANGUL SYLLABLE NYI 0xB2C8 == code || // Lo HANGUL SYLLABLE NI 0xB2E4 == code || // Lo HANGUL SYLLABLE DA 0xB300 == code || // Lo HANGUL SYLLABLE DAE 0xB31C == code || // Lo HANGUL SYLLABLE DYA 0xB338 == code || // Lo HANGUL SYLLABLE DYAE 0xB354 == code || // Lo HANGUL SYLLABLE DEO 0xB370 == code || // Lo HANGUL SYLLABLE DE 0xB38C == code || // Lo HANGUL SYLLABLE DYEO 0xB3A8 == code || // Lo HANGUL SYLLABLE DYE 0xB3C4 == code || // Lo HANGUL SYLLABLE DO 0xB3E0 == code || // Lo HANGUL SYLLABLE DWA 0xB3FC == code || // Lo HANGUL SYLLABLE DWAE 0xB418 == code || // Lo HANGUL SYLLABLE DOE 0xB434 == code || // Lo HANGUL SYLLABLE DYO 0xB450 == code || // Lo HANGUL SYLLABLE DU 0xB46C == code || // Lo HANGUL SYLLABLE DWEO 0xB488 == code || // Lo HANGUL SYLLABLE DWE 0xB4A4 == code || // Lo HANGUL SYLLABLE DWI 0xB4C0 == code || // Lo HANGUL SYLLABLE DYU 0xB4DC == code || // Lo HANGUL SYLLABLE DEU 0xB4F8 == code || // Lo HANGUL SYLLABLE DYI 0xB514 == code || // Lo HANGUL SYLLABLE DI 0xB530 == code || // Lo HANGUL SYLLABLE DDA 0xB54C == code || // Lo HANGUL SYLLABLE DDAE 0xB568 == code || // Lo HANGUL SYLLABLE DDYA 0xB584 == code || // Lo HANGUL SYLLABLE DDYAE 0xB5A0 == code || // Lo HANGUL SYLLABLE DDEO 0xB5BC == code || // Lo HANGUL SYLLABLE DDE 0xB5D8 == code || // Lo HANGUL SYLLABLE DDYEO 0xB5F4 == code || // Lo HANGUL SYLLABLE DDYE 0xB610 == code || // Lo HANGUL SYLLABLE DDO 0xB62C == code || // Lo HANGUL SYLLABLE DDWA 0xB648 == code || // Lo HANGUL SYLLABLE DDWAE 0xB664 == code || // Lo HANGUL SYLLABLE DDOE 0xB680 == code || // Lo HANGUL SYLLABLE DDYO 0xB69C == code || // Lo HANGUL SYLLABLE DDU 0xB6B8 == code || // Lo HANGUL SYLLABLE DDWEO 0xB6D4 == code || // Lo HANGUL SYLLABLE DDWE 0xB6F0 == code || // Lo HANGUL SYLLABLE DDWI 0xB70C == code || // Lo HANGUL SYLLABLE DDYU 0xB728 == code || // Lo HANGUL SYLLABLE DDEU 0xB744 == code || // Lo HANGUL SYLLABLE DDYI 0xB760 == code || // Lo HANGUL SYLLABLE DDI 0xB77C == code || // Lo HANGUL SYLLABLE RA 0xB798 == code || // Lo HANGUL SYLLABLE RAE 0xB7B4 == code || // Lo HANGUL SYLLABLE RYA 0xB7D0 == code || // Lo HANGUL SYLLABLE RYAE 0xB7EC == code || // Lo HANGUL SYLLABLE REO 0xB808 == code || // Lo HANGUL SYLLABLE RE 0xB824 == code || // Lo HANGUL SYLLABLE RYEO 0xB840 == code || // Lo HANGUL SYLLABLE RYE 0xB85C == code || // Lo HANGUL SYLLABLE RO 0xB878 == code || // Lo HANGUL SYLLABLE RWA 0xB894 == code || // Lo HANGUL SYLLABLE RWAE 0xB8B0 == code || // Lo HANGUL SYLLABLE ROE 0xB8CC == code || // Lo HANGUL SYLLABLE RYO 0xB8E8 == code || // Lo HANGUL SYLLABLE RU 0xB904 == code || // Lo HANGUL SYLLABLE RWEO 0xB920 == code || // Lo HANGUL SYLLABLE RWE 0xB93C == code || // Lo HANGUL SYLLABLE RWI 0xB958 == code || // Lo HANGUL SYLLABLE RYU 0xB974 == code || // Lo HANGUL SYLLABLE REU 0xB990 == code || // Lo HANGUL SYLLABLE RYI 0xB9AC == code || // Lo HANGUL SYLLABLE RI 0xB9C8 == code || // Lo HANGUL SYLLABLE MA 0xB9E4 == code || // Lo HANGUL SYLLABLE MAE 0xBA00 == code || // Lo HANGUL SYLLABLE MYA 0xBA1C == code || // Lo HANGUL SYLLABLE MYAE 0xBA38 == code || // Lo HANGUL SYLLABLE MEO 0xBA54 == code || // Lo HANGUL SYLLABLE ME 0xBA70 == code || // Lo HANGUL SYLLABLE MYEO 0xBA8C == code || // Lo HANGUL SYLLABLE MYE 0xBAA8 == code || // Lo HANGUL SYLLABLE MO 0xBAC4 == code || // Lo HANGUL SYLLABLE MWA 0xBAE0 == code || // Lo HANGUL SYLLABLE MWAE 0xBAFC == code || // Lo HANGUL SYLLABLE MOE 0xBB18 == code || // Lo HANGUL SYLLABLE MYO 0xBB34 == code || // Lo HANGUL SYLLABLE MU 0xBB50 == code || // Lo HANGUL SYLLABLE MWEO 0xBB6C == code || // Lo HANGUL SYLLABLE MWE 0xBB88 == code || // Lo HANGUL SYLLABLE MWI 0xBBA4 == code || // Lo HANGUL SYLLABLE MYU 0xBBC0 == code || // Lo HANGUL SYLLABLE MEU 0xBBDC == code || // Lo HANGUL SYLLABLE MYI 0xBBF8 == code || // Lo HANGUL SYLLABLE MI 0xBC14 == code || // Lo HANGUL SYLLABLE BA 0xBC30 == code || // Lo HANGUL SYLLABLE BAE 0xBC4C == code || // Lo HANGUL SYLLABLE BYA 0xBC68 == code || // Lo HANGUL SYLLABLE BYAE 0xBC84 == code || // Lo HANGUL SYLLABLE BEO 0xBCA0 == code || // Lo HANGUL SYLLABLE BE 0xBCBC == code || // Lo HANGUL SYLLABLE BYEO 0xBCD8 == code || // Lo HANGUL SYLLABLE BYE 0xBCF4 == code || // Lo HANGUL SYLLABLE BO 0xBD10 == code || // Lo HANGUL SYLLABLE BWA 0xBD2C == code || // Lo HANGUL SYLLABLE BWAE 0xBD48 == code || // Lo HANGUL SYLLABLE BOE 0xBD64 == code || // Lo HANGUL SYLLABLE BYO 0xBD80 == code || // Lo HANGUL SYLLABLE BU 0xBD9C == code || // Lo HANGUL SYLLABLE BWEO 0xBDB8 == code || // Lo HANGUL SYLLABLE BWE 0xBDD4 == code || // Lo HANGUL SYLLABLE BWI 0xBDF0 == code || // Lo HANGUL SYLLABLE BYU 0xBE0C == code || // Lo HANGUL SYLLABLE BEU 0xBE28 == code || // Lo HANGUL SYLLABLE BYI 0xBE44 == code || // Lo HANGUL SYLLABLE BI 0xBE60 == code || // Lo HANGUL SYLLABLE BBA 0xBE7C == code || // Lo HANGUL SYLLABLE BBAE 0xBE98 == code || // Lo HANGUL SYLLABLE BBYA 0xBEB4 == code || // Lo HANGUL SYLLABLE BBYAE 0xBED0 == code || // Lo HANGUL SYLLABLE BBEO 0xBEEC == code || // Lo HANGUL SYLLABLE BBE 0xBF08 == code || // Lo HANGUL SYLLABLE BBYEO 0xBF24 == code || // Lo HANGUL SYLLABLE BBYE 0xBF40 == code || // Lo HANGUL SYLLABLE BBO 0xBF5C == code || // Lo HANGUL SYLLABLE BBWA 0xBF78 == code || // Lo HANGUL SYLLABLE BBWAE 0xBF94 == code || // Lo HANGUL SYLLABLE BBOE 0xBFB0 == code || // Lo HANGUL SYLLABLE BBYO 0xBFCC == code || // Lo HANGUL SYLLABLE BBU 0xBFE8 == code || // Lo HANGUL SYLLABLE BBWEO 0xC004 == code || // Lo HANGUL SYLLABLE BBWE 0xC020 == code || // Lo HANGUL SYLLABLE BBWI 0xC03C == code || // Lo HANGUL SYLLABLE BBYU 0xC058 == code || // Lo HANGUL SYLLABLE BBEU 0xC074 == code || // Lo HANGUL SYLLABLE BBYI 0xC090 == code || // Lo HANGUL SYLLABLE BBI 0xC0AC == code || // Lo HANGUL SYLLABLE SA 0xC0C8 == code || // Lo HANGUL SYLLABLE SAE 0xC0E4 == code || // Lo HANGUL SYLLABLE SYA 0xC100 == code || // Lo HANGUL SYLLABLE SYAE 0xC11C == code || // Lo HANGUL SYLLABLE SEO 0xC138 == code || // Lo HANGUL SYLLABLE SE 0xC154 == code || // Lo HANGUL SYLLABLE SYEO 0xC170 == code || // Lo HANGUL SYLLABLE SYE 0xC18C == code || // Lo HANGUL SYLLABLE SO 0xC1A8 == code || // Lo HANGUL SYLLABLE SWA 0xC1C4 == code || // Lo HANGUL SYLLABLE SWAE 0xC1E0 == code || // Lo HANGUL SYLLABLE SOE 0xC1FC == code || // Lo HANGUL SYLLABLE SYO 0xC218 == code || // Lo HANGUL SYLLABLE SU 0xC234 == code || // Lo HANGUL SYLLABLE SWEO 0xC250 == code || // Lo HANGUL SYLLABLE SWE 0xC26C == code || // Lo HANGUL SYLLABLE SWI 0xC288 == code || // Lo HANGUL SYLLABLE SYU 0xC2A4 == code || // Lo HANGUL SYLLABLE SEU 0xC2C0 == code || // Lo HANGUL SYLLABLE SYI 0xC2DC == code || // Lo HANGUL SYLLABLE SI 0xC2F8 == code || // Lo HANGUL SYLLABLE SSA 0xC314 == code || // Lo HANGUL SYLLABLE SSAE 0xC330 == code || // Lo HANGUL SYLLABLE SSYA 0xC34C == code || // Lo HANGUL SYLLABLE SSYAE 0xC368 == code || // Lo HANGUL SYLLABLE SSEO 0xC384 == code || // Lo HANGUL SYLLABLE SSE 0xC3A0 == code || // Lo HANGUL SYLLABLE SSYEO 0xC3BC == code || // Lo HANGUL SYLLABLE SSYE 0xC3D8 == code || // Lo HANGUL SYLLABLE SSO 0xC3F4 == code || // Lo HANGUL SYLLABLE SSWA 0xC410 == code || // Lo HANGUL SYLLABLE SSWAE 0xC42C == code || // Lo HANGUL SYLLABLE SSOE 0xC448 == code || // Lo HANGUL SYLLABLE SSYO 0xC464 == code || // Lo HANGUL SYLLABLE SSU 0xC480 == code || // Lo HANGUL SYLLABLE SSWEO 0xC49C == code || // Lo HANGUL SYLLABLE SSWE 0xC4B8 == code || // Lo HANGUL SYLLABLE SSWI 0xC4D4 == code || // Lo HANGUL SYLLABLE SSYU 0xC4F0 == code || // Lo HANGUL SYLLABLE SSEU 0xC50C == code || // Lo HANGUL SYLLABLE SSYI 0xC528 == code || // Lo HANGUL SYLLABLE SSI 0xC544 == code || // Lo HANGUL SYLLABLE A 0xC560 == code || // Lo HANGUL SYLLABLE AE 0xC57C == code || // Lo HANGUL SYLLABLE YA 0xC598 == code || // Lo HANGUL SYLLABLE YAE 0xC5B4 == code || // Lo HANGUL SYLLABLE EO 0xC5D0 == code || // Lo HANGUL SYLLABLE E 0xC5EC == code || // Lo HANGUL SYLLABLE YEO 0xC608 == code || // Lo HANGUL SYLLABLE YE 0xC624 == code || // Lo HANGUL SYLLABLE O 0xC640 == code || // Lo HANGUL SYLLABLE WA 0xC65C == code || // Lo HANGUL SYLLABLE WAE 0xC678 == code || // Lo HANGUL SYLLABLE OE 0xC694 == code || // Lo HANGUL SYLLABLE YO 0xC6B0 == code || // Lo HANGUL SYLLABLE U 0xC6CC == code || // Lo HANGUL SYLLABLE WEO 0xC6E8 == code || // Lo HANGUL SYLLABLE WE 0xC704 == code || // Lo HANGUL SYLLABLE WI 0xC720 == code || // Lo HANGUL SYLLABLE YU 0xC73C == code || // Lo HANGUL SYLLABLE EU 0xC758 == code || // Lo HANGUL SYLLABLE YI 0xC774 == code || // Lo HANGUL SYLLABLE I 0xC790 == code || // Lo HANGUL SYLLABLE JA 0xC7AC == code || // Lo HANGUL SYLLABLE JAE 0xC7C8 == code || // Lo HANGUL SYLLABLE JYA 0xC7E4 == code || // Lo HANGUL SYLLABLE JYAE 0xC800 == code || // Lo HANGUL SYLLABLE JEO 0xC81C == code || // Lo HANGUL SYLLABLE JE 0xC838 == code || // Lo HANGUL SYLLABLE JYEO 0xC854 == code || // Lo HANGUL SYLLABLE JYE 0xC870 == code || // Lo HANGUL SYLLABLE JO 0xC88C == code || // Lo HANGUL SYLLABLE JWA 0xC8A8 == code || // Lo HANGUL SYLLABLE JWAE 0xC8C4 == code || // Lo HANGUL SYLLABLE JOE 0xC8E0 == code || // Lo HANGUL SYLLABLE JYO 0xC8FC == code || // Lo HANGUL SYLLABLE JU 0xC918 == code || // Lo HANGUL SYLLABLE JWEO 0xC934 == code || // Lo HANGUL SYLLABLE JWE 0xC950 == code || // Lo HANGUL SYLLABLE JWI 0xC96C == code || // Lo HANGUL SYLLABLE JYU 0xC988 == code || // Lo HANGUL SYLLABLE JEU 0xC9A4 == code || // Lo HANGUL SYLLABLE JYI 0xC9C0 == code || // Lo HANGUL SYLLABLE JI 0xC9DC == code || // Lo HANGUL SYLLABLE JJA 0xC9F8 == code || // Lo HANGUL SYLLABLE JJAE 0xCA14 == code || // Lo HANGUL SYLLABLE JJYA 0xCA30 == code || // Lo HANGUL SYLLABLE JJYAE 0xCA4C == code || // Lo HANGUL SYLLABLE JJEO 0xCA68 == code || // Lo HANGUL SYLLABLE JJE 0xCA84 == code || // Lo HANGUL SYLLABLE JJYEO 0xCAA0 == code || // Lo HANGUL SYLLABLE JJYE 0xCABC == code || // Lo HANGUL SYLLABLE JJO 0xCAD8 == code || // Lo HANGUL SYLLABLE JJWA 0xCAF4 == code || // Lo HANGUL SYLLABLE JJWAE 0xCB10 == code || // Lo HANGUL SYLLABLE JJOE 0xCB2C == code || // Lo HANGUL SYLLABLE JJYO 0xCB48 == code || // Lo HANGUL SYLLABLE JJU 0xCB64 == code || // Lo HANGUL SYLLABLE JJWEO 0xCB80 == code || // Lo HANGUL SYLLABLE JJWE 0xCB9C == code || // Lo HANGUL SYLLABLE JJWI 0xCBB8 == code || // Lo HANGUL SYLLABLE JJYU 0xCBD4 == code || // Lo HANGUL SYLLABLE JJEU 0xCBF0 == code || // Lo HANGUL SYLLABLE JJYI 0xCC0C == code || // Lo HANGUL SYLLABLE JJI 0xCC28 == code || // Lo HANGUL SYLLABLE CA 0xCC44 == code || // Lo HANGUL SYLLABLE CAE 0xCC60 == code || // Lo HANGUL SYLLABLE CYA 0xCC7C == code || // Lo HANGUL SYLLABLE CYAE 0xCC98 == code || // Lo HANGUL SYLLABLE CEO 0xCCB4 == code || // Lo HANGUL SYLLABLE CE 0xCCD0 == code || // Lo HANGUL SYLLABLE CYEO 0xCCEC == code || // Lo HANGUL SYLLABLE CYE 0xCD08 == code || // Lo HANGUL SYLLABLE CO 0xCD24 == code || // Lo HANGUL SYLLABLE CWA 0xCD40 == code || // Lo HANGUL SYLLABLE CWAE 0xCD5C == code || // Lo HANGUL SYLLABLE COE 0xCD78 == code || // Lo HANGUL SYLLABLE CYO 0xCD94 == code || // Lo HANGUL SYLLABLE CU 0xCDB0 == code || // Lo HANGUL SYLLABLE CWEO 0xCDCC == code || // Lo HANGUL SYLLABLE CWE 0xCDE8 == code || // Lo HANGUL SYLLABLE CWI 0xCE04 == code || // Lo HANGUL SYLLABLE CYU 0xCE20 == code || // Lo HANGUL SYLLABLE CEU 0xCE3C == code || // Lo HANGUL SYLLABLE CYI 0xCE58 == code || // Lo HANGUL SYLLABLE CI 0xCE74 == code || // Lo HANGUL SYLLABLE KA 0xCE90 == code || // Lo HANGUL SYLLABLE KAE 0xCEAC == code || // Lo HANGUL SYLLABLE KYA 0xCEC8 == code || // Lo HANGUL SYLLABLE KYAE 0xCEE4 == code || // Lo HANGUL SYLLABLE KEO 0xCF00 == code || // Lo HANGUL SYLLABLE KE 0xCF1C == code || // Lo HANGUL SYLLABLE KYEO 0xCF38 == code || // Lo HANGUL SYLLABLE KYE 0xCF54 == code || // Lo HANGUL SYLLABLE KO 0xCF70 == code || // Lo HANGUL SYLLABLE KWA 0xCF8C == code || // Lo HANGUL SYLLABLE KWAE 0xCFA8 == code || // Lo HANGUL SYLLABLE KOE 0xCFC4 == code || // Lo HANGUL SYLLABLE KYO 0xCFE0 == code || // Lo HANGUL SYLLABLE KU 0xCFFC == code || // Lo HANGUL SYLLABLE KWEO 0xD018 == code || // Lo HANGUL SYLLABLE KWE 0xD034 == code || // Lo HANGUL SYLLABLE KWI 0xD050 == code || // Lo HANGUL SYLLABLE KYU 0xD06C == code || // Lo HANGUL SYLLABLE KEU 0xD088 == code || // Lo HANGUL SYLLABLE KYI 0xD0A4 == code || // Lo HANGUL SYLLABLE KI 0xD0C0 == code || // Lo HANGUL SYLLABLE TA 0xD0DC == code || // Lo HANGUL SYLLABLE TAE 0xD0F8 == code || // Lo HANGUL SYLLABLE TYA 0xD114 == code || // Lo HANGUL SYLLABLE TYAE 0xD130 == code || // Lo HANGUL SYLLABLE TEO 0xD14C == code || // Lo HANGUL SYLLABLE TE 0xD168 == code || // Lo HANGUL SYLLABLE TYEO 0xD184 == code || // Lo HANGUL SYLLABLE TYE 0xD1A0 == code || // Lo HANGUL SYLLABLE TO 0xD1BC == code || // Lo HANGUL SYLLABLE TWA 0xD1D8 == code || // Lo HANGUL SYLLABLE TWAE 0xD1F4 == code || // Lo HANGUL SYLLABLE TOE 0xD210 == code || // Lo HANGUL SYLLABLE TYO 0xD22C == code || // Lo HANGUL SYLLABLE TU 0xD248 == code || // Lo HANGUL SYLLABLE TWEO 0xD264 == code || // Lo HANGUL SYLLABLE TWE 0xD280 == code || // Lo HANGUL SYLLABLE TWI 0xD29C == code || // Lo HANGUL SYLLABLE TYU 0xD2B8 == code || // Lo HANGUL SYLLABLE TEU 0xD2D4 == code || // Lo HANGUL SYLLABLE TYI 0xD2F0 == code || // Lo HANGUL SYLLABLE TI 0xD30C == code || // Lo HANGUL SYLLABLE PA 0xD328 == code || // Lo HANGUL SYLLABLE PAE 0xD344 == code || // Lo HANGUL SYLLABLE PYA 0xD360 == code || // Lo HANGUL SYLLABLE PYAE 0xD37C == code || // Lo HANGUL SYLLABLE PEO 0xD398 == code || // Lo HANGUL SYLLABLE PE 0xD3B4 == code || // Lo HANGUL SYLLABLE PYEO 0xD3D0 == code || // Lo HANGUL SYLLABLE PYE 0xD3EC == code || // Lo HANGUL SYLLABLE PO 0xD408 == code || // Lo HANGUL SYLLABLE PWA 0xD424 == code || // Lo HANGUL SYLLABLE PWAE 0xD440 == code || // Lo HANGUL SYLLABLE POE 0xD45C == code || // Lo HANGUL SYLLABLE PYO 0xD478 == code || // Lo HANGUL SYLLABLE PU 0xD494 == code || // Lo HANGUL SYLLABLE PWEO 0xD4B0 == code || // Lo HANGUL SYLLABLE PWE 0xD4CC == code || // Lo HANGUL SYLLABLE PWI 0xD4E8 == code || // Lo HANGUL SYLLABLE PYU 0xD504 == code || // Lo HANGUL SYLLABLE PEU 0xD520 == code || // Lo HANGUL SYLLABLE PYI 0xD53C == code || // Lo HANGUL SYLLABLE PI 0xD558 == code || // Lo HANGUL SYLLABLE HA 0xD574 == code || // Lo HANGUL SYLLABLE HAE 0xD590 == code || // Lo HANGUL SYLLABLE HYA 0xD5AC == code || // Lo HANGUL SYLLABLE HYAE 0xD5C8 == code || // Lo HANGUL SYLLABLE HEO 0xD5E4 == code || // Lo HANGUL SYLLABLE HE 0xD600 == code || // Lo HANGUL SYLLABLE HYEO 0xD61C == code || // Lo HANGUL SYLLABLE HYE 0xD638 == code || // Lo HANGUL SYLLABLE HO 0xD654 == code || // Lo HANGUL SYLLABLE HWA 0xD670 == code || // Lo HANGUL SYLLABLE HWAE 0xD68C == code || // Lo HANGUL SYLLABLE HOE 0xD6A8 == code || // Lo HANGUL SYLLABLE HYO 0xD6C4 == code || // Lo HANGUL SYLLABLE HU 0xD6E0 == code || // Lo HANGUL SYLLABLE HWEO 0xD6FC == code || // Lo HANGUL SYLLABLE HWE 0xD718 == code || // Lo HANGUL SYLLABLE HWI 0xD734 == code || // Lo HANGUL SYLLABLE HYU 0xD750 == code || // Lo HANGUL SYLLABLE HEU 0xD76C == code || // Lo HANGUL SYLLABLE HYI 0xD788 == code // Lo HANGUL SYLLABLE HI ){ return LV; } if( (0xAC01 <= code && code <= 0xAC1B) || // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH (0xAC1D <= code && code <= 0xAC37) || // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH (0xAC39 <= code && code <= 0xAC53) || // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH (0xAC55 <= code && code <= 0xAC6F) || // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH (0xAC71 <= code && code <= 0xAC8B) || // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH (0xAC8D <= code && code <= 0xACA7) || // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH (0xACA9 <= code && code <= 0xACC3) || // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH (0xACC5 <= code && code <= 0xACDF) || // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH (0xACE1 <= code && code <= 0xACFB) || // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH (0xACFD <= code && code <= 0xAD17) || // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH (0xAD19 <= code && code <= 0xAD33) || // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH (0xAD35 <= code && code <= 0xAD4F) || // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH (0xAD51 <= code && code <= 0xAD6B) || // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH (0xAD6D <= code && code <= 0xAD87) || // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH (0xAD89 <= code && code <= 0xADA3) || // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH (0xADA5 <= code && code <= 0xADBF) || // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH (0xADC1 <= code && code <= 0xADDB) || // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH (0xADDD <= code && code <= 0xADF7) || // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH (0xADF9 <= code && code <= 0xAE13) || // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH (0xAE15 <= code && code <= 0xAE2F) || // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH (0xAE31 <= code && code <= 0xAE4B) || // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH (0xAE4D <= code && code <= 0xAE67) || // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH (0xAE69 <= code && code <= 0xAE83) || // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH (0xAE85 <= code && code <= 0xAE9F) || // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH (0xAEA1 <= code && code <= 0xAEBB) || // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH (0xAEBD <= code && code <= 0xAED7) || // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH (0xAED9 <= code && code <= 0xAEF3) || // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH (0xAEF5 <= code && code <= 0xAF0F) || // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH (0xAF11 <= code && code <= 0xAF2B) || // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH (0xAF2D <= code && code <= 0xAF47) || // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH (0xAF49 <= code && code <= 0xAF63) || // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH (0xAF65 <= code && code <= 0xAF7F) || // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH (0xAF81 <= code && code <= 0xAF9B) || // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH (0xAF9D <= code && code <= 0xAFB7) || // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH (0xAFB9 <= code && code <= 0xAFD3) || // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH (0xAFD5 <= code && code <= 0xAFEF) || // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH (0xAFF1 <= code && code <= 0xB00B) || // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH (0xB00D <= code && code <= 0xB027) || // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH (0xB029 <= code && code <= 0xB043) || // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH (0xB045 <= code && code <= 0xB05F) || // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH (0xB061 <= code && code <= 0xB07B) || // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH (0xB07D <= code && code <= 0xB097) || // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH (0xB099 <= code && code <= 0xB0B3) || // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH (0xB0B5 <= code && code <= 0xB0CF) || // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH (0xB0D1 <= code && code <= 0xB0EB) || // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH (0xB0ED <= code && code <= 0xB107) || // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH (0xB109 <= code && code <= 0xB123) || // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH (0xB125 <= code && code <= 0xB13F) || // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH (0xB141 <= code && code <= 0xB15B) || // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH (0xB15D <= code && code <= 0xB177) || // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH (0xB179 <= code && code <= 0xB193) || // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH (0xB195 <= code && code <= 0xB1AF) || // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH (0xB1B1 <= code && code <= 0xB1CB) || // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH (0xB1CD <= code && code <= 0xB1E7) || // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH (0xB1E9 <= code && code <= 0xB203) || // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH (0xB205 <= code && code <= 0xB21F) || // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH (0xB221 <= code && code <= 0xB23B) || // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH (0xB23D <= code && code <= 0xB257) || // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH (0xB259 <= code && code <= 0xB273) || // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH (0xB275 <= code && code <= 0xB28F) || // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH (0xB291 <= code && code <= 0xB2AB) || // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH (0xB2AD <= code && code <= 0xB2C7) || // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH (0xB2C9 <= code && code <= 0xB2E3) || // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH (0xB2E5 <= code && code <= 0xB2FF) || // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH (0xB301 <= code && code <= 0xB31B) || // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH (0xB31D <= code && code <= 0xB337) || // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH (0xB339 <= code && code <= 0xB353) || // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH (0xB355 <= code && code <= 0xB36F) || // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH (0xB371 <= code && code <= 0xB38B) || // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH (0xB38D <= code && code <= 0xB3A7) || // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH (0xB3A9 <= code && code <= 0xB3C3) || // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH (0xB3C5 <= code && code <= 0xB3DF) || // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH (0xB3E1 <= code && code <= 0xB3FB) || // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH (0xB3FD <= code && code <= 0xB417) || // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH (0xB419 <= code && code <= 0xB433) || // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH (0xB435 <= code && code <= 0xB44F) || // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH (0xB451 <= code && code <= 0xB46B) || // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH (0xB46D <= code && code <= 0xB487) || // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH (0xB489 <= code && code <= 0xB4A3) || // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH (0xB4A5 <= code && code <= 0xB4BF) || // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH (0xB4C1 <= code && code <= 0xB4DB) || // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH (0xB4DD <= code && code <= 0xB4F7) || // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH (0xB4F9 <= code && code <= 0xB513) || // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH (0xB515 <= code && code <= 0xB52F) || // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH (0xB531 <= code && code <= 0xB54B) || // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH (0xB54D <= code && code <= 0xB567) || // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH (0xB569 <= code && code <= 0xB583) || // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH (0xB585 <= code && code <= 0xB59F) || // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH (0xB5A1 <= code && code <= 0xB5BB) || // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH (0xB5BD <= code && code <= 0xB5D7) || // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH (0xB5D9 <= code && code <= 0xB5F3) || // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH (0xB5F5 <= code && code <= 0xB60F) || // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH (0xB611 <= code && code <= 0xB62B) || // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH (0xB62D <= code && code <= 0xB647) || // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH (0xB649 <= code && code <= 0xB663) || // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH (0xB665 <= code && code <= 0xB67F) || // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH (0xB681 <= code && code <= 0xB69B) || // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH (0xB69D <= code && code <= 0xB6B7) || // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH (0xB6B9 <= code && code <= 0xB6D3) || // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH (0xB6D5 <= code && code <= 0xB6EF) || // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH (0xB6F1 <= code && code <= 0xB70B) || // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH (0xB70D <= code && code <= 0xB727) || // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH (0xB729 <= code && code <= 0xB743) || // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH (0xB745 <= code && code <= 0xB75F) || // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH (0xB761 <= code && code <= 0xB77B) || // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH (0xB77D <= code && code <= 0xB797) || // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH (0xB799 <= code && code <= 0xB7B3) || // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH (0xB7B5 <= code && code <= 0xB7CF) || // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH (0xB7D1 <= code && code <= 0xB7EB) || // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH (0xB7ED <= code && code <= 0xB807) || // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH (0xB809 <= code && code <= 0xB823) || // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH (0xB825 <= code && code <= 0xB83F) || // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH (0xB841 <= code && code <= 0xB85B) || // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH (0xB85D <= code && code <= 0xB877) || // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH (0xB879 <= code && code <= 0xB893) || // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH (0xB895 <= code && code <= 0xB8AF) || // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH (0xB8B1 <= code && code <= 0xB8CB) || // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH (0xB8CD <= code && code <= 0xB8E7) || // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH (0xB8E9 <= code && code <= 0xB903) || // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH (0xB905 <= code && code <= 0xB91F) || // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH (0xB921 <= code && code <= 0xB93B) || // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH (0xB93D <= code && code <= 0xB957) || // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH (0xB959 <= code && code <= 0xB973) || // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH (0xB975 <= code && code <= 0xB98F) || // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH (0xB991 <= code && code <= 0xB9AB) || // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH (0xB9AD <= code && code <= 0xB9C7) || // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH (0xB9C9 <= code && code <= 0xB9E3) || // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH (0xB9E5 <= code && code <= 0xB9FF) || // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH (0xBA01 <= code && code <= 0xBA1B) || // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH (0xBA1D <= code && code <= 0xBA37) || // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH (0xBA39 <= code && code <= 0xBA53) || // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH (0xBA55 <= code && code <= 0xBA6F) || // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH (0xBA71 <= code && code <= 0xBA8B) || // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH (0xBA8D <= code && code <= 0xBAA7) || // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH (0xBAA9 <= code && code <= 0xBAC3) || // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH (0xBAC5 <= code && code <= 0xBADF) || // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH (0xBAE1 <= code && code <= 0xBAFB) || // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH (0xBAFD <= code && code <= 0xBB17) || // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH (0xBB19 <= code && code <= 0xBB33) || // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH (0xBB35 <= code && code <= 0xBB4F) || // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH (0xBB51 <= code && code <= 0xBB6B) || // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH (0xBB6D <= code && code <= 0xBB87) || // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH (0xBB89 <= code && code <= 0xBBA3) || // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH (0xBBA5 <= code && code <= 0xBBBF) || // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH (0xBBC1 <= code && code <= 0xBBDB) || // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH (0xBBDD <= code && code <= 0xBBF7) || // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH (0xBBF9 <= code && code <= 0xBC13) || // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH (0xBC15 <= code && code <= 0xBC2F) || // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH (0xBC31 <= code && code <= 0xBC4B) || // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH (0xBC4D <= code && code <= 0xBC67) || // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH (0xBC69 <= code && code <= 0xBC83) || // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH (0xBC85 <= code && code <= 0xBC9F) || // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH (0xBCA1 <= code && code <= 0xBCBB) || // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH (0xBCBD <= code && code <= 0xBCD7) || // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH (0xBCD9 <= code && code <= 0xBCF3) || // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH (0xBCF5 <= code && code <= 0xBD0F) || // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH (0xBD11 <= code && code <= 0xBD2B) || // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH (0xBD2D <= code && code <= 0xBD47) || // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH (0xBD49 <= code && code <= 0xBD63) || // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH (0xBD65 <= code && code <= 0xBD7F) || // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH (0xBD81 <= code && code <= 0xBD9B) || // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH (0xBD9D <= code && code <= 0xBDB7) || // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH (0xBDB9 <= code && code <= 0xBDD3) || // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH (0xBDD5 <= code && code <= 0xBDEF) || // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH (0xBDF1 <= code && code <= 0xBE0B) || // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH (0xBE0D <= code && code <= 0xBE27) || // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH (0xBE29 <= code && code <= 0xBE43) || // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH (0xBE45 <= code && code <= 0xBE5F) || // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH (0xBE61 <= code && code <= 0xBE7B) || // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH (0xBE7D <= code && code <= 0xBE97) || // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH (0xBE99 <= code && code <= 0xBEB3) || // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH (0xBEB5 <= code && code <= 0xBECF) || // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH (0xBED1 <= code && code <= 0xBEEB) || // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH (0xBEED <= code && code <= 0xBF07) || // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH (0xBF09 <= code && code <= 0xBF23) || // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH (0xBF25 <= code && code <= 0xBF3F) || // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH (0xBF41 <= code && code <= 0xBF5B) || // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH (0xBF5D <= code && code <= 0xBF77) || // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH (0xBF79 <= code && code <= 0xBF93) || // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH (0xBF95 <= code && code <= 0xBFAF) || // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH (0xBFB1 <= code && code <= 0xBFCB) || // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH (0xBFCD <= code && code <= 0xBFE7) || // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH (0xBFE9 <= code && code <= 0xC003) || // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH (0xC005 <= code && code <= 0xC01F) || // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH (0xC021 <= code && code <= 0xC03B) || // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH (0xC03D <= code && code <= 0xC057) || // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH (0xC059 <= code && code <= 0xC073) || // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH (0xC075 <= code && code <= 0xC08F) || // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH (0xC091 <= code && code <= 0xC0AB) || // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH (0xC0AD <= code && code <= 0xC0C7) || // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH (0xC0C9 <= code && code <= 0xC0E3) || // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH (0xC0E5 <= code && code <= 0xC0FF) || // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH (0xC101 <= code && code <= 0xC11B) || // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH (0xC11D <= code && code <= 0xC137) || // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH (0xC139 <= code && code <= 0xC153) || // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH (0xC155 <= code && code <= 0xC16F) || // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH (0xC171 <= code && code <= 0xC18B) || // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH (0xC18D <= code && code <= 0xC1A7) || // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH (0xC1A9 <= code && code <= 0xC1C3) || // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH (0xC1C5 <= code && code <= 0xC1DF) || // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH (0xC1E1 <= code && code <= 0xC1FB) || // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH (0xC1FD <= code && code <= 0xC217) || // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH (0xC219 <= code && code <= 0xC233) || // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH (0xC235 <= code && code <= 0xC24F) || // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH (0xC251 <= code && code <= 0xC26B) || // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH (0xC26D <= code && code <= 0xC287) || // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH (0xC289 <= code && code <= 0xC2A3) || // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH (0xC2A5 <= code && code <= 0xC2BF) || // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH (0xC2C1 <= code && code <= 0xC2DB) || // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH (0xC2DD <= code && code <= 0xC2F7) || // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH (0xC2F9 <= code && code <= 0xC313) || // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH (0xC315 <= code && code <= 0xC32F) || // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH (0xC331 <= code && code <= 0xC34B) || // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH (0xC34D <= code && code <= 0xC367) || // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH (0xC369 <= code && code <= 0xC383) || // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH (0xC385 <= code && code <= 0xC39F) || // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH (0xC3A1 <= code && code <= 0xC3BB) || // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH (0xC3BD <= code && code <= 0xC3D7) || // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH (0xC3D9 <= code && code <= 0xC3F3) || // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH (0xC3F5 <= code && code <= 0xC40F) || // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH (0xC411 <= code && code <= 0xC42B) || // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH (0xC42D <= code && code <= 0xC447) || // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH (0xC449 <= code && code <= 0xC463) || // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH (0xC465 <= code && code <= 0xC47F) || // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH (0xC481 <= code && code <= 0xC49B) || // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH (0xC49D <= code && code <= 0xC4B7) || // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH (0xC4B9 <= code && code <= 0xC4D3) || // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH (0xC4D5 <= code && code <= 0xC4EF) || // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH (0xC4F1 <= code && code <= 0xC50B) || // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH (0xC50D <= code && code <= 0xC527) || // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH (0xC529 <= code && code <= 0xC543) || // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH (0xC545 <= code && code <= 0xC55F) || // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH (0xC561 <= code && code <= 0xC57B) || // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH (0xC57D <= code && code <= 0xC597) || // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH (0xC599 <= code && code <= 0xC5B3) || // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH (0xC5B5 <= code && code <= 0xC5CF) || // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH (0xC5D1 <= code && code <= 0xC5EB) || // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH (0xC5ED <= code && code <= 0xC607) || // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH (0xC609 <= code && code <= 0xC623) || // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH (0xC625 <= code && code <= 0xC63F) || // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH (0xC641 <= code && code <= 0xC65B) || // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH (0xC65D <= code && code <= 0xC677) || // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH (0xC679 <= code && code <= 0xC693) || // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH (0xC695 <= code && code <= 0xC6AF) || // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH (0xC6B1 <= code && code <= 0xC6CB) || // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH (0xC6CD <= code && code <= 0xC6E7) || // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH (0xC6E9 <= code && code <= 0xC703) || // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH (0xC705 <= code && code <= 0xC71F) || // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH (0xC721 <= code && code <= 0xC73B) || // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH (0xC73D <= code && code <= 0xC757) || // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH (0xC759 <= code && code <= 0xC773) || // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH (0xC775 <= code && code <= 0xC78F) || // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH (0xC791 <= code && code <= 0xC7AB) || // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH (0xC7AD <= code && code <= 0xC7C7) || // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH (0xC7C9 <= code && code <= 0xC7E3) || // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH (0xC7E5 <= code && code <= 0xC7FF) || // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH (0xC801 <= code && code <= 0xC81B) || // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH (0xC81D <= code && code <= 0xC837) || // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH (0xC839 <= code && code <= 0xC853) || // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH (0xC855 <= code && code <= 0xC86F) || // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH (0xC871 <= code && code <= 0xC88B) || // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH (0xC88D <= code && code <= 0xC8A7) || // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH (0xC8A9 <= code && code <= 0xC8C3) || // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH (0xC8C5 <= code && code <= 0xC8DF) || // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH (0xC8E1 <= code && code <= 0xC8FB) || // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH (0xC8FD <= code && code <= 0xC917) || // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH (0xC919 <= code && code <= 0xC933) || // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH (0xC935 <= code && code <= 0xC94F) || // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH (0xC951 <= code && code <= 0xC96B) || // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH (0xC96D <= code && code <= 0xC987) || // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH (0xC989 <= code && code <= 0xC9A3) || // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH (0xC9A5 <= code && code <= 0xC9BF) || // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH (0xC9C1 <= code && code <= 0xC9DB) || // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH (0xC9DD <= code && code <= 0xC9F7) || // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH (0xC9F9 <= code && code <= 0xCA13) || // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH (0xCA15 <= code && code <= 0xCA2F) || // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH (0xCA31 <= code && code <= 0xCA4B) || // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH (0xCA4D <= code && code <= 0xCA67) || // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH (0xCA69 <= code && code <= 0xCA83) || // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH (0xCA85 <= code && code <= 0xCA9F) || // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH (0xCAA1 <= code && code <= 0xCABB) || // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH (0xCABD <= code && code <= 0xCAD7) || // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH (0xCAD9 <= code && code <= 0xCAF3) || // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH (0xCAF5 <= code && code <= 0xCB0F) || // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH (0xCB11 <= code && code <= 0xCB2B) || // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH (0xCB2D <= code && code <= 0xCB47) || // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH (0xCB49 <= code && code <= 0xCB63) || // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH (0xCB65 <= code && code <= 0xCB7F) || // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH (0xCB81 <= code && code <= 0xCB9B) || // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH (0xCB9D <= code && code <= 0xCBB7) || // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH (0xCBB9 <= code && code <= 0xCBD3) || // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH (0xCBD5 <= code && code <= 0xCBEF) || // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH (0xCBF1 <= code && code <= 0xCC0B) || // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH (0xCC0D <= code && code <= 0xCC27) || // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH (0xCC29 <= code && code <= 0xCC43) || // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH (0xCC45 <= code && code <= 0xCC5F) || // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH (0xCC61 <= code && code <= 0xCC7B) || // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH (0xCC7D <= code && code <= 0xCC97) || // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH (0xCC99 <= code && code <= 0xCCB3) || // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH (0xCCB5 <= code && code <= 0xCCCF) || // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH (0xCCD1 <= code && code <= 0xCCEB) || // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH (0xCCED <= code && code <= 0xCD07) || // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH (0xCD09 <= code && code <= 0xCD23) || // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH (0xCD25 <= code && code <= 0xCD3F) || // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH (0xCD41 <= code && code <= 0xCD5B) || // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH (0xCD5D <= code && code <= 0xCD77) || // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH (0xCD79 <= code && code <= 0xCD93) || // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH (0xCD95 <= code && code <= 0xCDAF) || // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH (0xCDB1 <= code && code <= 0xCDCB) || // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH (0xCDCD <= code && code <= 0xCDE7) || // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH (0xCDE9 <= code && code <= 0xCE03) || // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH (0xCE05 <= code && code <= 0xCE1F) || // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH (0xCE21 <= code && code <= 0xCE3B) || // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH (0xCE3D <= code && code <= 0xCE57) || // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH (0xCE59 <= code && code <= 0xCE73) || // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH (0xCE75 <= code && code <= 0xCE8F) || // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH (0xCE91 <= code && code <= 0xCEAB) || // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH (0xCEAD <= code && code <= 0xCEC7) || // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH (0xCEC9 <= code && code <= 0xCEE3) || // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH (0xCEE5 <= code && code <= 0xCEFF) || // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH (0xCF01 <= code && code <= 0xCF1B) || // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH (0xCF1D <= code && code <= 0xCF37) || // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH (0xCF39 <= code && code <= 0xCF53) || // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH (0xCF55 <= code && code <= 0xCF6F) || // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH (0xCF71 <= code && code <= 0xCF8B) || // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH (0xCF8D <= code && code <= 0xCFA7) || // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH (0xCFA9 <= code && code <= 0xCFC3) || // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH (0xCFC5 <= code && code <= 0xCFDF) || // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH (0xCFE1 <= code && code <= 0xCFFB) || // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH (0xCFFD <= code && code <= 0xD017) || // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH (0xD019 <= code && code <= 0xD033) || // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH (0xD035 <= code && code <= 0xD04F) || // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH (0xD051 <= code && code <= 0xD06B) || // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH (0xD06D <= code && code <= 0xD087) || // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH (0xD089 <= code && code <= 0xD0A3) || // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH (0xD0A5 <= code && code <= 0xD0BF) || // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH (0xD0C1 <= code && code <= 0xD0DB) || // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH (0xD0DD <= code && code <= 0xD0F7) || // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH (0xD0F9 <= code && code <= 0xD113) || // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH (0xD115 <= code && code <= 0xD12F) || // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH (0xD131 <= code && code <= 0xD14B) || // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH (0xD14D <= code && code <= 0xD167) || // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH (0xD169 <= code && code <= 0xD183) || // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH (0xD185 <= code && code <= 0xD19F) || // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH (0xD1A1 <= code && code <= 0xD1BB) || // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH (0xD1BD <= code && code <= 0xD1D7) || // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH (0xD1D9 <= code && code <= 0xD1F3) || // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH (0xD1F5 <= code && code <= 0xD20F) || // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH (0xD211 <= code && code <= 0xD22B) || // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH (0xD22D <= code && code <= 0xD247) || // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH (0xD249 <= code && code <= 0xD263) || // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH (0xD265 <= code && code <= 0xD27F) || // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH (0xD281 <= code && code <= 0xD29B) || // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH (0xD29D <= code && code <= 0xD2B7) || // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH (0xD2B9 <= code && code <= 0xD2D3) || // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH (0xD2D5 <= code && code <= 0xD2EF) || // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH (0xD2F1 <= code && code <= 0xD30B) || // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH (0xD30D <= code && code <= 0xD327) || // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH (0xD329 <= code && code <= 0xD343) || // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH (0xD345 <= code && code <= 0xD35F) || // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH (0xD361 <= code && code <= 0xD37B) || // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH (0xD37D <= code && code <= 0xD397) || // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH (0xD399 <= code && code <= 0xD3B3) || // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH (0xD3B5 <= code && code <= 0xD3CF) || // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH (0xD3D1 <= code && code <= 0xD3EB) || // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH (0xD3ED <= code && code <= 0xD407) || // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH (0xD409 <= code && code <= 0xD423) || // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH (0xD425 <= code && code <= 0xD43F) || // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH (0xD441 <= code && code <= 0xD45B) || // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH (0xD45D <= code && code <= 0xD477) || // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH (0xD479 <= code && code <= 0xD493) || // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH (0xD495 <= code && code <= 0xD4AF) || // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH (0xD4B1 <= code && code <= 0xD4CB) || // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH (0xD4CD <= code && code <= 0xD4E7) || // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH (0xD4E9 <= code && code <= 0xD503) || // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH (0xD505 <= code && code <= 0xD51F) || // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH (0xD521 <= code && code <= 0xD53B) || // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH (0xD53D <= code && code <= 0xD557) || // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH (0xD559 <= code && code <= 0xD573) || // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH (0xD575 <= code && code <= 0xD58F) || // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH (0xD591 <= code && code <= 0xD5AB) || // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH (0xD5AD <= code && code <= 0xD5C7) || // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH (0xD5C9 <= code && code <= 0xD5E3) || // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH (0xD5E5 <= code && code <= 0xD5FF) || // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH (0xD601 <= code && code <= 0xD61B) || // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH (0xD61D <= code && code <= 0xD637) || // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH (0xD639 <= code && code <= 0xD653) || // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH (0xD655 <= code && code <= 0xD66F) || // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH (0xD671 <= code && code <= 0xD68B) || // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH (0xD68D <= code && code <= 0xD6A7) || // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH (0xD6A9 <= code && code <= 0xD6C3) || // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH (0xD6C5 <= code && code <= 0xD6DF) || // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH (0xD6E1 <= code && code <= 0xD6FB) || // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH (0xD6FD <= code && code <= 0xD717) || // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH (0xD719 <= code && code <= 0xD733) || // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH (0xD735 <= code && code <= 0xD74F) || // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH (0xD751 <= code && code <= 0xD76B) || // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH (0xD76D <= code && code <= 0xD787) || // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH (0xD789 <= code && code <= 0xD7A3) // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH ){ return LVT; } if( 0x261D == code || // So WHITE UP POINTING INDEX 0x26F9 == code || // So PERSON WITH BALL (0x270A <= code && code <= 0x270D) || // So [4] RAISED FIST..WRITING HAND 0x1F385 == code || // So FATHER CHRISTMAS (0x1F3C2 <= code && code <= 0x1F3C4) || // So [3] SNOWBOARDER..SURFER 0x1F3C7 == code || // So HORSE RACING (0x1F3CA <= code && code <= 0x1F3CC) || // So [3] SWIMMER..GOLFER (0x1F442 <= code && code <= 0x1F443) || // So [2] EAR..NOSE (0x1F446 <= code && code <= 0x1F450) || // So [11] WHITE UP POINTING BACKHAND INDEX..OPEN HANDS SIGN 0x1F46E == code || // So POLICE OFFICER (0x1F470 <= code && code <= 0x1F478) || // So [9] BRIDE WITH VEIL..PRINCESS 0x1F47C == code || // So BABY ANGEL (0x1F481 <= code && code <= 0x1F483) || // So [3] INFORMATION DESK PERSON..DANCER (0x1F485 <= code && code <= 0x1F487) || // So [3] NAIL POLISH..HAIRCUT 0x1F4AA == code || // So FLEXED BICEPS (0x1F574 <= code && code <= 0x1F575) || // So [2] MAN IN BUSINESS SUIT LEVITATING..SLEUTH OR SPY 0x1F57A == code || // So MAN DANCING 0x1F590 == code || // So RAISED HAND WITH FINGERS SPLAYED (0x1F595 <= code && code <= 0x1F596) || // So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS (0x1F645 <= code && code <= 0x1F647) || // So [3] FACE WITH NO GOOD GESTURE..PERSON BOWING DEEPLY (0x1F64B <= code && code <= 0x1F64F) || // So [5] HAPPY PERSON RAISING ONE HAND..PERSON WITH FOLDED HANDS 0x1F6A3 == code || // So ROWBOAT (0x1F6B4 <= code && code <= 0x1F6B6) || // So [3] BICYCLIST..PEDESTRIAN 0x1F6C0 == code || // So BATH 0x1F6CC == code || // So SLEEPING ACCOMMODATION (0x1F918 <= code && code <= 0x1F91C) || // So [5] SIGN OF THE HORNS..RIGHT-FACING FIST (0x1F91E <= code && code <= 0x1F91F) || // So [2] HAND WITH INDEX AND MIDDLE FINGERS CROSSED..I LOVE YOU HAND SIGN 0x1F926 == code || // So FACE PALM (0x1F930 <= code && code <= 0x1F939) || // So [10] PREGNANT WOMAN..JUGGLING (0x1F93D <= code && code <= 0x1F93E) || // So [2] WATER POLO..HANDBALL (0x1F9D1 <= code && code <= 0x1F9DD) // So [13] ADULT..ELF ){ return E_Base; } if( (0x1F3FB <= code && code <= 0x1F3FF) // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 ){ return E_Modifier; } if( 0x200D == code // Cf ZERO WIDTH JOINER ){ return ZWJ; } if( 0x2640 == code || // So FEMALE SIGN 0x2642 == code || // So MALE SIGN (0x2695 <= code && code <= 0x2696) || // So [2] STAFF OF AESCULAPIUS..SCALES 0x2708 == code || // So AIRPLANE 0x2764 == code || // So HEAVY BLACK HEART 0x1F308 == code || // So RAINBOW 0x1F33E == code || // So EAR OF RICE 0x1F373 == code || // So COOKING 0x1F393 == code || // So GRADUATION CAP 0x1F3A4 == code || // So MICROPHONE 0x1F3A8 == code || // So ARTIST PALETTE 0x1F3EB == code || // So SCHOOL 0x1F3ED == code || // So FACTORY 0x1F48B == code || // So KISS MARK (0x1F4BB <= code && code <= 0x1F4BC) || // So [2] PERSONAL COMPUTER..BRIEFCASE 0x1F527 == code || // So WRENCH 0x1F52C == code || // So MICROSCOPE 0x1F5E8 == code || // So LEFT SPEECH BUBBLE 0x1F680 == code || // So ROCKET 0x1F692 == code // So FIRE ENGINE ){ return Glue_After_Zwj; } if( (0x1F466 <= code && code <= 0x1F469) // So [4] BOY..WOMAN ){ return E_Base_GAZ; } //all unlisted characters have a grapheme break property of "Other" return Other; } return this; } // A glyph represents a single character in a particular label. It may or may // not have a billboard, depending on whether the texture info has an index into // the the label collection's texture atlas. Invisible characters have no texture, and // no billboard. However, it always has a valid dimensions object. function Glyph() { this.textureInfo = undefined; this.dimensions = undefined; this.billboard = undefined; } // GlyphTextureInfo represents a single character, drawn in a particular style, // shared and reference counted across all labels. It may or may not have an // index into the label collection's texture atlas, depending on whether the character // has both width and height, but it always has a valid dimensions object. function GlyphTextureInfo(labelCollection, index, dimensions) { this.labelCollection = labelCollection; this.index = index; this.dimensions = dimensions; } // Traditionally, leading is %20 of the font size. var defaultLineSpacingPercent = 1.2; var whitePixelCanvasId = "ID_WHITE_PIXEL"; var whitePixelSize = new Cartesian2(4, 4); var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); function addWhitePixelCanvas(textureAtlas, labelCollection) { var canvas = document.createElement("canvas"); canvas.width = whitePixelSize.x; canvas.height = whitePixelSize.y; var context2D = canvas.getContext("2d"); context2D.fillStyle = "#fff"; context2D.fillRect(0, 0, canvas.width, canvas.height); textureAtlas.addImage(whitePixelCanvasId, canvas).then(function (index) { labelCollection._whitePixelIndex = index; }); } // reusable object for calling writeTextToCanvas var writeTextToCanvasParameters = {}; function createGlyphCanvas( character, font, fillColor, outlineColor, outlineWidth, style, verticalOrigin ) { writeTextToCanvasParameters.font = font; writeTextToCanvasParameters.fillColor = fillColor; writeTextToCanvasParameters.strokeColor = outlineColor; writeTextToCanvasParameters.strokeWidth = outlineWidth; // Setting the padding to something bigger is necessary to get enough space for the outlining. writeTextToCanvasParameters.padding = SDFSettings$1.PADDING; if (verticalOrigin === VerticalOrigin$1.CENTER) { writeTextToCanvasParameters.textBaseline = "middle"; } else if (verticalOrigin === VerticalOrigin$1.TOP) { writeTextToCanvasParameters.textBaseline = "top"; } else { // VerticalOrigin.BOTTOM and VerticalOrigin.BASELINE writeTextToCanvasParameters.textBaseline = "bottom"; } writeTextToCanvasParameters.fill = style === LabelStyle$1.FILL || style === LabelStyle$1.FILL_AND_OUTLINE; writeTextToCanvasParameters.stroke = style === LabelStyle$1.OUTLINE || style === LabelStyle$1.FILL_AND_OUTLINE; writeTextToCanvasParameters.backgroundColor = Color.BLACK; return writeTextToCanvas(character, writeTextToCanvasParameters); } function unbindGlyph(labelCollection, glyph) { glyph.textureInfo = undefined; glyph.dimensions = undefined; var billboard = glyph.billboard; if (defined(billboard)) { billboard.show = false; billboard.image = undefined; if (defined(billboard._removeCallbackFunc)) { billboard._removeCallbackFunc(); billboard._removeCallbackFunc = undefined; } labelCollection._spareBillboards.push(billboard); glyph.billboard = undefined; } } function addGlyphToTextureAtlas(textureAtlas, id, canvas, glyphTextureInfo) { textureAtlas.addImage(id, canvas).then(function (index) { glyphTextureInfo.index = index; }); } var splitter = new GraphemeSplitter(); function rebindAllGlyphs(labelCollection, label) { var text = label._renderedText; var graphemes = splitter.splitGraphemes(text); var textLength = graphemes.length; var glyphs = label._glyphs; var glyphsLength = glyphs.length; var glyph; var glyphIndex; var textIndex; // Compute a font size scale relative to the sdf font generated size. label._relativeSize = label._fontSize / SDFSettings$1.FONT_SIZE; // if we have more glyphs than needed, unbind the extras. if (textLength < glyphsLength) { for (glyphIndex = textLength; glyphIndex < glyphsLength; ++glyphIndex) { unbindGlyph(labelCollection, glyphs[glyphIndex]); } } // presize glyphs to match the new text length glyphs.length = textLength; var showBackground = label._showBackground && text.split("\n").join("").length > 0; var backgroundBillboard = label._backgroundBillboard; var backgroundBillboardCollection = labelCollection._backgroundBillboardCollection; if (!showBackground) { if (defined(backgroundBillboard)) { backgroundBillboardCollection.remove(backgroundBillboard); label._backgroundBillboard = backgroundBillboard = undefined; } } else { if (!defined(backgroundBillboard)) { backgroundBillboard = backgroundBillboardCollection.add({ collection: labelCollection, image: whitePixelCanvasId, imageSubRegion: whitePixelBoundingRegion, }); label._backgroundBillboard = backgroundBillboard; } backgroundBillboard.color = label._backgroundColor; backgroundBillboard.show = label._show; backgroundBillboard.position = label._position; backgroundBillboard.eyeOffset = label._eyeOffset; backgroundBillboard.pixelOffset = label._pixelOffset; backgroundBillboard.horizontalOrigin = HorizontalOrigin$1.LEFT; backgroundBillboard.verticalOrigin = label._verticalOrigin; backgroundBillboard.heightReference = label._heightReference; backgroundBillboard.scale = label.totalScale; backgroundBillboard.pickPrimitive = label; backgroundBillboard.id = label._id; backgroundBillboard.translucencyByDistance = label._translucencyByDistance; backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; backgroundBillboard.scaleByDistance = label._scaleByDistance; backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; backgroundBillboard.disableDepthTestDistance = label._disableDepthTestDistance; } var glyphTextureCache = labelCollection._glyphTextureCache; // walk the text looking for new characters (creating new glyphs for each) // or changed characters (rebinding existing glyphs) for (textIndex = 0; textIndex < textLength; ++textIndex) { var character = graphemes[textIndex]; var verticalOrigin = label._verticalOrigin; var id = JSON.stringify([ character, label._fontFamily, label._fontStyle, label._fontWeight, +verticalOrigin, ]); var glyphTextureInfo = glyphTextureCache[id]; if (!defined(glyphTextureInfo)) { var glyphFont = label._fontStyle + " " + label._fontWeight + " " + SDFSettings$1.FONT_SIZE + "px " + label._fontFamily; var canvas = createGlyphCanvas( character, glyphFont, Color.WHITE, Color.WHITE, 0.0, LabelStyle$1.FILL, verticalOrigin ); glyphTextureInfo = new GlyphTextureInfo( labelCollection, -1, canvas.dimensions ); glyphTextureCache[id] = glyphTextureInfo; if (canvas.width > 0 && canvas.height > 0) { var sdfValues = calcSDF(canvas, { cutoff: SDFSettings$1.CUTOFF, radius: SDFSettings$1.RADIUS, }); var ctx = canvas.getContext("2d"); var canvasWidth = canvas.width; var canvasHeight = canvas.height; var imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); for (var i = 0; i < canvasWidth; i++) { for (var j = 0; j < canvasHeight; j++) { var baseIndex = j * canvasWidth + i; var alpha = sdfValues[baseIndex] * 255; var imageIndex = baseIndex * 4; imgData.data[imageIndex + 0] = alpha; imgData.data[imageIndex + 1] = alpha; imgData.data[imageIndex + 2] = alpha; imgData.data[imageIndex + 3] = alpha; } } ctx.putImageData(imgData, 0, 0); if (character !== " ") { addGlyphToTextureAtlas( labelCollection._textureAtlas, id, canvas, glyphTextureInfo ); } } } glyph = glyphs[textIndex]; if (defined(glyph)) { // clean up leftover information from the previous glyph if (glyphTextureInfo.index === -1) { // no texture, and therefore no billboard, for this glyph. // so, completely unbind glyph. unbindGlyph(labelCollection, glyph); } else if (defined(glyph.textureInfo)) { // we have a texture and billboard. If we had one before, release // our reference to that texture info, but reuse the billboard. glyph.textureInfo = undefined; } } else { // create a glyph object glyph = new Glyph(); glyphs[textIndex] = glyph; } glyph.textureInfo = glyphTextureInfo; glyph.dimensions = glyphTextureInfo.dimensions; // if we have a texture, configure the existing billboard, or obtain one if (glyphTextureInfo.index !== -1) { var billboard = glyph.billboard; var spareBillboards = labelCollection._spareBillboards; if (!defined(billboard)) { if (spareBillboards.length > 0) { billboard = spareBillboards.pop(); } else { billboard = labelCollection._billboardCollection.add({ collection: labelCollection, }); billboard._labelDimensions = new Cartesian2(); billboard._labelTranslate = new Cartesian2(); } glyph.billboard = billboard; } billboard.show = label._show; billboard.position = label._position; billboard.eyeOffset = label._eyeOffset; billboard.pixelOffset = label._pixelOffset; billboard.horizontalOrigin = HorizontalOrigin$1.LEFT; billboard.verticalOrigin = label._verticalOrigin; billboard.heightReference = label._heightReference; billboard.scale = label.totalScale; billboard.pickPrimitive = label; billboard.id = label._id; billboard.image = id; billboard.translucencyByDistance = label._translucencyByDistance; billboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; billboard.scaleByDistance = label._scaleByDistance; billboard.distanceDisplayCondition = label._distanceDisplayCondition; billboard.disableDepthTestDistance = label._disableDepthTestDistance; billboard._batchIndex = label._batchIndex; billboard.outlineColor = label.outlineColor; if (label.style === LabelStyle$1.FILL_AND_OUTLINE) { billboard.color = label._fillColor; billboard.outlineWidth = label.outlineWidth; } else if (label.style === LabelStyle$1.FILL) { billboard.color = label._fillColor; billboard.outlineWidth = 0.0; } else if (label.style === LabelStyle$1.OUTLINE) { billboard.color = Color.TRANSPARENT; billboard.outlineWidth = label.outlineWidth; } } } // changing glyphs will cause the position of the // glyphs to change, since different characters have different widths label._repositionAllGlyphs = true; } function calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding) { if (horizontalOrigin === HorizontalOrigin$1.CENTER) { return -lineWidth / 2; } else if (horizontalOrigin === HorizontalOrigin$1.RIGHT) { return -(lineWidth + backgroundPadding.x); } return backgroundPadding.x; } // reusable Cartesian2 instances var glyphPixelOffset = new Cartesian2(); var scratchBackgroundPadding = new Cartesian2(); function repositionAllGlyphs(label) { var glyphs = label._glyphs; var text = label._renderedText; var glyph; var dimensions; var lastLineWidth = 0; var maxLineWidth = 0; var lineWidths = []; var maxGlyphDescent = Number.NEGATIVE_INFINITY; var maxGlyphY = 0; var numberOfLines = 1; var glyphIndex; var glyphLength = glyphs.length; var backgroundBillboard = label._backgroundBillboard; var backgroundPadding = Cartesian2.clone( defined(backgroundBillboard) ? label._backgroundPadding : Cartesian2.ZERO, scratchBackgroundPadding ); // We need to scale the background padding, which is specified in pixels by the inverse of the relative size so it is scaled properly. backgroundPadding.x /= label._relativeSize; backgroundPadding.y /= label._relativeSize; for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { if (text.charAt(glyphIndex) === "\n") { lineWidths.push(lastLineWidth); ++numberOfLines; lastLineWidth = 0; } else { glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; maxGlyphY = Math.max(maxGlyphY, dimensions.height - dimensions.descent); maxGlyphDescent = Math.max(maxGlyphDescent, dimensions.descent); //Computing the line width must also account for the kerning that occurs between letters. lastLineWidth += dimensions.width - dimensions.bounds.minx; if (glyphIndex < glyphLength - 1) { lastLineWidth += glyphs[glyphIndex + 1].dimensions.bounds.minx; } maxLineWidth = Math.max(maxLineWidth, lastLineWidth); } } lineWidths.push(lastLineWidth); var maxLineHeight = maxGlyphY + maxGlyphDescent; var scale = label.totalScale; var horizontalOrigin = label._horizontalOrigin; var verticalOrigin = label._verticalOrigin; var lineIndex = 0; var lineWidth = lineWidths[lineIndex]; var widthOffset = calculateWidthOffset( lineWidth, horizontalOrigin, backgroundPadding ); var lineSpacing = (defined(label._lineHeight) ? label._lineHeight : defaultLineSpacingPercent * label._fontSize) / label._relativeSize; var otherLinesHeight = lineSpacing * (numberOfLines - 1); var totalLineWidth = maxLineWidth; var totalLineHeight = maxLineHeight + otherLinesHeight; if (defined(backgroundBillboard)) { totalLineWidth += backgroundPadding.x * 2; totalLineHeight += backgroundPadding.y * 2; backgroundBillboard._labelHorizontalOrigin = horizontalOrigin; } glyphPixelOffset.x = widthOffset * scale; glyphPixelOffset.y = 0; var firstCharOfLine = true; var lineOffsetY = 0; for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { if (text.charAt(glyphIndex) === "\n") { ++lineIndex; lineOffsetY += lineSpacing; lineWidth = lineWidths[lineIndex]; widthOffset = calculateWidthOffset( lineWidth, horizontalOrigin, backgroundPadding ); glyphPixelOffset.x = widthOffset * scale; firstCharOfLine = true; } else { glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; if (verticalOrigin === VerticalOrigin$1.TOP) { glyphPixelOffset.y = dimensions.height - maxGlyphY - backgroundPadding.y; glyphPixelOffset.y += SDFSettings$1.PADDING; } else if (verticalOrigin === VerticalOrigin$1.CENTER) { glyphPixelOffset.y = (otherLinesHeight + dimensions.height - maxGlyphY) / 2; } else if (verticalOrigin === VerticalOrigin$1.BASELINE) { glyphPixelOffset.y = otherLinesHeight; glyphPixelOffset.y -= SDFSettings$1.PADDING; } else { // VerticalOrigin.BOTTOM glyphPixelOffset.y = otherLinesHeight + maxGlyphDescent + backgroundPadding.y; glyphPixelOffset.y -= SDFSettings$1.PADDING; } glyphPixelOffset.y = (glyphPixelOffset.y - dimensions.descent - lineOffsetY) * scale; // Handle any offsets for the first character of the line since the bounds might not be right on the bottom left pixel. if (firstCharOfLine) { glyphPixelOffset.x -= SDFSettings$1.PADDING * scale; firstCharOfLine = false; } if (defined(glyph.billboard)) { glyph.billboard._setTranslate(glyphPixelOffset); glyph.billboard._labelDimensions.x = totalLineWidth; glyph.billboard._labelDimensions.y = totalLineHeight; glyph.billboard._labelHorizontalOrigin = horizontalOrigin; } //Compute the next x offset taking into account the kerning performed //on both the current letter as well as the next letter to be drawn //as well as any applied scale. if (glyphIndex < glyphLength - 1) { var nextGlyph = glyphs[glyphIndex + 1]; glyphPixelOffset.x += (dimensions.width - dimensions.bounds.minx + nextGlyph.dimensions.bounds.minx) * scale; } } } if (defined(backgroundBillboard) && text.split("\n").join("").length > 0) { if (horizontalOrigin === HorizontalOrigin$1.CENTER) { widthOffset = -maxLineWidth / 2 - backgroundPadding.x; } else if (horizontalOrigin === HorizontalOrigin$1.RIGHT) { widthOffset = -(maxLineWidth + backgroundPadding.x * 2); } else { widthOffset = 0; } glyphPixelOffset.x = widthOffset * scale; if (verticalOrigin === VerticalOrigin$1.TOP) { glyphPixelOffset.y = maxLineHeight - maxGlyphY - maxGlyphDescent; } else if (verticalOrigin === VerticalOrigin$1.CENTER) { glyphPixelOffset.y = (maxLineHeight - maxGlyphY) / 2 - maxGlyphDescent; } else if (verticalOrigin === VerticalOrigin$1.BASELINE) { glyphPixelOffset.y = -backgroundPadding.y - maxGlyphDescent; } else { // VerticalOrigin.BOTTOM glyphPixelOffset.y = 0; } glyphPixelOffset.y = glyphPixelOffset.y * scale; backgroundBillboard.width = totalLineWidth; backgroundBillboard.height = totalLineHeight; backgroundBillboard._setTranslate(glyphPixelOffset); backgroundBillboard._labelTranslate = Cartesian2.clone( glyphPixelOffset, backgroundBillboard._labelTranslate ); } if (label.heightReference === HeightReference$1.CLAMP_TO_GROUND) { for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { glyph = glyphs[glyphIndex]; var billboard = glyph.billboard; if (defined(billboard)) { billboard._labelTranslate = Cartesian2.clone( glyphPixelOffset, billboard._labelTranslate ); } } } } function destroyLabel(labelCollection, label) { var glyphs = label._glyphs; for (var i = 0, len = glyphs.length; i < len; ++i) { unbindGlyph(labelCollection, glyphs[i]); } if (defined(label._backgroundBillboard)) { labelCollection._backgroundBillboardCollection.remove( label._backgroundBillboard ); label._backgroundBillboard = undefined; } label._labelCollection = undefined; if (defined(label._removeCallbackFunc)) { label._removeCallbackFunc(); } destroyObject(label); } /** * A renderable collection of labels. Labels are viewport-aligned text positioned in the 3D scene. * Each label can have a different font, color, scale, etc. *

*
*
* Example labels *
*

* Labels are added and removed from the collection using {@link LabelCollection#add} * and {@link LabelCollection#remove}. * * @alias LabelCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each label from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Scene} [options.scene] Must be passed in for labels that use the height reference property or will be depth tested against the globe. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The label blending option. The default * is used for rendering both opaque and translucent labels. However, if either all of the labels are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x. * @param {Boolean} [options.show=true] Determines if the labels in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many labels, to * many collections with only a few labels each. Avoid having collections where some * labels change every frame and others do not; instead, create one or more collections * for static labels, and one or more collections for dynamic labels. * * @see LabelCollection#add * @see LabelCollection#remove * @see Label * @see BillboardCollection * * @demo {@link https://sandcastle.cesium.com/index.html?src=Labels.html|Cesium Sandcastle Labels Demo} * * @example * // Create a label collection with two labels * var labels = scene.primitives.add(new Cesium.LabelCollection()); * labels.add({ * position : new Cesium.Cartesian3(1.0, 2.0, 3.0), * text : 'A label' * }); * labels.add({ * position : new Cesium.Cartesian3(4.0, 5.0, 6.0), * text : 'Another label' * }); */ function LabelCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._scene = options.scene; this._batchTable = options.batchTable; this._textureAtlas = undefined; this._backgroundTextureAtlas = undefined; this._whitePixelIndex = undefined; this._backgroundBillboardCollection = new BillboardCollection({ scene: this._scene, }); this._backgroundBillboardCollection.destroyTextureAtlas = false; this._billboardCollection = new BillboardCollection({ scene: this._scene, batchTable: this._batchTable, }); this._billboardCollection.destroyTextureAtlas = false; this._billboardCollection._sdf = true; this._spareBillboards = []; this._glyphTextureCache = {}; this._labels = []; this._labelsToUpdate = []; this._totalGlyphCount = 0; this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints /** * Determines if labels in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * The 4x4 transformation matrix that transforms each label in this collection from model to world coordinates. * When this is the identity matrix, the labels are drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type Matrix4 * @default {@link Matrix4.IDENTITY} * * @example * var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); * labels.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); * labels.add({ * position : new Cesium.Cartesian3(0.0, 0.0, 0.0), * text : 'Center' * }); * labels.add({ * position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0), * text : 'East' * }); * labels.add({ * position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0), * text : 'North' * }); * labels.add({ * position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0), * text : 'Up' * }); */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * The label blending option. The default is used for rendering both opaque and translucent labels. * However, if either all of the labels are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve * performance by up to 2x. * @type {BlendOption} * @default BlendOption.OPAQUE_AND_TRANSLUCENT */ this.blendOption = defaultValue( options.blendOption, BlendOption$1.OPAQUE_AND_TRANSLUCENT ); } Object.defineProperties(LabelCollection.prototype, { /** * Returns the number of labels in this collection. This is commonly used with * {@link LabelCollection#get} to iterate over all the labels * in the collection. * @memberof LabelCollection.prototype * @type {Number} */ length: { get: function () { return this._labels.length; }, }, }); /** * Creates and adds a label with the specified initial properties to the collection. * The added label is returned so it can be modified or removed from the collection later. * * @param {Object} [options] A template describing the label's properties as shown in Example 1. * @returns {Label} The label that was added to the collection. * * @performance Calling add is expected constant time. However, the collection's vertex buffer * is rewritten; this operations is O(n) and also incurs * CPU to GPU overhead. For best performance, add as many billboards as possible before * calling update. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Example 1: Add a label, specifying all the default values. * var l = labels.add({ * show : true, * position : Cesium.Cartesian3.ZERO, * text : '', * font : '30px sans-serif', * fillColor : Cesium.Color.WHITE, * outlineColor : Cesium.Color.BLACK, * outlineWidth : 1.0, * showBackground : false, * backgroundColor : new Cesium.Color(0.165, 0.165, 0.165, 0.8), * backgroundPadding : new Cesium.Cartesian2(7, 5), * style : Cesium.LabelStyle.FILL, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, * horizontalOrigin : Cesium.HorizontalOrigin.LEFT, * verticalOrigin : Cesium.VerticalOrigin.BASELINE, * scale : 1.0, * translucencyByDistance : undefined, * pixelOffsetScaleByDistance : undefined, * heightReference : HeightReference.NONE, * distanceDisplayCondition : undefined * }); * * @example * // Example 2: Specify only the label's cartographic position, * // text, and font. * var l = labels.add({ * position : Cesium.Cartesian3.fromRadians(longitude, latitude, height), * text : 'Hello World', * font : '24px Helvetica', * }); * * @see LabelCollection#remove * @see LabelCollection#removeAll */ LabelCollection.prototype.add = function (options) { var label = new Label(options, this); this._labels.push(label); this._labelsToUpdate.push(label); return label; }; /** * Removes a label from the collection. Once removed, a label is no longer usable. * * @param {Label} label The label to remove. * @returns {Boolean} true if the label was removed; false if the label was not found in the collection. * * @performance Calling remove is expected constant time. However, the collection's vertex buffer * is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For * best performance, remove as many labels as possible before calling update. * If you intend to temporarily hide a label, it is usually more efficient to call * {@link Label#show} instead of removing and re-adding the label. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * var l = labels.add(...); * labels.remove(l); // Returns true * * @see LabelCollection#add * @see LabelCollection#removeAll * @see Label#show */ LabelCollection.prototype.remove = function (label) { if (defined(label) && label._labelCollection === this) { var index = this._labels.indexOf(label); if (index !== -1) { this._labels.splice(index, 1); destroyLabel(this, label); return true; } } return false; }; /** * Removes all labels from the collection. * * @performance O(n). It is more efficient to remove all the labels * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * labels.add(...); * labels.add(...); * labels.removeAll(); * * @see LabelCollection#add * @see LabelCollection#remove */ LabelCollection.prototype.removeAll = function () { var labels = this._labels; for (var i = 0, len = labels.length; i < len; ++i) { destroyLabel(this, labels[i]); } labels.length = 0; }; /** * Check whether this collection contains a given label. * * @param {Label} label The label to check for. * @returns {Boolean} true if this collection contains the label, false otherwise. * * @see LabelCollection#get * */ LabelCollection.prototype.contains = function (label) { return defined(label) && label._labelCollection === this; }; /** * Returns the label in the collection at the specified index. Indices are zero-based * and increase as labels are added. Removing a label shifts all labels after * it to the left, changing their indices. This function is commonly used with * {@link LabelCollection#length} to iterate over all the labels * in the collection. * * @param {Number} index The zero-based index of the billboard. * * @returns {Label} The label at the specified index. * * @performance Expected constant time. If labels were removed from the collection and * {@link Scene#render} was not called, an implicit O(n) * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Toggle the show property of every label in the collection * var len = labels.length; * for (var i = 0; i < len; ++i) { * var l = billboards.get(i); * l.show = !l.show; * } * * @see LabelCollection#length */ LabelCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); return this._labels[index]; }; /** * @private * */ LabelCollection.prototype.update = function (frameState) { if (!this.show) { return; } var billboardCollection = this._billboardCollection; var backgroundBillboardCollection = this._backgroundBillboardCollection; billboardCollection.modelMatrix = this.modelMatrix; billboardCollection.debugShowBoundingVolume = this.debugShowBoundingVolume; backgroundBillboardCollection.modelMatrix = this.modelMatrix; backgroundBillboardCollection.debugShowBoundingVolume = this.debugShowBoundingVolume; var context = frameState.context; if (!defined(this._textureAtlas)) { this._textureAtlas = new TextureAtlas({ context: context, }); billboardCollection.textureAtlas = this._textureAtlas; } if (!defined(this._backgroundTextureAtlas)) { this._backgroundTextureAtlas = new TextureAtlas({ context: context, initialSize: whitePixelSize, }); backgroundBillboardCollection.textureAtlas = this._backgroundTextureAtlas; addWhitePixelCanvas(this._backgroundTextureAtlas, this); } var len = this._labelsToUpdate.length; for (var i = 0; i < len; ++i) { var label = this._labelsToUpdate[i]; if (label.isDestroyed()) { continue; } var preUpdateGlyphCount = label._glyphs.length; if (label._rebindAllGlyphs) { rebindAllGlyphs(this, label); label._rebindAllGlyphs = false; } if (label._repositionAllGlyphs) { repositionAllGlyphs(label); label._repositionAllGlyphs = false; } var glyphCountDifference = label._glyphs.length - preUpdateGlyphCount; this._totalGlyphCount += glyphCountDifference; } var blendOption = backgroundBillboardCollection.length > 0 ? BlendOption$1.TRANSLUCENT : this.blendOption; billboardCollection.blendOption = blendOption; backgroundBillboardCollection.blendOption = blendOption; billboardCollection._highlightColor = this._highlightColor; backgroundBillboardCollection._highlightColor = this._highlightColor; this._labelsToUpdate.length = 0; backgroundBillboardCollection.update(frameState); billboardCollection.update(frameState); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see LabelCollection#destroy */ LabelCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * labels = labels && labels.destroy(); * * @see LabelCollection#isDestroyed */ LabelCollection.prototype.destroy = function () { this.removeAll(); this._billboardCollection = this._billboardCollection.destroy(); this._textureAtlas = this._textureAtlas && this._textureAtlas.destroy(); this._backgroundBillboardCollection = this._backgroundBillboardCollection.destroy(); this._backgroundTextureAtlas = this._backgroundTextureAtlas && this._backgroundTextureAtlas.destroy(); return destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var PolylineVS = "attribute vec3 position3DHigh;\n\ attribute vec3 position3DLow;\n\ attribute vec3 position2DHigh;\n\ attribute vec3 position2DLow;\n\ attribute vec3 prevPosition3DHigh;\n\ attribute vec3 prevPosition3DLow;\n\ attribute vec3 prevPosition2DHigh;\n\ attribute vec3 prevPosition2DLow;\n\ attribute vec3 nextPosition3DHigh;\n\ attribute vec3 nextPosition3DLow;\n\ attribute vec3 nextPosition2DHigh;\n\ attribute vec3 nextPosition2DLow;\n\ attribute vec4 texCoordExpandAndBatchIndex;\n\ \n\ varying vec2 v_st;\n\ varying float v_width;\n\ varying vec4 v_pickColor;\n\ varying float v_polylineAngle;\n\ \n\ void main()\n\ {\n\ float texCoord = texCoordExpandAndBatchIndex.x;\n\ float expandDir = texCoordExpandAndBatchIndex.y;\n\ bool usePrev = texCoordExpandAndBatchIndex.z < 0.0;\n\ float batchTableIndex = texCoordExpandAndBatchIndex.w;\n\ \n\ vec2 widthAndShow = batchTable_getWidthAndShow(batchTableIndex);\n\ float width = widthAndShow.x + 0.5;\n\ float show = widthAndShow.y;\n\ \n\ if (width < 1.0)\n\ {\n\ show = 0.0;\n\ }\n\ \n\ vec4 pickColor = batchTable_getPickColor(batchTableIndex);\n\ \n\ vec4 p, prev, next;\n\ if (czm_morphTime == 1.0)\n\ {\n\ p = czm_translateRelativeToEye(position3DHigh.xyz, position3DLow.xyz);\n\ prev = czm_translateRelativeToEye(prevPosition3DHigh.xyz, prevPosition3DLow.xyz);\n\ next = czm_translateRelativeToEye(nextPosition3DHigh.xyz, nextPosition3DLow.xyz);\n\ }\n\ else if (czm_morphTime == 0.0)\n\ {\n\ p = czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy);\n\ prev = czm_translateRelativeToEye(prevPosition2DHigh.zxy, prevPosition2DLow.zxy);\n\ next = czm_translateRelativeToEye(nextPosition2DHigh.zxy, nextPosition2DLow.zxy);\n\ }\n\ else\n\ {\n\ p = czm_columbusViewMorph(\n\ czm_translateRelativeToEye(position2DHigh.zxy, position2DLow.zxy),\n\ czm_translateRelativeToEye(position3DHigh.xyz, position3DLow.xyz),\n\ czm_morphTime);\n\ prev = czm_columbusViewMorph(\n\ czm_translateRelativeToEye(prevPosition2DHigh.zxy, prevPosition2DLow.zxy),\n\ czm_translateRelativeToEye(prevPosition3DHigh.xyz, prevPosition3DLow.xyz),\n\ czm_morphTime);\n\ next = czm_columbusViewMorph(\n\ czm_translateRelativeToEye(nextPosition2DHigh.zxy, nextPosition2DLow.zxy),\n\ czm_translateRelativeToEye(nextPosition3DHigh.xyz, nextPosition3DLow.xyz),\n\ czm_morphTime);\n\ }\n\ \n\ #ifdef DISTANCE_DISPLAY_CONDITION\n\ vec3 centerHigh = batchTable_getCenterHigh(batchTableIndex);\n\ vec4 centerLowAndRadius = batchTable_getCenterLowAndRadius(batchTableIndex);\n\ vec3 centerLow = centerLowAndRadius.xyz;\n\ float radius = centerLowAndRadius.w;\n\ vec2 distanceDisplayCondition = batchTable_getDistanceDisplayCondition(batchTableIndex);\n\ \n\ float lengthSq;\n\ if (czm_sceneMode == czm_sceneMode2D)\n\ {\n\ lengthSq = czm_eyeHeight2D.y;\n\ }\n\ else\n\ {\n\ vec4 center = czm_translateRelativeToEye(centerHigh.xyz, centerLow.xyz);\n\ lengthSq = max(0.0, dot(center.xyz, center.xyz) - radius * radius);\n\ }\n\ \n\ float nearSq = distanceDisplayCondition.x * distanceDisplayCondition.x;\n\ float farSq = distanceDisplayCondition.y * distanceDisplayCondition.y;\n\ if (lengthSq < nearSq || lengthSq > farSq)\n\ {\n\ show = 0.0;\n\ }\n\ #endif\n\ \n\ float polylineAngle;\n\ vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, polylineAngle);\n\ gl_Position = czm_viewportOrthographic * positionWC * show;\n\ \n\ v_st.s = texCoord;\n\ v_st.t = czm_writeNonPerspective(clamp(expandDir, 0.0, 1.0), gl_Position.w);\n\ \n\ v_width = width;\n\ v_pickColor = pickColor;\n\ v_polylineAngle = polylineAngle;\n\ }\n\ "; /** * A renderable polyline. Create this by calling {@link PolylineCollection#add} * * @alias Polyline * @internalConstructor * @class * * @param {Object} options Object with the following properties: * @param {Boolean} [options.show=true] true if this polyline will be shown; otherwise, false. * @param {Number} [options.width=1.0] The width of the polyline in pixels. * @param {Boolean} [options.loop=false] Whether a line segment will be added between the last and first line positions to make this line a loop. * @param {Material} [options.material=Material.ColorType] The material. * @param {Cartesian3[]} [options.positions] The positions. * @param {Object} [options.id] The user-defined object to be returned when this polyline is picked. * @param {DistanceDisplayCondition} [options.distanceDisplayCondition] The condition specifying at what distance from the camera that this polyline will be displayed. * @param {PolylineCollection} polylineCollection The renderable polyline collection. * * @see PolylineCollection * */ function Polyline(options, polylineCollection) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._show = defaultValue(options.show, true); this._width = defaultValue(options.width, 1.0); this._loop = defaultValue(options.loop, false); this._distanceDisplayCondition = options.distanceDisplayCondition; this._material = options.material; if (!defined(this._material)) { this._material = Material.fromType(Material.ColorType, { color: new Color(1.0, 1.0, 1.0, 1.0), }); } var positions = options.positions; if (!defined(positions)) { positions = []; } this._positions = positions; this._actualPositions = arrayRemoveDuplicates( positions, Cartesian3.equalsEpsilon ); if (this._loop && this._actualPositions.length > 2) { if (this._actualPositions === this._positions) { this._actualPositions = positions.slice(); } this._actualPositions.push(Cartesian3.clone(this._actualPositions[0])); } this._length = this._actualPositions.length; this._id = options.id; var modelMatrix; if (defined(polylineCollection)) { modelMatrix = Matrix4.clone(polylineCollection.modelMatrix); } this._modelMatrix = modelMatrix; this._segments = PolylinePipeline.wrapLongitude( this._actualPositions, modelMatrix ); this._actualLength = undefined; // eslint-disable-next-line no-use-before-define this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES$2); this._polylineCollection = polylineCollection; this._dirty = false; this._pickId = undefined; this._boundingVolume = BoundingSphere.fromPoints(this._actualPositions); this._boundingVolumeWC = BoundingSphere.transform( this._boundingVolume, this._modelMatrix ); this._boundingVolume2D = new BoundingSphere(); // modified in PolylineCollection } var POSITION_INDEX$3 = (Polyline.POSITION_INDEX = 0); var SHOW_INDEX$3 = (Polyline.SHOW_INDEX = 1); var WIDTH_INDEX$1 = (Polyline.WIDTH_INDEX = 2); var MATERIAL_INDEX$1 = (Polyline.MATERIAL_INDEX = 3); var POSITION_SIZE_INDEX$1 = (Polyline.POSITION_SIZE_INDEX = 4); var DISTANCE_DISPLAY_CONDITION$1 = (Polyline.DISTANCE_DISPLAY_CONDITION = 5); var NUMBER_OF_PROPERTIES$2 = (Polyline.NUMBER_OF_PROPERTIES = 6); function makeDirty$1(polyline, propertyChanged) { ++polyline._propertiesChanged[propertyChanged]; var polylineCollection = polyline._polylineCollection; if (defined(polylineCollection)) { polylineCollection._updatePolyline(polyline, propertyChanged); polyline._dirty = true; } } Object.defineProperties(Polyline.prototype, { /** * Determines if this polyline will be shown. Use this to hide or show a polyline, instead * of removing it and re-adding it to the collection. * @memberof Polyline.prototype * @type {Boolean} */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (value !== this._show) { this._show = value; makeDirty$1(this, SHOW_INDEX$3); } }, }, /** * Gets or sets the positions of the polyline. * @memberof Polyline.prototype * @type {Cartesian3[]} * @example * polyline.positions = Cesium.Cartesian3.fromDegreesArray([ * 0.0, 0.0, * 10.0, 0.0, * 0.0, 20.0 * ]); */ positions: { get: function () { return this._positions; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var positions = arrayRemoveDuplicates(value, Cartesian3.equalsEpsilon); if (this._loop && positions.length > 2) { if (positions === value) { positions = value.slice(); } positions.push(Cartesian3.clone(positions[0])); } if ( this._actualPositions.length !== positions.length || this._actualPositions.length !== this._length ) { makeDirty$1(this, POSITION_SIZE_INDEX$1); } this._positions = value; this._actualPositions = positions; this._length = positions.length; this._boundingVolume = BoundingSphere.fromPoints( this._actualPositions, this._boundingVolume ); this._boundingVolumeWC = BoundingSphere.transform( this._boundingVolume, this._modelMatrix, this._boundingVolumeWC ); makeDirty$1(this, POSITION_INDEX$3); this.update(); }, }, /** * Gets or sets the surface appearance of the polyline. This can be one of several built-in {@link Material} objects or a custom material, scripted with * {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric}. * @memberof Polyline.prototype * @type {Material} */ material: { get: function () { return this._material; }, set: function (material) { //>>includeStart('debug', pragmas.debug); if (!defined(material)) { throw new DeveloperError("material is required."); } //>>includeEnd('debug'); if (this._material !== material) { this._material = material; makeDirty$1(this, MATERIAL_INDEX$1); } }, }, /** * Gets or sets the width of the polyline. * @memberof Polyline.prototype * @type {Number} */ width: { get: function () { return this._width; }, set: function (value) { //>>includeStart('debug', pragmas.debug) if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var width = this._width; if (value !== width) { this._width = value; makeDirty$1(this, WIDTH_INDEX$1); } }, }, /** * Gets or sets whether a line segment will be added between the first and last polyline positions. * @memberof Polyline.prototype * @type {Boolean} */ loop: { get: function () { return this._loop; }, set: function (value) { //>>includeStart('debug', pragmas.debug) if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (value !== this._loop) { var positions = this._actualPositions; if (value) { if ( positions.length > 2 && !Cartesian3.equals(positions[0], positions[positions.length - 1]) ) { if (positions.length === this._positions.length) { this._actualPositions = positions = this._positions.slice(); } positions.push(Cartesian3.clone(positions[0])); } } else if ( positions.length > 2 && Cartesian3.equals(positions[0], positions[positions.length - 1]) ) { if (positions.length - 1 === this._positions.length) { this._actualPositions = this._positions; } else { positions.pop(); } } this._loop = value; makeDirty$1(this, POSITION_SIZE_INDEX$1); } }, }, /** * Gets or sets the user-defined value returned when the polyline is picked. * @memberof Polyline.prototype * @type {*} */ id: { get: function () { return this._id; }, set: function (value) { this._id = value; if (defined(this._pickId)) { this._pickId.object.id = value; } }, }, /** * @private */ pickId: { get: function () { return this._pickId; }, }, /** * Gets the destruction status of this polyline * @memberof Polyline.prototype * @type {Boolean} * @default false * @private */ isDestroyed: { get: function () { return !defined(this._polylineCollection); }, }, /** * Gets or sets the condition specifying at what distance from the camera that this polyline will be displayed. * @memberof Polyline.prototype * @type {DistanceDisplayCondition} * @default undefined */ distanceDisplayCondition: { get: function () { return this._distanceDisplayCondition; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); if ( !DistanceDisplayCondition.equals(value, this._distanceDisplayCondition) ) { this._distanceDisplayCondition = DistanceDisplayCondition.clone( value, this._distanceDisplayCondition ); makeDirty$1(this, DISTANCE_DISPLAY_CONDITION$1); } }, }, }); /** * @private */ Polyline.prototype.update = function () { var modelMatrix = Matrix4.IDENTITY; if (defined(this._polylineCollection)) { modelMatrix = this._polylineCollection.modelMatrix; } var segmentPositionsLength = this._segments.positions.length; var segmentLengths = this._segments.lengths; var positionsChanged = this._propertiesChanged[POSITION_INDEX$3] > 0 || this._propertiesChanged[POSITION_SIZE_INDEX$1] > 0; if (!Matrix4.equals(modelMatrix, this._modelMatrix) || positionsChanged) { this._segments = PolylinePipeline.wrapLongitude( this._actualPositions, modelMatrix ); this._boundingVolumeWC = BoundingSphere.transform( this._boundingVolume, modelMatrix, this._boundingVolumeWC ); } this._modelMatrix = Matrix4.clone(modelMatrix, this._modelMatrix); if (this._segments.positions.length !== segmentPositionsLength) { // number of positions changed makeDirty$1(this, POSITION_SIZE_INDEX$1); } else { var length = segmentLengths.length; for (var i = 0; i < length; ++i) { if (segmentLengths[i] !== this._segments.lengths[i]) { // indices changed makeDirty$1(this, POSITION_SIZE_INDEX$1); break; } } } }; /** * @private */ Polyline.prototype.getPickId = function (context) { if (!defined(this._pickId)) { this._pickId = context.createPickId({ primitive: this, collection: this._polylineCollection, id: this._id, }); } return this._pickId; }; Polyline.prototype._clean = function () { this._dirty = false; var properties = this._propertiesChanged; for (var k = 0; k < NUMBER_OF_PROPERTIES$2 - 1; ++k) { properties[k] = 0; } }; Polyline.prototype._destroy = function () { this._pickId = this._pickId && this._pickId.destroy(); this._material = this._material && this._material.destroy(); this._polylineCollection = undefined; }; var SHOW_INDEX$2 = Polyline.SHOW_INDEX; var WIDTH_INDEX = Polyline.WIDTH_INDEX; var POSITION_INDEX$2 = Polyline.POSITION_INDEX; var MATERIAL_INDEX = Polyline.MATERIAL_INDEX; //POSITION_SIZE_INDEX is needed for when the polyline's position array changes size. //When it does, we need to recreate the indicesBuffer. var POSITION_SIZE_INDEX = Polyline.POSITION_SIZE_INDEX; var DISTANCE_DISPLAY_CONDITION = Polyline.DISTANCE_DISPLAY_CONDITION; var NUMBER_OF_PROPERTIES$1 = Polyline.NUMBER_OF_PROPERTIES; var attributeLocations$3 = { texCoordExpandAndBatchIndex: 0, position3DHigh: 1, position3DLow: 2, position2DHigh: 3, position2DLow: 4, prevPosition3DHigh: 5, prevPosition3DLow: 6, prevPosition2DHigh: 7, prevPosition2DLow: 8, nextPosition3DHigh: 9, nextPosition3DLow: 10, nextPosition2DHigh: 11, nextPosition2DLow: 12, }; /** * A renderable collection of polylines. *

*
*
* Example polylines *
*

* Polylines are added and removed from the collection using {@link PolylineCollection#add} * and {@link PolylineCollection#remove}. * * @alias PolylineCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each polyline from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {Boolean} [options.show=true] Determines if the polylines in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many polylines, to * many collections with only a few polylines each. Organize collections so that polylines * with the same update frequency are in the same collection, i.e., polylines that do not * change should be in one collection; polylines that change every frame should be in another * collection; and so on. * * @see PolylineCollection#add * @see PolylineCollection#remove * @see Polyline * @see LabelCollection * * @example * // Create a polyline collection with two polylines * var polylines = new Cesium.PolylineCollection(); * polylines.add({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -75.10, 39.57, * -77.02, 38.53, * -80.50, 35.14, * -80.12, 25.46]), * width : 2 * }); * * polylines.add({ * positions : Cesium.Cartesian3.fromDegreesArray([ * -73.10, 37.57, * -75.02, 36.53, * -78.50, 33.14, * -78.12, 23.46]), * width : 4 * }); */ function PolylineCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * Determines if polylines in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * The 4x4 transformation matrix that transforms each polyline in this collection from model to world coordinates. * When this is the identity matrix, the polylines are drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type {Matrix4} * @default {@link Matrix4.IDENTITY} */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); this._opaqueRS = undefined; this._translucentRS = undefined; this._colorCommands = []; this._polylinesUpdated = false; this._polylinesRemoved = false; this._createVertexArray = false; this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES$1); this._polylines = []; this._polylineBuckets = {}; // The buffer usage is determined based on the usage of the attribute over time. this._positionBufferUsage = { bufferUsage: BufferUsage$1.STATIC_DRAW, frameCount: 0, }; this._mode = undefined; this._polylinesToUpdate = []; this._vertexArrays = []; this._positionBuffer = undefined; this._texCoordExpandAndBatchIndexBuffer = undefined; this._batchTable = undefined; this._createBatchTable = false; // Only used by Vector3DTilePoints this._useHighlightColor = false; this._highlightColor = Color.clone(Color.WHITE); var that = this; this._uniformMap = { u_highlightColor: function () { return that._highlightColor; }, }; } Object.defineProperties(PolylineCollection.prototype, { /** * Returns the number of polylines in this collection. This is commonly used with * {@link PolylineCollection#get} to iterate over all the polylines * in the collection. * @memberof PolylineCollection.prototype * @type {Number} */ length: { get: function () { removePolylines(this); return this._polylines.length; }, }, }); /** * Creates and adds a polyline with the specified initial properties to the collection. * The added polyline is returned so it can be modified or removed from the collection later. * * @param {Object}[options] A template describing the polyline's properties as shown in Example 1. * @returns {Polyline} The polyline that was added to the collection. * * @performance After calling add, {@link PolylineCollection#update} is called and * the collection's vertex buffer is rewritten - an O(n) operation that also incurs CPU to GPU overhead. * For best performance, add as many polylines as possible before calling update. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Example 1: Add a polyline, specifying all the default values. * var p = polylines.add({ * show : true, * positions : ellipsoid.cartographicArrayToCartesianArray([ Cesium.Cartographic.fromDegrees(-75.10, 39.57), Cesium.Cartographic.fromDegrees(-77.02, 38.53)]), * width : 1 * }); * * @see PolylineCollection#remove * @see PolylineCollection#removeAll * @see PolylineCollection#update */ PolylineCollection.prototype.add = function (options) { var p = new Polyline(options, this); p._index = this._polylines.length; this._polylines.push(p); this._createVertexArray = true; this._createBatchTable = true; return p; }; /** * Removes a polyline from the collection. * * @param {Polyline} polyline The polyline to remove. * @returns {Boolean} true if the polyline was removed; false if the polyline was not found in the collection. * * @performance After calling remove, {@link PolylineCollection#update} is called and * the collection's vertex buffer is rewritten - an O(n) operation that also incurs CPU to GPU overhead. * For best performance, remove as many polylines as possible before calling update. * If you intend to temporarily hide a polyline, it is usually more efficient to call * {@link Polyline#show} instead of removing and re-adding the polyline. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * var p = polylines.add(...); * polylines.remove(p); // Returns true * * @see PolylineCollection#add * @see PolylineCollection#removeAll * @see PolylineCollection#update * @see Polyline#show */ PolylineCollection.prototype.remove = function (polyline) { if (this.contains(polyline)) { this._polylinesRemoved = true; this._createVertexArray = true; this._createBatchTable = true; if (defined(polyline._bucket)) { var bucket = polyline._bucket; bucket.shaderProgram = bucket.shaderProgram && bucket.shaderProgram.destroy(); } polyline._destroy(); return true; } return false; }; /** * Removes all polylines from the collection. * * @performance O(n). It is more efficient to remove all the polylines * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * polylines.add(...); * polylines.add(...); * polylines.removeAll(); * * @see PolylineCollection#add * @see PolylineCollection#remove * @see PolylineCollection#update */ PolylineCollection.prototype.removeAll = function () { releaseShaders(this); destroyPolylines(this); this._polylineBuckets = {}; this._polylinesRemoved = false; this._polylines.length = 0; this._polylinesToUpdate.length = 0; this._createVertexArray = true; }; /** * Determines if this collection contains the specified polyline. * * @param {Polyline} polyline The polyline to check for. * @returns {Boolean} true if this collection contains the polyline, false otherwise. * * @see PolylineCollection#get */ PolylineCollection.prototype.contains = function (polyline) { return defined(polyline) && polyline._polylineCollection === this; }; /** * Returns the polyline in the collection at the specified index. Indices are zero-based * and increase as polylines are added. Removing a polyline shifts all polylines after * it to the left, changing their indices. This function is commonly used with * {@link PolylineCollection#length} to iterate over all the polylines * in the collection. * * @param {Number} index The zero-based index of the polyline. * @returns {Polyline} The polyline at the specified index. * * @performance If polylines were removed from the collection and * {@link PolylineCollection#update} was not called, an implicit O(n) * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * // Toggle the show property of every polyline in the collection * var len = polylines.length; * for (var i = 0; i < len; ++i) { * var p = polylines.get(i); * p.show = !p.show; * } * * @see PolylineCollection#length */ PolylineCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); removePolylines(this); return this._polylines[index]; }; function createBatchTable(collection, context) { if (defined(collection._batchTable)) { collection._batchTable.destroy(); } var attributes = [ { functionName: "batchTable_getWidthAndShow", componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 2, }, { functionName: "batchTable_getPickColor", componentDatatype: ComponentDatatype$1.UNSIGNED_BYTE, componentsPerAttribute: 4, normalize: true, }, { functionName: "batchTable_getCenterHigh", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { functionName: "batchTable_getCenterLowAndRadius", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 4, }, { functionName: "batchTable_getDistanceDisplayCondition", componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, }, ]; collection._batchTable = new BatchTable( context, attributes, collection._polylines.length ); } var scratchUpdatePolylineEncodedCartesian = new EncodedCartesian3(); var scratchUpdatePolylineCartesian4 = new Cartesian4(); var scratchNearFarCartesian2 = new Cartesian2(); /** * Called when {@link Viewer} or {@link CesiumWidget} render the scene to * get the draw commands needed to render this primitive. *

* Do not call this function directly. This is documented just to * list the exceptions that may be propagated when the scene is rendered: *

* * @exception {RuntimeError} Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero. */ PolylineCollection.prototype.update = function (frameState) { removePolylines(this); if (this._polylines.length === 0 || !this.show) { return; } updateMode$1(this, frameState); var context = frameState.context; var projection = frameState.mapProjection; var polyline; var properties = this._propertiesChanged; if (this._createBatchTable) { if (ContextLimits.maximumVertexTextureImageUnits === 0) { throw new RuntimeError( "Vertex texture fetch support is required to render polylines. The maximum number of vertex texture image units must be greater than zero." ); } createBatchTable(this, context); this._createBatchTable = false; } if (this._createVertexArray || computeNewBuffersUsage(this)) { createVertexArrays(this, context, projection); } else if (this._polylinesUpdated) { // Polylines were modified, but no polylines were added or removed. var polylinesToUpdate = this._polylinesToUpdate; if (this._mode !== SceneMode$1.SCENE3D) { var updateLength = polylinesToUpdate.length; for (var i = 0; i < updateLength; ++i) { polyline = polylinesToUpdate[i]; polyline.update(); } } // if a polyline's positions size changes, we need to recreate the vertex arrays and vertex buffers because the indices will be different. // if a polyline's material changes, we need to recreate the VAOs and VBOs because they will be batched differently. if (properties[POSITION_SIZE_INDEX] || properties[MATERIAL_INDEX]) { createVertexArrays(this, context, projection); } else { var length = polylinesToUpdate.length; var polylineBuckets = this._polylineBuckets; for (var ii = 0; ii < length; ++ii) { polyline = polylinesToUpdate[ii]; properties = polyline._propertiesChanged; var bucket = polyline._bucket; var index = 0; for (var x in polylineBuckets) { if (polylineBuckets.hasOwnProperty(x)) { if (polylineBuckets[x] === bucket) { if (properties[POSITION_INDEX$2]) { bucket.writeUpdate( index, polyline, this._positionBuffer, projection ); } break; } index += polylineBuckets[x].lengthOfPositions; } } if (properties[SHOW_INDEX$2] || properties[WIDTH_INDEX]) { this._batchTable.setBatchedAttribute( polyline._index, 0, new Cartesian2(polyline._width, polyline._show) ); } if (this._batchTable.attributes.length > 2) { if (properties[POSITION_INDEX$2] || properties[POSITION_SIZE_INDEX]) { var boundingSphere = frameState.mode === SceneMode$1.SCENE2D ? polyline._boundingVolume2D : polyline._boundingVolumeWC; var encodedCenter = EncodedCartesian3.fromCartesian( boundingSphere.center, scratchUpdatePolylineEncodedCartesian ); var low = Cartesian4.fromElements( encodedCenter.low.x, encodedCenter.low.y, encodedCenter.low.z, boundingSphere.radius, scratchUpdatePolylineCartesian4 ); this._batchTable.setBatchedAttribute( polyline._index, 2, encodedCenter.high ); this._batchTable.setBatchedAttribute(polyline._index, 3, low); } if (properties[DISTANCE_DISPLAY_CONDITION]) { var nearFarCartesian = scratchNearFarCartesian2; nearFarCartesian.x = 0.0; nearFarCartesian.y = Number.MAX_VALUE; var distanceDisplayCondition = polyline.distanceDisplayCondition; if (defined(distanceDisplayCondition)) { nearFarCartesian.x = distanceDisplayCondition.near; nearFarCartesian.y = distanceDisplayCondition.far; } this._batchTable.setBatchedAttribute( polyline._index, 4, nearFarCartesian ); } } polyline._clean(); } } polylinesToUpdate.length = 0; this._polylinesUpdated = false; } properties = this._propertiesChanged; for (var k = 0; k < NUMBER_OF_PROPERTIES$1; ++k) { properties[k] = 0; } var modelMatrix = Matrix4.IDENTITY; if (frameState.mode === SceneMode$1.SCENE3D) { modelMatrix = this.modelMatrix; } var pass = frameState.passes; var useDepthTest = frameState.morphTime !== 0.0; if ( !defined(this._opaqueRS) || this._opaqueRS.depthTest.enabled !== useDepthTest ) { this._opaqueRS = RenderState.fromCache({ depthMask: useDepthTest, depthTest: { enabled: useDepthTest, }, }); } if ( !defined(this._translucentRS) || this._translucentRS.depthTest.enabled !== useDepthTest ) { this._translucentRS = RenderState.fromCache({ blending: BlendingState$1.ALPHA_BLEND, depthMask: !useDepthTest, depthTest: { enabled: useDepthTest, }, }); } this._batchTable.update(frameState); if (pass.render || pass.pick) { var colorList = this._colorCommands; createCommandLists(this, frameState, colorList, modelMatrix); } }; var boundingSphereScratch$2 = new BoundingSphere(); var boundingSphereScratch2 = new BoundingSphere(); function createCommandLists( polylineCollection, frameState, commands, modelMatrix ) { var context = frameState.context; var commandList = frameState.commandList; var commandsLength = commands.length; var commandIndex = 0; var cloneBoundingSphere = true; var vertexArrays = polylineCollection._vertexArrays; var debugShowBoundingVolume = polylineCollection.debugShowBoundingVolume; var batchTable = polylineCollection._batchTable; var uniformCallback = batchTable.getUniformMapCallback(); var length = vertexArrays.length; for (var m = 0; m < length; ++m) { var va = vertexArrays[m]; var buckets = va.buckets; var bucketLength = buckets.length; for (var n = 0; n < bucketLength; ++n) { var bucketLocator = buckets[n]; var offset = bucketLocator.offset; var sp = bucketLocator.bucket.shaderProgram; var polylines = bucketLocator.bucket.polylines; var polylineLength = polylines.length; var currentId; var currentMaterial; var count = 0; var command; var uniformMap; for (var s = 0; s < polylineLength; ++s) { var polyline = polylines[s]; var mId = createMaterialId(polyline._material); if (mId !== currentId) { if (defined(currentId) && count > 0) { var translucent = currentMaterial.isTranslucent(); if (commandIndex >= commandsLength) { command = new DrawCommand({ owner: polylineCollection, }); commands.push(command); } else { command = commands[commandIndex]; } ++commandIndex; uniformMap = combine$2( uniformCallback(currentMaterial._uniforms), polylineCollection._uniformMap ); command.boundingVolume = BoundingSphere.clone( boundingSphereScratch$2, command.boundingVolume ); command.modelMatrix = modelMatrix; command.shaderProgram = sp; command.vertexArray = va.va; command.renderState = translucent ? polylineCollection._translucentRS : polylineCollection._opaqueRS; command.pass = translucent ? Pass$1.TRANSLUCENT : Pass$1.OPAQUE; command.debugShowBoundingVolume = debugShowBoundingVolume; command.pickId = "v_pickColor"; command.uniformMap = uniformMap; command.count = count; command.offset = offset; offset += count; count = 0; cloneBoundingSphere = true; commandList.push(command); } currentMaterial = polyline._material; currentMaterial.update(context); currentId = mId; } var locators = polyline._locatorBuckets; var locatorLength = locators.length; for (var t = 0; t < locatorLength; ++t) { var locator = locators[t]; if (locator.locator === bucketLocator) { count += locator.count; } } var boundingVolume; if (frameState.mode === SceneMode$1.SCENE3D) { boundingVolume = polyline._boundingVolumeWC; } else if (frameState.mode === SceneMode$1.COLUMBUS_VIEW) { boundingVolume = polyline._boundingVolume2D; } else if (frameState.mode === SceneMode$1.SCENE2D) { if (defined(polyline._boundingVolume2D)) { boundingVolume = BoundingSphere.clone( polyline._boundingVolume2D, boundingSphereScratch2 ); boundingVolume.center.x = 0.0; } } else if ( defined(polyline._boundingVolumeWC) && defined(polyline._boundingVolume2D) ) { boundingVolume = BoundingSphere.union( polyline._boundingVolumeWC, polyline._boundingVolume2D, boundingSphereScratch2 ); } if (cloneBoundingSphere) { cloneBoundingSphere = false; BoundingSphere.clone(boundingVolume, boundingSphereScratch$2); } else { BoundingSphere.union( boundingVolume, boundingSphereScratch$2, boundingSphereScratch$2 ); } } if (defined(currentId) && count > 0) { if (commandIndex >= commandsLength) { command = new DrawCommand({ owner: polylineCollection, }); commands.push(command); } else { command = commands[commandIndex]; } ++commandIndex; uniformMap = combine$2( uniformCallback(currentMaterial._uniforms), polylineCollection._uniformMap ); command.boundingVolume = BoundingSphere.clone( boundingSphereScratch$2, command.boundingVolume ); command.modelMatrix = modelMatrix; command.shaderProgram = sp; command.vertexArray = va.va; command.renderState = currentMaterial.isTranslucent() ? polylineCollection._translucentRS : polylineCollection._opaqueRS; command.pass = currentMaterial.isTranslucent() ? Pass$1.TRANSLUCENT : Pass$1.OPAQUE; command.debugShowBoundingVolume = debugShowBoundingVolume; command.pickId = "v_pickColor"; command.uniformMap = uniformMap; command.count = count; command.offset = offset; cloneBoundingSphere = true; commandList.push(command); } currentId = undefined; } } commands.length = commandIndex; } /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see PolylineCollection#destroy */ PolylineCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * polylines = polylines && polylines.destroy(); * * @see PolylineCollection#isDestroyed */ PolylineCollection.prototype.destroy = function () { destroyVertexArrays(this); releaseShaders(this); destroyPolylines(this); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; function computeNewBuffersUsage(collection) { var usageChanged = false; var properties = collection._propertiesChanged; var bufferUsage = collection._positionBufferUsage; if (properties[POSITION_INDEX$2]) { if (bufferUsage.bufferUsage !== BufferUsage$1.STREAM_DRAW) { usageChanged = true; bufferUsage.bufferUsage = BufferUsage$1.STREAM_DRAW; bufferUsage.frameCount = 100; } else { bufferUsage.frameCount = 100; } } else if (bufferUsage.bufferUsage !== BufferUsage$1.STATIC_DRAW) { if (bufferUsage.frameCount === 0) { usageChanged = true; bufferUsage.bufferUsage = BufferUsage$1.STATIC_DRAW; } else { bufferUsage.frameCount--; } } return usageChanged; } var emptyVertexBuffer = [0.0, 0.0, 0.0]; function createVertexArrays(collection, context, projection) { collection._createVertexArray = false; releaseShaders(collection); destroyVertexArrays(collection); sortPolylinesIntoBuckets(collection); //stores all of the individual indices arrays. var totalIndices = [[]]; var indices = totalIndices[0]; var batchTable = collection._batchTable; var useHighlightColor = collection._useHighlightColor; //used to determine the vertexBuffer offset if the indicesArray goes over 64k. //if it's the same polyline while it goes over 64k, the offset needs to backtrack componentsPerAttribute * componentDatatype bytes //so that the polyline looks contiguous. //if the polyline ends at the 64k mark, then the offset is just 64k * componentsPerAttribute * componentDatatype var vertexBufferOffset = [0]; var offset = 0; var vertexArrayBuckets = [[]]; var totalLength = 0; var polylineBuckets = collection._polylineBuckets; var x; var bucket; for (x in polylineBuckets) { if (polylineBuckets.hasOwnProperty(x)) { bucket = polylineBuckets[x]; bucket.updateShader(context, batchTable, useHighlightColor); totalLength += bucket.lengthOfPositions; } } if (totalLength > 0) { var mode = collection._mode; var positionArray = new Float32Array(6 * totalLength * 3); var texCoordExpandAndBatchIndexArray = new Float32Array(totalLength * 4); var position3DArray; var positionIndex = 0; var colorIndex = 0; var texCoordExpandAndBatchIndexIndex = 0; for (x in polylineBuckets) { if (polylineBuckets.hasOwnProperty(x)) { bucket = polylineBuckets[x]; bucket.write( positionArray, texCoordExpandAndBatchIndexArray, positionIndex, colorIndex, texCoordExpandAndBatchIndexIndex, batchTable, context, projection ); if (mode === SceneMode$1.MORPHING) { if (!defined(position3DArray)) { position3DArray = new Float32Array(6 * totalLength * 3); } bucket.writeForMorph(position3DArray, positionIndex); } var bucketLength = bucket.lengthOfPositions; positionIndex += 6 * bucketLength * 3; colorIndex += bucketLength * 4; texCoordExpandAndBatchIndexIndex += bucketLength * 4; offset = bucket.updateIndices( totalIndices, vertexBufferOffset, vertexArrayBuckets, offset ); } } var positionBufferUsage = collection._positionBufferUsage.bufferUsage; var texCoordExpandAndBatchIndexBufferUsage = BufferUsage$1.STATIC_DRAW; collection._positionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: positionArray, usage: positionBufferUsage, }); var position3DBuffer; if (defined(position3DArray)) { position3DBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: position3DArray, usage: positionBufferUsage, }); } collection._texCoordExpandAndBatchIndexBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: texCoordExpandAndBatchIndexArray, usage: texCoordExpandAndBatchIndexBufferUsage, }); var positionSizeInBytes = 3 * Float32Array.BYTES_PER_ELEMENT; var texCoordExpandAndBatchIndexSizeInBytes = 4 * Float32Array.BYTES_PER_ELEMENT; var vbo = 0; var numberOfIndicesArrays = totalIndices.length; for (var k = 0; k < numberOfIndicesArrays; ++k) { indices = totalIndices[k]; if (indices.length > 0) { var indicesArray = new Uint16Array(indices); var indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: indicesArray, usage: BufferUsage$1.STATIC_DRAW, indexDatatype: IndexDatatype$1.UNSIGNED_SHORT, }); vbo += vertexBufferOffset[k]; var positionHighOffset = 6 * (k * (positionSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) - vbo * positionSizeInBytes); //componentsPerAttribute(3) * componentDatatype(4) var positionLowOffset = positionSizeInBytes + positionHighOffset; var prevPositionHighOffset = positionSizeInBytes + positionLowOffset; var prevPositionLowOffset = positionSizeInBytes + prevPositionHighOffset; var nextPositionHighOffset = positionSizeInBytes + prevPositionLowOffset; var nextPositionLowOffset = positionSizeInBytes + nextPositionHighOffset; var vertexTexCoordExpandAndBatchIndexBufferOffset = k * (texCoordExpandAndBatchIndexSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES) - vbo * texCoordExpandAndBatchIndexSizeInBytes; var attributes = [ { index: attributeLocations$3.position3DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: positionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.position3DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: positionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.position2DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: positionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.position2DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: positionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.prevPosition3DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: prevPositionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.prevPosition3DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: prevPositionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.prevPosition2DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: prevPositionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.prevPosition2DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: prevPositionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.nextPosition3DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: nextPositionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.nextPosition3DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: nextPositionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.nextPosition2DHigh, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: nextPositionHighOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.nextPosition2DLow, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, offsetInBytes: nextPositionLowOffset, strideInBytes: 6 * positionSizeInBytes, }, { index: attributeLocations$3.texCoordExpandAndBatchIndex, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, vertexBuffer: collection._texCoordExpandAndBatchIndexBuffer, offsetInBytes: vertexTexCoordExpandAndBatchIndexBufferOffset, }, ]; var buffer3D; var bufferProperty3D; var buffer2D; var bufferProperty2D; if (mode === SceneMode$1.SCENE3D) { buffer3D = collection._positionBuffer; bufferProperty3D = "vertexBuffer"; buffer2D = emptyVertexBuffer; bufferProperty2D = "value"; } else if ( mode === SceneMode$1.SCENE2D || mode === SceneMode$1.COLUMBUS_VIEW ) { buffer3D = emptyVertexBuffer; bufferProperty3D = "value"; buffer2D = collection._positionBuffer; bufferProperty2D = "vertexBuffer"; } else { buffer3D = position3DBuffer; bufferProperty3D = "vertexBuffer"; buffer2D = collection._positionBuffer; bufferProperty2D = "vertexBuffer"; } attributes[0][bufferProperty3D] = buffer3D; attributes[1][bufferProperty3D] = buffer3D; attributes[2][bufferProperty2D] = buffer2D; attributes[3][bufferProperty2D] = buffer2D; attributes[4][bufferProperty3D] = buffer3D; attributes[5][bufferProperty3D] = buffer3D; attributes[6][bufferProperty2D] = buffer2D; attributes[7][bufferProperty2D] = buffer2D; attributes[8][bufferProperty3D] = buffer3D; attributes[9][bufferProperty3D] = buffer3D; attributes[10][bufferProperty2D] = buffer2D; attributes[11][bufferProperty2D] = buffer2D; var va = new VertexArray({ context: context, attributes: attributes, indexBuffer: indexBuffer, }); collection._vertexArrays.push({ va: va, buckets: vertexArrayBuckets[k], }); } } } } function replacer(key, value) { if (value instanceof Texture) { return value.id; } return value; } var scratchUniformArray = []; function createMaterialId(material) { var uniforms = Material._uniformList[material.type]; var length = uniforms.length; scratchUniformArray.length = 2.0 * length; var index = 0; for (var i = 0; i < length; ++i) { var uniform = uniforms[i]; scratchUniformArray[index] = uniform; scratchUniformArray[index + 1] = material._uniforms[uniform](); index += 2; } return material.type + ":" + JSON.stringify(scratchUniformArray, replacer); } function sortPolylinesIntoBuckets(collection) { var mode = collection._mode; var modelMatrix = collection._modelMatrix; var polylineBuckets = (collection._polylineBuckets = {}); var polylines = collection._polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { var p = polylines[i]; if (p._actualPositions.length > 1) { p.update(); var material = p.material; var value = polylineBuckets[material.type]; if (!defined(value)) { value = polylineBuckets[material.type] = new PolylineBucket( material, mode, modelMatrix ); } value.addPolyline(p); } } } function updateMode$1(collection, frameState) { var mode = frameState.mode; if ( collection._mode !== mode || !Matrix4.equals(collection._modelMatrix, collection.modelMatrix) ) { collection._mode = mode; collection._modelMatrix = Matrix4.clone(collection.modelMatrix); collection._createVertexArray = true; } } function removePolylines(collection) { if (collection._polylinesRemoved) { collection._polylinesRemoved = false; var definedPolylines = []; var definedPolylinesToUpdate = []; var polyIndex = 0; var polyline; var length = collection._polylines.length; for (var i = 0; i < length; ++i) { polyline = collection._polylines[i]; if (!polyline.isDestroyed) { polyline._index = polyIndex++; definedPolylinesToUpdate.push(polyline); definedPolylines.push(polyline); } } collection._polylines = definedPolylines; collection._polylinesToUpdate = definedPolylinesToUpdate; } } function releaseShaders(collection) { var polylines = collection._polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { if (!polylines[i].isDestroyed) { var bucket = polylines[i]._bucket; if (defined(bucket)) { bucket.shaderProgram = bucket.shaderProgram && bucket.shaderProgram.destroy(); } } } } function destroyVertexArrays(collection) { var length = collection._vertexArrays.length; for (var t = 0; t < length; ++t) { collection._vertexArrays[t].va.destroy(); } collection._vertexArrays.length = 0; } PolylineCollection.prototype._updatePolyline = function ( polyline, propertyChanged ) { this._polylinesUpdated = true; if (!polyline._dirty) { this._polylinesToUpdate.push(polyline); } ++this._propertiesChanged[propertyChanged]; }; function destroyPolylines(collection) { var polylines = collection._polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { if (!polylines[i].isDestroyed) { polylines[i]._destroy(); } } } function VertexArrayBucketLocator(count, offset, bucket) { this.count = count; this.offset = offset; this.bucket = bucket; } function PolylineBucket(material, mode, modelMatrix) { this.polylines = []; this.lengthOfPositions = 0; this.material = material; this.shaderProgram = undefined; this.mode = mode; this.modelMatrix = modelMatrix; } PolylineBucket.prototype.addPolyline = function (p) { var polylines = this.polylines; polylines.push(p); p._actualLength = this.getPolylinePositionsLength(p); this.lengthOfPositions += p._actualLength; p._bucket = this; }; PolylineBucket.prototype.updateShader = function ( context, batchTable, useHighlightColor ) { if (defined(this.shaderProgram)) { return; } var defines = ["DISTANCE_DISPLAY_CONDITION"]; if (useHighlightColor) { defines.push("VECTOR_TILE"); } // Check for use of v_polylineAngle in material shader if ( this.material.shaderSource.search(/varying\s+float\s+v_polylineAngle;/g) !== -1 ) { defines.push("POLYLINE_DASH"); } if (!FeatureDetection.isInternetExplorer()) { defines.push("CLIP_POLYLINE"); } var fs = new ShaderSource({ defines: defines, sources: [ "varying vec4 v_pickColor;\n", this.material.shaderSource, PolylineFS$1, ], }); var vsSource = batchTable.getVertexShaderCallback()(PolylineVS); var vs = new ShaderSource({ defines: defines, sources: [PolylineCommon, vsSource], }); this.shaderProgram = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$3, }); }; function intersectsIDL(polyline) { return ( Cartesian3.dot(Cartesian3.UNIT_X, polyline._boundingVolume.center) < 0 || polyline._boundingVolume.intersectPlane(Plane.ORIGIN_ZX_PLANE) === Intersect$1.INTERSECTING ); } PolylineBucket.prototype.getPolylinePositionsLength = function (polyline) { var length; if (this.mode === SceneMode$1.SCENE3D || !intersectsIDL(polyline)) { length = polyline._actualPositions.length; return length * 4.0 - 4.0; } var count = 0; var segmentLengths = polyline._segments.lengths; length = segmentLengths.length; for (var i = 0; i < length; ++i) { count += segmentLengths[i] * 4.0 - 4.0; } return count; }; var scratchWritePosition = new Cartesian3(); var scratchWritePrevPosition = new Cartesian3(); var scratchWriteNextPosition = new Cartesian3(); var scratchWriteVector = new Cartesian3(); var scratchPickColorCartesian = new Cartesian4(); var scratchWidthShowCartesian = new Cartesian2(); PolylineBucket.prototype.write = function ( positionArray, texCoordExpandAndBatchIndexArray, positionIndex, colorIndex, texCoordExpandAndBatchIndexIndex, batchTable, context, projection ) { var mode = this.mode; var maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI; var polylines = this.polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { var polyline = polylines[i]; var width = polyline.width; var show = polyline.show && width > 0.0; var polylineBatchIndex = polyline._index; var segments = this.getSegments(polyline, projection); var positions = segments.positions; var lengths = segments.lengths; var positionsLength = positions.length; var pickColor = polyline.getPickId(context).color; var segmentIndex = 0; var count = 0; var position; for (var j = 0; j < positionsLength; ++j) { if (j === 0) { if (polyline._loop) { position = positions[positionsLength - 2]; } else { position = scratchWriteVector; Cartesian3.subtract(positions[0], positions[1], position); Cartesian3.add(positions[0], position, position); } } else { position = positions[j - 1]; } Cartesian3.clone(position, scratchWritePrevPosition); Cartesian3.clone(positions[j], scratchWritePosition); if (j === positionsLength - 1) { if (polyline._loop) { position = positions[1]; } else { position = scratchWriteVector; Cartesian3.subtract( positions[positionsLength - 1], positions[positionsLength - 2], position ); Cartesian3.add(positions[positionsLength - 1], position, position); } } else { position = positions[j + 1]; } Cartesian3.clone(position, scratchWriteNextPosition); var segmentLength = lengths[segmentIndex]; if (j === count + segmentLength) { count += segmentLength; ++segmentIndex; } var segmentStart = j - count === 0; var segmentEnd = j === count + lengths[segmentIndex] - 1; if (mode === SceneMode$1.SCENE2D) { scratchWritePrevPosition.z = 0.0; scratchWritePosition.z = 0.0; scratchWriteNextPosition.z = 0.0; } if (mode === SceneMode$1.SCENE2D || mode === SceneMode$1.MORPHING) { if ( (segmentStart || segmentEnd) && maxLon - Math.abs(scratchWritePosition.x) < 1.0 ) { if ( (scratchWritePosition.x < 0.0 && scratchWritePrevPosition.x > 0.0) || (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0) ) { Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition); } if ( (scratchWritePosition.x < 0.0 && scratchWriteNextPosition.x > 0.0) || (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0) ) { Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition); } } } var startK = segmentStart ? 2 : 0; var endK = segmentEnd ? 2 : 4; for (var k = startK; k < endK; ++k) { EncodedCartesian3.writeElements( scratchWritePosition, positionArray, positionIndex ); EncodedCartesian3.writeElements( scratchWritePrevPosition, positionArray, positionIndex + 6 ); EncodedCartesian3.writeElements( scratchWriteNextPosition, positionArray, positionIndex + 12 ); var direction = k - 2 < 0 ? -1.0 : 1.0; texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex] = j / (positionsLength - 1); // s tex coord texCoordExpandAndBatchIndexArray[texCoordExpandAndBatchIndexIndex + 1] = 2 * (k % 2) - 1; // expand direction texCoordExpandAndBatchIndexArray[ texCoordExpandAndBatchIndexIndex + 2 ] = direction; texCoordExpandAndBatchIndexArray[ texCoordExpandAndBatchIndexIndex + 3 ] = polylineBatchIndex; positionIndex += 6 * 3; texCoordExpandAndBatchIndexIndex += 4; } } var colorCartesian = scratchPickColorCartesian; colorCartesian.x = Color.floatToByte(pickColor.red); colorCartesian.y = Color.floatToByte(pickColor.green); colorCartesian.z = Color.floatToByte(pickColor.blue); colorCartesian.w = Color.floatToByte(pickColor.alpha); var widthShowCartesian = scratchWidthShowCartesian; widthShowCartesian.x = width; widthShowCartesian.y = show ? 1.0 : 0.0; var boundingSphere = mode === SceneMode$1.SCENE2D ? polyline._boundingVolume2D : polyline._boundingVolumeWC; var encodedCenter = EncodedCartesian3.fromCartesian( boundingSphere.center, scratchUpdatePolylineEncodedCartesian ); var high = encodedCenter.high; var low = Cartesian4.fromElements( encodedCenter.low.x, encodedCenter.low.y, encodedCenter.low.z, boundingSphere.radius, scratchUpdatePolylineCartesian4 ); var nearFarCartesian = scratchNearFarCartesian2; nearFarCartesian.x = 0.0; nearFarCartesian.y = Number.MAX_VALUE; var distanceDisplayCondition = polyline.distanceDisplayCondition; if (defined(distanceDisplayCondition)) { nearFarCartesian.x = distanceDisplayCondition.near; nearFarCartesian.y = distanceDisplayCondition.far; } batchTable.setBatchedAttribute(polylineBatchIndex, 0, widthShowCartesian); batchTable.setBatchedAttribute(polylineBatchIndex, 1, colorCartesian); if (batchTable.attributes.length > 2) { batchTable.setBatchedAttribute(polylineBatchIndex, 2, high); batchTable.setBatchedAttribute(polylineBatchIndex, 3, low); batchTable.setBatchedAttribute(polylineBatchIndex, 4, nearFarCartesian); } } }; var morphPositionScratch = new Cartesian3(); var morphPrevPositionScratch = new Cartesian3(); var morphNextPositionScratch = new Cartesian3(); var morphVectorScratch = new Cartesian3(); PolylineBucket.prototype.writeForMorph = function ( positionArray, positionIndex ) { var modelMatrix = this.modelMatrix; var polylines = this.polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { var polyline = polylines[i]; var positions = polyline._segments.positions; var lengths = polyline._segments.lengths; var positionsLength = positions.length; var segmentIndex = 0; var count = 0; for (var j = 0; j < positionsLength; ++j) { var prevPosition; if (j === 0) { if (polyline._loop) { prevPosition = positions[positionsLength - 2]; } else { prevPosition = morphVectorScratch; Cartesian3.subtract(positions[0], positions[1], prevPosition); Cartesian3.add(positions[0], prevPosition, prevPosition); } } else { prevPosition = positions[j - 1]; } prevPosition = Matrix4.multiplyByPoint( modelMatrix, prevPosition, morphPrevPositionScratch ); var position = Matrix4.multiplyByPoint( modelMatrix, positions[j], morphPositionScratch ); var nextPosition; if (j === positionsLength - 1) { if (polyline._loop) { nextPosition = positions[1]; } else { nextPosition = morphVectorScratch; Cartesian3.subtract( positions[positionsLength - 1], positions[positionsLength - 2], nextPosition ); Cartesian3.add( positions[positionsLength - 1], nextPosition, nextPosition ); } } else { nextPosition = positions[j + 1]; } nextPosition = Matrix4.multiplyByPoint( modelMatrix, nextPosition, morphNextPositionScratch ); var segmentLength = lengths[segmentIndex]; if (j === count + segmentLength) { count += segmentLength; ++segmentIndex; } var segmentStart = j - count === 0; var segmentEnd = j === count + lengths[segmentIndex] - 1; var startK = segmentStart ? 2 : 0; var endK = segmentEnd ? 2 : 4; for (var k = startK; k < endK; ++k) { EncodedCartesian3.writeElements(position, positionArray, positionIndex); EncodedCartesian3.writeElements( prevPosition, positionArray, positionIndex + 6 ); EncodedCartesian3.writeElements( nextPosition, positionArray, positionIndex + 12 ); positionIndex += 6 * 3; } } } }; var scratchSegmentLengths = new Array(1); PolylineBucket.prototype.updateIndices = function ( totalIndices, vertexBufferOffset, vertexArrayBuckets, offset ) { var vaCount = vertexArrayBuckets.length - 1; var bucketLocator = new VertexArrayBucketLocator(0, offset, this); vertexArrayBuckets[vaCount].push(bucketLocator); var count = 0; var indices = totalIndices[totalIndices.length - 1]; var indicesCount = 0; if (indices.length > 0) { indicesCount = indices[indices.length - 1] + 1; } var polylines = this.polylines; var length = polylines.length; for (var i = 0; i < length; ++i) { var polyline = polylines[i]; polyline._locatorBuckets = []; var segments; if (this.mode === SceneMode$1.SCENE3D) { segments = scratchSegmentLengths; var positionsLength = polyline._actualPositions.length; if (positionsLength > 0) { segments[0] = positionsLength; } else { continue; } } else { segments = polyline._segments.lengths; } var numberOfSegments = segments.length; if (numberOfSegments > 0) { var segmentIndexCount = 0; for (var j = 0; j < numberOfSegments; ++j) { var segmentLength = segments[j] - 1.0; for (var k = 0; k < segmentLength; ++k) { if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) { polyline._locatorBuckets.push({ locator: bucketLocator, count: segmentIndexCount, }); segmentIndexCount = 0; vertexBufferOffset.push(4); indices = []; totalIndices.push(indices); indicesCount = 0; bucketLocator.count = count; count = 0; offset = 0; bucketLocator = new VertexArrayBucketLocator(0, 0, this); vertexArrayBuckets[++vaCount] = [bucketLocator]; } indices.push(indicesCount, indicesCount + 2, indicesCount + 1); indices.push(indicesCount + 1, indicesCount + 2, indicesCount + 3); segmentIndexCount += 6; count += 6; offset += 6; indicesCount += 4; } } polyline._locatorBuckets.push({ locator: bucketLocator, count: segmentIndexCount, }); if (indicesCount + 4 > CesiumMath.SIXTY_FOUR_KILOBYTES) { vertexBufferOffset.push(0); indices = []; totalIndices.push(indices); indicesCount = 0; bucketLocator.count = count; offset = 0; count = 0; bucketLocator = new VertexArrayBucketLocator(0, 0, this); vertexArrayBuckets[++vaCount] = [bucketLocator]; } } polyline._clean(); } bucketLocator.count = count; return offset; }; PolylineBucket.prototype.getPolylineStartIndex = function (polyline) { var polylines = this.polylines; var positionIndex = 0; var length = polylines.length; for (var i = 0; i < length; ++i) { var p = polylines[i]; if (p === polyline) { break; } positionIndex += p._actualLength; } return positionIndex; }; var scratchSegments = { positions: undefined, lengths: undefined, }; var scratchLengths = new Array(1); var pscratch = new Cartesian3(); var scratchCartographic$9 = new Cartographic(); PolylineBucket.prototype.getSegments = function (polyline, projection) { var positions = polyline._actualPositions; if (this.mode === SceneMode$1.SCENE3D) { scratchLengths[0] = positions.length; scratchSegments.positions = positions; scratchSegments.lengths = scratchLengths; return scratchSegments; } if (intersectsIDL(polyline)) { positions = polyline._segments.positions; } var ellipsoid = projection.ellipsoid; var newPositions = []; var modelMatrix = this.modelMatrix; var length = positions.length; var position; var p = pscratch; for (var n = 0; n < length; ++n) { position = positions[n]; p = Matrix4.multiplyByPoint(modelMatrix, position, p); newPositions.push( projection.project( ellipsoid.cartesianToCartographic(p, scratchCartographic$9) ) ); } if (newPositions.length > 0) { polyline._boundingVolume2D = BoundingSphere.fromPoints( newPositions, polyline._boundingVolume2D ); var center2D = polyline._boundingVolume2D.center; polyline._boundingVolume2D.center = new Cartesian3( center2D.z, center2D.x, center2D.y ); } scratchSegments.positions = newPositions; scratchSegments.lengths = polyline._segments.lengths; return scratchSegments; }; var scratchPositionsArray; PolylineBucket.prototype.writeUpdate = function ( index, polyline, positionBuffer, projection ) { var mode = this.mode; var maxLon = projection.ellipsoid.maximumRadius * CesiumMath.PI; var positionsLength = polyline._actualLength; if (positionsLength) { index += this.getPolylineStartIndex(polyline); var positionArray = scratchPositionsArray; var positionsArrayLength = 6 * positionsLength * 3; if ( !defined(positionArray) || positionArray.length < positionsArrayLength ) { positionArray = scratchPositionsArray = new Float32Array( positionsArrayLength ); } else if (positionArray.length > positionsArrayLength) { positionArray = new Float32Array( positionArray.buffer, 0, positionsArrayLength ); } var segments = this.getSegments(polyline, projection); var positions = segments.positions; var lengths = segments.lengths; var positionIndex = 0; var segmentIndex = 0; var count = 0; var position; positionsLength = positions.length; for (var i = 0; i < positionsLength; ++i) { if (i === 0) { if (polyline._loop) { position = positions[positionsLength - 2]; } else { position = scratchWriteVector; Cartesian3.subtract(positions[0], positions[1], position); Cartesian3.add(positions[0], position, position); } } else { position = positions[i - 1]; } Cartesian3.clone(position, scratchWritePrevPosition); Cartesian3.clone(positions[i], scratchWritePosition); if (i === positionsLength - 1) { if (polyline._loop) { position = positions[1]; } else { position = scratchWriteVector; Cartesian3.subtract( positions[positionsLength - 1], positions[positionsLength - 2], position ); Cartesian3.add(positions[positionsLength - 1], position, position); } } else { position = positions[i + 1]; } Cartesian3.clone(position, scratchWriteNextPosition); var segmentLength = lengths[segmentIndex]; if (i === count + segmentLength) { count += segmentLength; ++segmentIndex; } var segmentStart = i - count === 0; var segmentEnd = i === count + lengths[segmentIndex] - 1; if (mode === SceneMode$1.SCENE2D) { scratchWritePrevPosition.z = 0.0; scratchWritePosition.z = 0.0; scratchWriteNextPosition.z = 0.0; } if (mode === SceneMode$1.SCENE2D || mode === SceneMode$1.MORPHING) { if ( (segmentStart || segmentEnd) && maxLon - Math.abs(scratchWritePosition.x) < 1.0 ) { if ( (scratchWritePosition.x < 0.0 && scratchWritePrevPosition.x > 0.0) || (scratchWritePosition.x > 0.0 && scratchWritePrevPosition.x < 0.0) ) { Cartesian3.clone(scratchWritePosition, scratchWritePrevPosition); } if ( (scratchWritePosition.x < 0.0 && scratchWriteNextPosition.x > 0.0) || (scratchWritePosition.x > 0.0 && scratchWriteNextPosition.x < 0.0) ) { Cartesian3.clone(scratchWritePosition, scratchWriteNextPosition); } } } var startJ = segmentStart ? 2 : 0; var endJ = segmentEnd ? 2 : 4; for (var j = startJ; j < endJ; ++j) { EncodedCartesian3.writeElements( scratchWritePosition, positionArray, positionIndex ); EncodedCartesian3.writeElements( scratchWritePrevPosition, positionArray, positionIndex + 6 ); EncodedCartesian3.writeElements( scratchWriteNextPosition, positionArray, positionIndex + 12 ); positionIndex += 6 * 3; } } positionBuffer.copyFromArrayView( positionArray, 6 * 3 * Float32Array.BYTES_PER_ELEMENT * index ); } }; /** * Creates a batch of points or billboards and labels. * * @alias Vector3DTilePoints * @constructor * * @param {Object} options An object with following properties: * @param {Uint16Array} options.positions The positions of the polygons. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Rectangle} options.rectangle The rectangle containing the tile. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polygons. * @param {Uint16Array} options.batchIds The batch ids for each polygon. * * @private */ function Vector3DTilePoints(options) { // released after the first update this._positions = options.positions; this._batchTable = options.batchTable; this._batchIds = options.batchIds; this._rectangle = options.rectangle; this._minHeight = options.minimumHeight; this._maxHeight = options.maximumHeight; this._billboardCollection = undefined; this._labelCollection = undefined; this._polylineCollection = undefined; this._verticesPromise = undefined; this._packedBuffer = undefined; this._ready = false; this._readyPromise = when.defer(); this._resolvedPromise = false; } Object.defineProperties(Vector3DTilePoints.prototype, { /** * Gets the number of points. * * @memberof Vector3DTilePoints.prototype * * @type {Number} * @readonly */ pointsLength: { get: function () { return this._billboardCollection.length; }, }, /** * Gets the texture atlas memory in bytes. * * @memberof Vector3DTilePoints.prototype * * @type {Number} * @readonly */ texturesByteLength: { get: function () { var billboardSize = this._billboardCollection.textureAtlas.texture .sizeInBytes; var labelSize = this._labelCollection._textureAtlas.texture.sizeInBytes; return billboardSize + labelSize; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTilePoints.prototype * @type {Promise} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); function packBuffer$2(points, ellipsoid) { var rectangle = points._rectangle; var minimumHeight = points._minHeight; var maximumHeight = points._maxHeight; var packedLength = 2 + Rectangle.packedLength + Ellipsoid.packedLength; var packedBuffer = new Float64Array(packedLength); var offset = 0; packedBuffer[offset++] = minimumHeight; packedBuffer[offset++] = maximumHeight; Rectangle.pack(rectangle, packedBuffer, offset); offset += Rectangle.packedLength; Ellipsoid.pack(ellipsoid, packedBuffer, offset); return packedBuffer; } var createVerticesTaskProcessor$2 = new TaskProcessor( "createVectorTilePoints", 5 ); var scratchPosition$3 = new Cartesian3(); function createPoints(points, ellipsoid) { if (defined(points._billboardCollection)) { return; } var positions; if (!defined(points._verticesPromise)) { positions = points._positions; var packedBuffer = points._packedBuffer; if (!defined(packedBuffer)) { // Copy because they may be the views on the same buffer. positions = points._positions = arraySlice(positions); points._batchIds = arraySlice(points._batchIds); packedBuffer = points._packedBuffer = packBuffer$2(points, ellipsoid); } var transferrableObjects = [positions.buffer, packedBuffer.buffer]; var parameters = { positions: positions.buffer, packedBuffer: packedBuffer.buffer, }; var verticesPromise = (points._verticesPromise = createVerticesTaskProcessor$2.scheduleTask( parameters, transferrableObjects )); if (!defined(verticesPromise)) { // Postponed return; } verticesPromise.then(function (result) { points._positions = new Float64Array(result.positions); points._ready = true; }); } if (points._ready && !defined(points._billboardCollection)) { positions = points._positions; var batchTable = points._batchTable; var batchIds = points._batchIds; var billboardCollection = (points._billboardCollection = new BillboardCollection( { batchTable: batchTable } )); var labelCollection = (points._labelCollection = new LabelCollection({ batchTable: batchTable, })); var polylineCollection = (points._polylineCollection = new PolylineCollection()); polylineCollection._useHighlightColor = true; var numberOfPoints = positions.length / 3; for (var i = 0; i < numberOfPoints; ++i) { var id = batchIds[i]; var position = Cartesian3.unpack(positions, i * 3, scratchPosition$3); var b = billboardCollection.add(); b.position = position; b._batchIndex = id; var l = labelCollection.add(); l.text = " "; l.position = position; l._batchIndex = id; var p = polylineCollection.add(); p.positions = [Cartesian3.clone(position), Cartesian3.clone(position)]; } points._positions = undefined; points._packedBuffer = undefined; } } /** * Creates features for each point and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the point features will be placed. */ Vector3DTilePoints.prototype.createFeatures = function (content, features) { var billboardCollection = this._billboardCollection; var labelCollection = this._labelCollection; var polylineCollection = this._polylineCollection; var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var billboard = billboardCollection.get(i); var label = labelCollection.get(i); var polyline = polylineCollection.get(i); features[batchId] = new Cesium3DTilePointFeature( content, batchId, billboard, label, polyline ); } }; /** * Colors the entire tile when enabled is true. The resulting color will be (batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePoints.prototype.applyDebugSettings = function (enabled, color) { if (enabled) { Color.clone(color, this._billboardCollection._highlightColor); Color.clone(color, this._labelCollection._highlightColor); Color.clone(color, this._polylineCollection._highlightColor); } else { Color.clone(Color.WHITE, this._billboardCollection._highlightColor); Color.clone(Color.WHITE, this._labelCollection._highlightColor); Color.clone(Color.WHITE, this._polylineCollection._highlightColor); } }; function clearStyle$1(polygons, features) { var batchIds = polygons._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.show = true; feature.pointSize = Cesium3DTilePointFeature.defaultPointSize; feature.color = Cesium3DTilePointFeature.defaultColor; feature.pointOutlineColor = Cesium3DTilePointFeature.defaultPointOutlineColor; feature.pointOutlineWidth = Cesium3DTilePointFeature.defaultPointOutlineWidth; feature.labelColor = Color.WHITE; feature.labelOutlineColor = Color.WHITE; feature.labelOutlineWidth = 1.0; feature.font = "30px sans-serif"; feature.labelStyle = LabelStyle$1.FILL; feature.labelText = undefined; feature.backgroundColor = new Color(0.165, 0.165, 0.165, 0.8); feature.backgroundPadding = new Cartesian2(7, 5); feature.backgroundEnabled = false; feature.scaleByDistance = undefined; feature.translucencyByDistance = undefined; feature.distanceDisplayCondition = undefined; feature.heightOffset = 0.0; feature.anchorLineEnabled = false; feature.anchorLineColor = Color.WHITE; feature.image = undefined; feature.disableDepthTestDistance = 0.0; feature.horizontalOrigin = HorizontalOrigin$1.CENTER; feature.verticalOrigin = VerticalOrigin$1.CENTER; feature.labelHorizontalOrigin = HorizontalOrigin$1.RIGHT; feature.labelVerticalOrigin = VerticalOrigin$1.BASELINE; } } var scratchColor$g = new Color(); var scratchColor2 = new Color(); var scratchColor3 = new Color(); var scratchColor4 = new Color(); var scratchColor5 = new Color(); var scratchColor6 = new Color(); var scratchScaleByDistance = new NearFarScalar(); var scratchTranslucencyByDistance = new NearFarScalar(); var scratchDistanceDisplayCondition = new DistanceDisplayCondition(); /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePoints.prototype.applyStyle = function (style, features) { if (!defined(style)) { clearStyle$1(this, features); return; } var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; if (defined(style.show)) { feature.show = style.show.evaluate(feature); } if (defined(style.pointSize)) { feature.pointSize = style.pointSize.evaluate(feature); } if (defined(style.color)) { feature.color = style.color.evaluateColor(feature, scratchColor$g); } if (defined(style.pointOutlineColor)) { feature.pointOutlineColor = style.pointOutlineColor.evaluateColor( feature, scratchColor2 ); } if (defined(style.pointOutlineWidth)) { feature.pointOutlineWidth = style.pointOutlineWidth.evaluate(feature); } if (defined(style.labelColor)) { feature.labelColor = style.labelColor.evaluateColor( feature, scratchColor3 ); } if (defined(style.labelOutlineColor)) { feature.labelOutlineColor = style.labelOutlineColor.evaluateColor( feature, scratchColor4 ); } if (defined(style.labelOutlineWidth)) { feature.labelOutlineWidth = style.labelOutlineWidth.evaluate(feature); } if (defined(style.font)) { feature.font = style.font.evaluate(feature); } if (defined(style.labelStyle)) { feature.labelStyle = style.labelStyle.evaluate(feature); } if (defined(style.labelText)) { feature.labelText = style.labelText.evaluate(feature); } else { feature.labelText = undefined; } if (defined(style.backgroundColor)) { feature.backgroundColor = style.backgroundColor.evaluateColor( feature, scratchColor5 ); } if (defined(style.backgroundPadding)) { feature.backgroundPadding = style.backgroundPadding.evaluate(feature); } if (defined(style.backgroundEnabled)) { feature.backgroundEnabled = style.backgroundEnabled.evaluate(feature); } if (defined(style.scaleByDistance)) { var scaleByDistanceCart4 = style.scaleByDistance.evaluate(feature); scratchScaleByDistance.near = scaleByDistanceCart4.x; scratchScaleByDistance.nearValue = scaleByDistanceCart4.y; scratchScaleByDistance.far = scaleByDistanceCart4.z; scratchScaleByDistance.farValue = scaleByDistanceCart4.w; feature.scaleByDistance = scratchScaleByDistance; } else { feature.scaleByDistance = undefined; } if (defined(style.translucencyByDistance)) { var translucencyByDistanceCart4 = style.translucencyByDistance.evaluate( feature ); scratchTranslucencyByDistance.near = translucencyByDistanceCart4.x; scratchTranslucencyByDistance.nearValue = translucencyByDistanceCart4.y; scratchTranslucencyByDistance.far = translucencyByDistanceCart4.z; scratchTranslucencyByDistance.farValue = translucencyByDistanceCart4.w; feature.translucencyByDistance = scratchTranslucencyByDistance; } else { feature.translucencyByDistance = undefined; } if (defined(style.distanceDisplayCondition)) { var distanceDisplayConditionCart2 = style.distanceDisplayCondition.evaluate( feature ); scratchDistanceDisplayCondition.near = distanceDisplayConditionCart2.x; scratchDistanceDisplayCondition.far = distanceDisplayConditionCart2.y; feature.distanceDisplayCondition = scratchDistanceDisplayCondition; } else { feature.distanceDisplayCondition = undefined; } if (defined(style.heightOffset)) { feature.heightOffset = style.heightOffset.evaluate(feature); } if (defined(style.anchorLineEnabled)) { feature.anchorLineEnabled = style.anchorLineEnabled.evaluate(feature); } if (defined(style.anchorLineColor)) { feature.anchorLineColor = style.anchorLineColor.evaluateColor( feature, scratchColor6 ); } if (defined(style.image)) { feature.image = style.image.evaluate(feature); } else { feature.image = undefined; } if (defined(style.disableDepthTestDistance)) { feature.disableDepthTestDistance = style.disableDepthTestDistance.evaluate( feature ); } if (defined(style.horizontalOrigin)) { feature.horizontalOrigin = style.horizontalOrigin.evaluate(feature); } if (defined(style.verticalOrigin)) { feature.verticalOrigin = style.verticalOrigin.evaluate(feature); } if (defined(style.labelHorizontalOrigin)) { feature.labelHorizontalOrigin = style.labelHorizontalOrigin.evaluate( feature ); } if (defined(style.labelVerticalOrigin)) { feature.labelVerticalOrigin = style.labelVerticalOrigin.evaluate(feature); } } }; /** * @private */ Vector3DTilePoints.prototype.update = function (frameState) { createPoints(this, frameState.mapProjection.ellipsoid); if (!this._ready) { return; } this._polylineCollection.update(frameState); this._billboardCollection.update(frameState); this._labelCollection.update(frameState); if (!this._resolvedPromise) { this._readyPromise.resolve(); this._resolvedPromise = true; } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTilePoints.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTilePoints.prototype.destroy = function () { this._billboardCollection = this._billboardCollection && this._billboardCollection.destroy(); this._labelCollection = this._labelCollection && this._labelCollection.destroy(); this._polylineCollection = this._polylineCollection && this._polylineCollection.destroy(); return destroyObject(this); }; /** * Creates a batch of pre-triangulated polygons draped on terrain and/or 3D Tiles. * * @alias Vector3DTilePolygons * @constructor * * @param {Object} options An object with following properties: * @param {Float32Array|Uint16Array} options.positions The positions of the polygons. The positions must be contiguous * so that the positions for polygon n are in [c, c + counts[n]] where c = sum{counts[0], counts[n - 1]} and they are the outer ring of * the polygon in counter-clockwise order. * @param {Uint32Array} options.counts The number of positions in the each polygon. * @param {Uint32Array} options.indices The indices of the triangulated polygons. The indices must be contiguous so that * the indices for polygon n are in [i, i + indexCounts[n]] where i = sum{indexCounts[0], indexCounts[n - 1]}. * @param {Uint32Array} options.indexCounts The number of indices for each polygon. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Float32Array} [options.polygonMinimumHeights] An array containing the minimum heights for each polygon. * @param {Float32Array} [options.polygonMaximumHeights] An array containing the maximum heights for each polygon. * @param {Rectangle} options.rectangle The rectangle containing the tile. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polygons. * @param {Uint16Array} options.batchIds The batch ids for each polygon. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of polygons. * * @private */ function Vector3DTilePolygons(options) { // All of the private properties will be released except _readyPromise // and _primitive after the Vector3DTilePrimitive is created. this._batchTable = options.batchTable; this._batchIds = options.batchIds; this._positions = options.positions; this._counts = options.counts; this._indices = options.indices; this._indexCounts = options.indexCounts; this._indexOffsets = undefined; this._batchTableColors = undefined; this._packedBuffer = undefined; this._batchedPositions = undefined; this._transferrableBatchIds = undefined; this._vertexBatchIds = undefined; this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._minimumHeight = options.minimumHeight; this._maximumHeight = options.maximumHeight; this._polygonMinimumHeights = options.polygonMinimumHeights; this._polygonMaximumHeights = options.polygonMaximumHeights; this._center = defaultValue(options.center, Cartesian3.ZERO); this._rectangle = options.rectangle; this._center = undefined; this._boundingVolume = options.boundingVolume; this._boundingVolumes = undefined; this._batchedIndices = undefined; this._ready = false; this._readyPromise = when.defer(); this._verticesPromise = undefined; this._primitive = undefined; /** * Draws the wireframe of the classification meshes. * @type {Boolean} * @default false */ this.debugWireframe = false; /** * Forces a re-batch instead of waiting after a number of frames have been rendered. For testing only. * @type {Boolean} * @default false */ this.forceRebatch = false; /** * What this tile will classify. * @type {ClassificationType} * @default ClassificationType.BOTH */ this.classificationType = ClassificationType$1.BOTH; } Object.defineProperties(Vector3DTilePolygons.prototype, { /** * Gets the number of triangles. * * @memberof Vector3DTilePolygons.prototype * * @type {Number} * @readonly */ trianglesLength: { get: function () { if (defined(this._primitive)) { return this._primitive.trianglesLength; } return 0; }, }, /** * Gets the geometry memory in bytes. * * @memberof Vector3DTilePolygons.prototype * * @type {Number} * @readonly */ geometryByteLength: { get: function () { if (defined(this._primitive)) { return this._primitive.geometryByteLength; } return 0; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTilePolygons.prototype * @type {Promise} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); function packBuffer$1(polygons) { var packedBuffer = new Float64Array( 3 + Cartesian3.packedLength + Ellipsoid.packedLength + Rectangle.packedLength ); var offset = 0; packedBuffer[offset++] = polygons._indices.BYTES_PER_ELEMENT; packedBuffer[offset++] = polygons._minimumHeight; packedBuffer[offset++] = polygons._maximumHeight; Cartesian3.pack(polygons._center, packedBuffer, offset); offset += Cartesian3.packedLength; Ellipsoid.pack(polygons._ellipsoid, packedBuffer, offset); offset += Ellipsoid.packedLength; Rectangle.pack(polygons._rectangle, packedBuffer, offset); return packedBuffer; } function unpackBuffer(polygons, packedBuffer) { var offset = 1; var numBVS = packedBuffer[offset++]; var bvs = (polygons._boundingVolumes = new Array(numBVS)); for (var i = 0; i < numBVS; ++i) { bvs[i] = OrientedBoundingBox.unpack(packedBuffer, offset); offset += OrientedBoundingBox.packedLength; } var numBatchedIndices = packedBuffer[offset++]; var bis = (polygons._batchedIndices = new Array(numBatchedIndices)); for (var j = 0; j < numBatchedIndices; ++j) { var color = Color.unpack(packedBuffer, offset); offset += Color.packedLength; var indexOffset = packedBuffer[offset++]; var count = packedBuffer[offset++]; var length = packedBuffer[offset++]; var batchIds = new Array(length); for (var k = 0; k < length; ++k) { batchIds[k] = packedBuffer[offset++]; } bis[j] = new Vector3DTileBatch({ color: color, offset: indexOffset, count: count, batchIds: batchIds, }); } } var createVerticesTaskProcessor$1 = new TaskProcessor( "createVectorTilePolygons", 5 ); var scratchColor$f = new Color(); function createPrimitive(polygons) { if (defined(polygons._primitive)) { return; } if (!defined(polygons._verticesPromise)) { var positions = polygons._positions; var counts = polygons._counts; var indexCounts = polygons._indexCounts; var indices = polygons._indices; var batchIds = polygons._transferrableBatchIds; var batchTableColors = polygons._batchTableColors; var packedBuffer = polygons._packedBuffer; if (!defined(batchTableColors)) { // Copy because they may be the views on the same buffer. positions = polygons._positions = arraySlice(polygons._positions); counts = polygons._counts = arraySlice(polygons._counts); indexCounts = polygons._indexCounts = arraySlice(polygons._indexCounts); indices = polygons._indices = arraySlice(polygons._indices); polygons._center = polygons._ellipsoid.cartographicToCartesian( Rectangle.center(polygons._rectangle) ); batchIds = polygons._transferrableBatchIds = new Uint32Array( polygons._batchIds ); batchTableColors = polygons._batchTableColors = new Uint32Array( batchIds.length ); var batchTable = polygons._batchTable; var length = batchTableColors.length; for (var i = 0; i < length; ++i) { var color = batchTable.getColor(i, scratchColor$f); batchTableColors[i] = color.toRgba(); } packedBuffer = polygons._packedBuffer = packBuffer$1(polygons); } var transferrableObjects = [ positions.buffer, counts.buffer, indexCounts.buffer, indices.buffer, batchIds.buffer, batchTableColors.buffer, packedBuffer.buffer, ]; var parameters = { packedBuffer: packedBuffer.buffer, positions: positions.buffer, counts: counts.buffer, indexCounts: indexCounts.buffer, indices: indices.buffer, batchIds: batchIds.buffer, batchTableColors: batchTableColors.buffer, }; var minimumHeights = polygons._polygonMinimumHeights; var maximumHeights = polygons._polygonMaximumHeights; if (defined(minimumHeights) && defined(maximumHeights)) { minimumHeights = arraySlice(minimumHeights); maximumHeights = arraySlice(maximumHeights); transferrableObjects.push(minimumHeights.buffer, maximumHeights.buffer); parameters.minimumHeights = minimumHeights; parameters.maximumHeights = maximumHeights; } var verticesPromise = (polygons._verticesPromise = createVerticesTaskProcessor$1.scheduleTask( parameters, transferrableObjects )); if (!defined(verticesPromise)) { // Postponed return; } when(verticesPromise, function (result) { polygons._positions = undefined; polygons._counts = undefined; polygons._polygonMinimumHeights = undefined; polygons._polygonMaximumHeights = undefined; var packedBuffer = new Float64Array(result.packedBuffer); var indexDatatype = packedBuffer[0]; unpackBuffer(polygons, packedBuffer); polygons._indices = IndexDatatype$1.getSizeInBytes(indexDatatype) === 2 ? new Uint16Array(result.indices) : new Uint32Array(result.indices); polygons._indexOffsets = new Uint32Array(result.indexOffsets); polygons._indexCounts = new Uint32Array(result.indexCounts); // will be released polygons._batchedPositions = new Float32Array(result.positions); polygons._vertexBatchIds = new Uint16Array(result.batchIds); polygons._ready = true; }); } if (polygons._ready && !defined(polygons._primitive)) { polygons._primitive = new Vector3DTilePrimitive({ batchTable: polygons._batchTable, positions: polygons._batchedPositions, batchIds: polygons._batchIds, vertexBatchIds: polygons._vertexBatchIds, indices: polygons._indices, indexOffsets: polygons._indexOffsets, indexCounts: polygons._indexCounts, batchedIndices: polygons._batchedIndices, boundingVolume: polygons._boundingVolume, boundingVolumes: polygons._boundingVolumes, center: polygons._center, }); polygons._batchTable = undefined; polygons._batchIds = undefined; polygons._positions = undefined; polygons._counts = undefined; polygons._indices = undefined; polygons._indexCounts = undefined; polygons._indexOffsets = undefined; polygons._batchTableColors = undefined; polygons._packedBuffer = undefined; polygons._batchedPositions = undefined; polygons._transferrableBatchIds = undefined; polygons._vertexBatchIds = undefined; polygons._ellipsoid = undefined; polygons._minimumHeight = undefined; polygons._maximumHeight = undefined; polygons._polygonMinimumHeights = undefined; polygons._polygonMaximumHeights = undefined; polygons._center = undefined; polygons._rectangle = undefined; polygons._boundingVolume = undefined; polygons._boundingVolumes = undefined; polygons._batchedIndices = undefined; polygons._verticesPromise = undefined; polygons._readyPromise.resolve(); } } /** * Creates features for each polygon and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed. */ Vector3DTilePolygons.prototype.createFeatures = function (content, features) { this._primitive.createFeatures(content, features); }; /** * Colors the entire tile when enabled is true. The resulting color will be (polygon batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePolygons.prototype.applyDebugSettings = function (enabled, color) { this._primitive.applyDebugSettings(enabled, color); }; /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePolygons.prototype.applyStyle = function (style, features) { this._primitive.applyStyle(style, features); }; /** * Call when updating the color of a polygon with batchId changes color. The polygons will need to be re-batched * on the next update. * * @param {Number} batchId The batch id of the polygon whose color has changed. * @param {Color} color The new polygon color. */ Vector3DTilePolygons.prototype.updateCommands = function (batchId, color) { this._primitive.updateCommands(batchId, color); }; /** * Updates the batches and queues the commands for rendering. * * @param {FrameState} frameState The current frame state. */ Vector3DTilePolygons.prototype.update = function (frameState) { createPrimitive(this); if (!this._ready) { return; } this._primitive.debugWireframe = this.debugWireframe; this._primitive.forceRebatch = this.forceRebatch; this._primitive.classificationType = this.classificationType; this._primitive.update(frameState); }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTilePolygons.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTilePolygons.prototype.destroy = function () { this._primitive = this._primitive && this._primitive.destroy(); return destroyObject(this); }; //This file is automatically rebuilt by the Cesium build process. var Vector3DTilePolylinesVS = "attribute vec4 currentPosition;\n\ attribute vec4 previousPosition;\n\ attribute vec4 nextPosition;\n\ attribute vec2 expandAndWidth;\n\ attribute float a_batchId;\n\ \n\ uniform mat4 u_modifiedModelView;\n\ \n\ void main()\n\ {\n\ float expandDir = expandAndWidth.x;\n\ float width = abs(expandAndWidth.y) + 0.5;\n\ bool usePrev = expandAndWidth.y < 0.0;\n\ \n\ vec4 p = u_modifiedModelView * currentPosition;\n\ vec4 prev = u_modifiedModelView * previousPosition;\n\ vec4 next = u_modifiedModelView * nextPosition;\n\ \n\ float angle;\n\ vec4 positionWC = getPolylineWindowCoordinatesEC(p, prev, next, expandDir, width, usePrev, angle);\n\ gl_Position = czm_viewportOrthographic * positionWC;\n\ }\n\ "; /** * Creates a batch of polylines that have been subdivided to be draped on terrain. * * @alias Vector3DTilePolylines * @constructor * * @param {Object} options An object with following properties: * @param {Uint16Array} options.positions The positions of the polylines * @param {Uint32Array} options.counts The number or positions in the each polyline. * @param {Uint16Array} options.widths The width of each polyline. * @param {Number} options.minimumHeight The minimum height of the terrain covered by the tile. * @param {Number} options.maximumHeight The maximum height of the terrain covered by the tile. * @param {Rectangle} options.rectangle The rectangle containing the tile. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The RTC center. * @param {Cesium3DTileBatchTable} options.batchTable The batch table for the tile containing the batched polylines. * @param {Uint16Array} options.batchIds The batch ids for each polyline. * @param {BoundingSphere} options.boundingVolume The bounding volume for the entire batch of polylines. * * @private */ function Vector3DTilePolylines(options) { // these arrays are all released after the first update. this._positions = options.positions; this._widths = options.widths; this._counts = options.counts; this._batchIds = options.batchIds; this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._minimumHeight = options.minimumHeight; this._maximumHeight = options.maximumHeight; this._center = options.center; this._rectangle = options.rectangle; this._boundingVolume = options.boundingVolume; this._batchTable = options.batchTable; this._va = undefined; this._sp = undefined; this._rs = undefined; this._uniformMap = undefined; this._command = undefined; this._transferrableBatchIds = undefined; this._packedBuffer = undefined; this._currentPositions = undefined; this._previousPositions = undefined; this._nextPositions = undefined; this._expandAndWidth = undefined; this._vertexBatchIds = undefined; this._indices = undefined; this._constantColor = Color.clone(Color.WHITE); this._highlightColor = this._constantColor; this._trianglesLength = 0; this._geometryByteLength = 0; this._ready = false; this._readyPromise = when.defer(); this._verticesPromise = undefined; } Object.defineProperties(Vector3DTilePolylines.prototype, { /** * Gets the number of triangles. * * @memberof Vector3DTilePolylines.prototype * * @type {Number} * @readonly */ trianglesLength: { get: function () { return this._trianglesLength; }, }, /** * Gets the geometry memory in bytes. * * @memberof Vector3DTilePolylines.prototype * * @type {Number} * @readonly */ geometryByteLength: { get: function () { return this._geometryByteLength; }, }, /** * Gets a promise that resolves when the primitive is ready to render. * @memberof Vector3DTilePolylines.prototype * @type {Promise} * @readonly */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, }); function packBuffer(polylines) { var rectangle = polylines._rectangle; var minimumHeight = polylines._minimumHeight; var maximumHeight = polylines._maximumHeight; var ellipsoid = polylines._ellipsoid; var center = polylines._center; var packedLength = 2 + Rectangle.packedLength + Ellipsoid.packedLength + Cartesian3.packedLength; var packedBuffer = new Float64Array(packedLength); var offset = 0; packedBuffer[offset++] = minimumHeight; packedBuffer[offset++] = maximumHeight; Rectangle.pack(rectangle, packedBuffer, offset); offset += Rectangle.packedLength; Ellipsoid.pack(ellipsoid, packedBuffer, offset); offset += Ellipsoid.packedLength; Cartesian3.pack(center, packedBuffer, offset); return packedBuffer; } var createVerticesTaskProcessor = new TaskProcessor( "createVectorTilePolylines", 5 ); var attributeLocations$2 = { previousPosition: 0, currentPosition: 1, nextPosition: 2, expandAndWidth: 3, a_batchId: 4, }; function createVertexArray(polylines, context) { if (defined(polylines._va)) { return; } if (!defined(polylines._verticesPromise)) { var positions = polylines._positions; var widths = polylines._widths; var counts = polylines._counts; var batchIds = polylines._transferrableBatchIds; var packedBuffer = polylines._packedBuffer; if (!defined(packedBuffer)) { // Copy because they may be the views on the same buffer. positions = polylines._positions = arraySlice(positions); widths = polylines._widths = arraySlice(widths); counts = polylines._counts = arraySlice(counts); batchIds = polylines._transferrableBatchIds = arraySlice( polylines._batchIds ); packedBuffer = polylines._packedBuffer = packBuffer(polylines); } var transferrableObjects = [ positions.buffer, widths.buffer, counts.buffer, batchIds.buffer, packedBuffer.buffer, ]; var parameters = { positions: positions.buffer, widths: widths.buffer, counts: counts.buffer, batchIds: batchIds.buffer, packedBuffer: packedBuffer.buffer, }; var verticesPromise = (polylines._verticesPromise = createVerticesTaskProcessor.scheduleTask( parameters, transferrableObjects )); if (!defined(verticesPromise)) { // Postponed return; } when(verticesPromise) .then(function (result) { polylines._currentPositions = new Float32Array(result.currentPositions); polylines._previousPositions = new Float32Array( result.previousPositions ); polylines._nextPositions = new Float32Array(result.nextPositions); polylines._expandAndWidth = new Float32Array(result.expandAndWidth); polylines._vertexBatchIds = new Uint16Array(result.batchIds); var indexDatatype = result.indexDatatype; polylines._indices = indexDatatype === IndexDatatype$1.UNSIGNED_SHORT ? new Uint16Array(result.indices) : new Uint32Array(result.indices); polylines._ready = true; }) .otherwise(function (error) { polylines._readyPromise.reject(error); }); } if (polylines._ready && !defined(polylines._va)) { var curPositions = polylines._currentPositions; var prevPositions = polylines._previousPositions; var nextPositions = polylines._nextPositions; var expandAndWidth = polylines._expandAndWidth; var vertexBatchIds = polylines._vertexBatchIds; var indices = polylines._indices; var byteLength = prevPositions.byteLength + curPositions.byteLength + nextPositions.byteLength; byteLength += expandAndWidth.byteLength + vertexBatchIds.byteLength + indices.byteLength; polylines._trianglesLength = indices.length / 3; polylines._geometryByteLength = byteLength; var prevPositionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: prevPositions, usage: BufferUsage$1.STATIC_DRAW, }); var curPositionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: curPositions, usage: BufferUsage$1.STATIC_DRAW, }); var nextPositionBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: nextPositions, usage: BufferUsage$1.STATIC_DRAW, }); var expandAndWidthBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: expandAndWidth, usage: BufferUsage$1.STATIC_DRAW, }); var idBuffer = Buffer$1.createVertexBuffer({ context: context, typedArray: vertexBatchIds, usage: BufferUsage$1.STATIC_DRAW, }); var indexBuffer = Buffer$1.createIndexBuffer({ context: context, typedArray: indices, usage: BufferUsage$1.STATIC_DRAW, indexDatatype: indices.BYTES_PER_ELEMENT === 2 ? IndexDatatype$1.UNSIGNED_SHORT : IndexDatatype$1.UNSIGNED_INT, }); var vertexAttributes = [ { index: attributeLocations$2.previousPosition, vertexBuffer: prevPositionBuffer, componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { index: attributeLocations$2.currentPosition, vertexBuffer: curPositionBuffer, componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { index: attributeLocations$2.nextPosition, vertexBuffer: nextPositionBuffer, componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 3, }, { index: attributeLocations$2.expandAndWidth, vertexBuffer: expandAndWidthBuffer, componentDatatype: ComponentDatatype$1.FLOAT, componentsPerAttribute: 2, }, { index: attributeLocations$2.a_batchId, vertexBuffer: idBuffer, componentDatatype: ComponentDatatype$1.UNSIGNED_SHORT, componentsPerAttribute: 1, }, ]; polylines._va = new VertexArray({ context: context, attributes: vertexAttributes, indexBuffer: indexBuffer, }); polylines._positions = undefined; polylines._widths = undefined; polylines._counts = undefined; polylines._ellipsoid = undefined; polylines._minimumHeight = undefined; polylines._maximumHeight = undefined; polylines._rectangle = undefined; polylines._transferrableBatchIds = undefined; polylines._packedBuffer = undefined; polylines._currentPositions = undefined; polylines._previousPositions = undefined; polylines._nextPositions = undefined; polylines._expandAndWidth = undefined; polylines._vertexBatchIds = undefined; polylines._indices = undefined; polylines._readyPromise.resolve(); } } var modifiedModelViewScratch$1 = new Matrix4(); var rtcScratch$1 = new Cartesian3(); function createUniformMap$2(primitive, context) { if (defined(primitive._uniformMap)) { return; } primitive._uniformMap = { u_modifiedModelView: function () { var viewMatrix = context.uniformState.view; Matrix4.clone(viewMatrix, modifiedModelViewScratch$1); Matrix4.multiplyByPoint( modifiedModelViewScratch$1, primitive._center, rtcScratch$1 ); Matrix4.setTranslation( modifiedModelViewScratch$1, rtcScratch$1, modifiedModelViewScratch$1 ); return modifiedModelViewScratch$1; }, u_highlightColor: function () { return primitive._highlightColor; }, }; } function createRenderStates$1(primitive) { if (defined(primitive._rs)) { return; } var polygonOffset = { enabled: true, factor: -5.0, units: -5.0, }; primitive._rs = RenderState.fromCache({ blending: BlendingState$1.ALPHA_BLEND, depthMask: false, depthTest: { enabled: true, }, polygonOffset: polygonOffset, }); } var PolylineFS = "uniform vec4 u_highlightColor; \n" + "void main()\n" + "{\n" + " gl_FragColor = u_highlightColor;\n" + "}\n"; function createShaders(primitive, context) { if (defined(primitive._sp)) { return; } var batchTable = primitive._batchTable; var vsSource = batchTable.getVertexShaderCallback( false, "a_batchId", undefined )(Vector3DTilePolylinesVS); var fsSource = batchTable.getFragmentShaderCallback()( PolylineFS, false, undefined ); var vs = new ShaderSource({ defines: [ "VECTOR_TILE", !FeatureDetection.isInternetExplorer() ? "CLIP_POLYLINE" : "", ], sources: [PolylineCommon, vsSource], }); var fs = new ShaderSource({ defines: ["VECTOR_TILE"], sources: [fsSource], }); primitive._sp = ShaderProgram.fromCache({ context: context, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$2, }); } function queueCommands(primitive, frameState) { if (!defined(primitive._command)) { var uniformMap = primitive._batchTable.getUniformMapCallback()( primitive._uniformMap ); primitive._command = new DrawCommand({ owner: primitive, vertexArray: primitive._va, renderState: primitive._rs, shaderProgram: primitive._sp, uniformMap: uniformMap, boundingVolume: primitive._boundingVolume, pass: Pass$1.TRANSLUCENT, pickId: primitive._batchTable.getPickId(), }); } frameState.commandList.push(primitive._command); } /** * Creates features for each polyline and places it at the batch id index of features. * * @param {Vector3DTileContent} content The vector tile content. * @param {Cesium3DTileFeature[]} features An array of features where the polygon features will be placed. */ Vector3DTilePolylines.prototype.createFeatures = function (content, features) { var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; features[batchId] = new Cesium3DTileFeature(content, batchId); } }; /** * Colors the entire tile when enabled is true. The resulting color will be (polyline batch table color * color). * * @param {Boolean} enabled Whether to enable debug coloring. * @param {Color} color The debug color. */ Vector3DTilePolylines.prototype.applyDebugSettings = function (enabled, color) { this._highlightColor = enabled ? color : this._constantColor; }; function clearStyle(polygons, features) { var batchIds = polygons._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.show = true; feature.color = Color.WHITE; } } var scratchColor$e = new Color(); var DEFAULT_COLOR_VALUE = Color.WHITE; var DEFAULT_SHOW_VALUE = true; /** * Apply a style to the content. * * @param {Cesium3DTileStyle} style The style. * @param {Cesium3DTileFeature[]} features The array of features. */ Vector3DTilePolylines.prototype.applyStyle = function (style, features) { if (!defined(style)) { clearStyle(this, features); return; } var batchIds = this._batchIds; var length = batchIds.length; for (var i = 0; i < length; ++i) { var batchId = batchIds[i]; var feature = features[batchId]; feature.color = defined(style.color) ? style.color.evaluateColor(feature, scratchColor$e) : DEFAULT_COLOR_VALUE; feature.show = defined(style.show) ? style.show.evaluate(feature) : DEFAULT_SHOW_VALUE; } }; /** * Updates the batches and queues the commands for rendering. * * @param {FrameState} frameState The current frame state. */ Vector3DTilePolylines.prototype.update = function (frameState) { var context = frameState.context; createVertexArray(this, context); createUniformMap$2(this, context); createShaders(this, context); createRenderStates$1(this); if (!this._ready) { return; } var passes = frameState.passes; if (passes.render || passes.pick) { queueCommands(this, frameState); } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. *

* * @returns {Boolean} true if this object was destroyed; otherwise, false. */ Vector3DTilePolylines.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. *

* * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ Vector3DTilePolylines.prototype.destroy = function () { this._va = this._va && this._va.destroy(); this._sp = this._sp && this._sp.destroy(); return destroyObject(this); }; /** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/vctr/TileFormats/VectorData|Vector} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Vector3DTileContent * @constructor * * @private */ function Vector3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) { this._tileset = tileset; this._tile = tile; this._resource = resource; this._polygons = undefined; this._polylines = undefined; this._points = undefined; this._contentReadyPromise = undefined; this._readyPromise = when.defer(); this._batchTable = undefined; this._features = undefined; /** * Part of the {@link Cesium3DTileContent} interface. */ this.featurePropertiesDirty = false; initialize$1(this, arrayBuffer, byteOffset); } Object.defineProperties(Vector3DTileContent.prototype, { featuresLength: { get: function () { return defined(this._batchTable) ? this._batchTable.featuresLength : 0; }, }, pointsLength: { get: function () { if (defined(this._points)) { return this._points.pointsLength; } return 0; }, }, trianglesLength: { get: function () { var trianglesLength = 0; if (defined(this._polygons)) { trianglesLength += this._polygons.trianglesLength; } if (defined(this._polylines)) { trianglesLength += this._polylines.trianglesLength; } return trianglesLength; }, }, geometryByteLength: { get: function () { var geometryByteLength = 0; if (defined(this._polygons)) { geometryByteLength += this._polygons.geometryByteLength; } if (defined(this._polylines)) { geometryByteLength += this._polylines.geometryByteLength; } return geometryByteLength; }, }, texturesByteLength: { get: function () { if (defined(this._points)) { return this._points.texturesByteLength; } return 0; }, }, batchTableByteLength: { get: function () { return defined(this._batchTable) ? this._batchTable.memorySizeInBytes : 0; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return this._readyPromise.promise; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return this._resource.getUrlComponent(true); }, }, batchTable: { get: function () { return this._batchTable; }, }, }); function createColorChangedCallback(content) { return function (batchId, color) { if (defined(content._polygons)) { content._polygons.updateCommands(batchId, color); } }; } function getBatchIds(featureTableJson, featureTableBinary) { var polygonBatchIds; var polylineBatchIds; var pointBatchIds; var i; var numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0); var numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0); var numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0); if (numberOfPolygons > 0 && defined(featureTableJson.POLYGON_BATCH_IDS)) { var polygonBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYGON_BATCH_IDS.byteOffset; polygonBatchIds = new Uint16Array( featureTableBinary.buffer, polygonBatchIdsByteOffset, numberOfPolygons ); } if (numberOfPolylines > 0 && defined(featureTableJson.POLYLINE_BATCH_IDS)) { var polylineBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POLYLINE_BATCH_IDS.byteOffset; polylineBatchIds = new Uint16Array( featureTableBinary.buffer, polylineBatchIdsByteOffset, numberOfPolylines ); } if (numberOfPoints > 0 && defined(featureTableJson.POINT_BATCH_IDS)) { var pointBatchIdsByteOffset = featureTableBinary.byteOffset + featureTableJson.POINT_BATCH_IDS.byteOffset; pointBatchIds = new Uint16Array( featureTableBinary.buffer, pointBatchIdsByteOffset, numberOfPoints ); } var atLeastOneDefined = defined(polygonBatchIds) || defined(polylineBatchIds) || defined(pointBatchIds); var atLeastOneUndefined = (numberOfPolygons > 0 && !defined(polygonBatchIds)) || (numberOfPolylines > 0 && !defined(polylineBatchIds)) || (numberOfPoints > 0 && !defined(pointBatchIds)); if (atLeastOneDefined && atLeastOneUndefined) { throw new RuntimeError( "If one group of batch ids is defined, then all batch ids must be defined." ); } var allUndefinedBatchIds = !defined(polygonBatchIds) && !defined(polylineBatchIds) && !defined(pointBatchIds); if (allUndefinedBatchIds) { var id = 0; if (!defined(polygonBatchIds) && numberOfPolygons > 0) { polygonBatchIds = new Uint16Array(numberOfPolygons); for (i = 0; i < numberOfPolygons; ++i) { polygonBatchIds[i] = id++; } } if (!defined(polylineBatchIds) && numberOfPolylines > 0) { polylineBatchIds = new Uint16Array(numberOfPolylines); for (i = 0; i < numberOfPolylines; ++i) { polylineBatchIds[i] = id++; } } if (!defined(pointBatchIds) && numberOfPoints > 0) { pointBatchIds = new Uint16Array(numberOfPoints); for (i = 0; i < numberOfPoints; ++i) { pointBatchIds[i] = id++; } } } return { polygons: polygonBatchIds, polylines: polylineBatchIds, points: pointBatchIds, }; } var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; function initialize$1(content, arrayBuffer, byteOffset) { byteOffset = defaultValue(byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32; // Skip magic number var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError( "Only Vector tile version 1 is supported. Version " + version + " is not." ); } byteOffset += sizeOfUint32; var byteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; if (byteLength === 0) { content._readyPromise.resolve(content); return; } var featureTableJSONByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; if (featureTableJSONByteLength === 0) { throw new RuntimeError( "Feature table must have a byte length greater than zero" ); } var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableJSONByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var indicesByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var positionByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var polylinePositionByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var pointsPositionByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var featureTableJson = getJsonFromTypedArray( uint8Array, byteOffset, featureTableJSONByteLength ); byteOffset += featureTableJSONByteLength; var featureTableBinary = new Uint8Array( arrayBuffer, byteOffset, featureTableBinaryByteLength ); byteOffset += featureTableBinaryByteLength; var batchTableJson; var batchTableBinary; if (batchTableJSONByteLength > 0) { // PERFORMANCE_IDEA: is it possible to allocate this on-demand? Perhaps keep the // arraybuffer/string compressed in memory and then decompress it when it is first accessed. // // We could also make another request for it, but that would make the property set/get // API async, and would double the number of numbers in some cases. batchTableJson = getJsonFromTypedArray( uint8Array, byteOffset, batchTableJSONByteLength ); byteOffset += batchTableJSONByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array( arrayBuffer, byteOffset, batchTableBinaryByteLength ); // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); byteOffset += batchTableBinaryByteLength; } } var numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0); var numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0); var numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0); var totalPrimitives = numberOfPolygons + numberOfPolylines + numberOfPoints; var batchTable = new Cesium3DTileBatchTable( content, totalPrimitives, batchTableJson, batchTableBinary, createColorChangedCallback(content) ); content._batchTable = batchTable; if (totalPrimitives === 0) { return; } var featureTable = new Cesium3DTileFeatureTable( featureTableJson, featureTableBinary ); var region = featureTable.getGlobalProperty("REGION"); if (!defined(region)) { throw new RuntimeError( "Feature table global property: REGION must be defined" ); } var rectangle = Rectangle.unpack(region); var minHeight = region[4]; var maxHeight = region[5]; var modelMatrix = content._tile.computedTransform; var center = featureTable.getGlobalProperty( "RTC_CENTER", ComponentDatatype$1.FLOAT, 3 ); if (defined(center)) { center = Cartesian3.unpack(center); Matrix4.multiplyByPoint(modelMatrix, center, center); } else { center = Rectangle.center(rectangle); center.height = CesiumMath.lerp(minHeight, maxHeight, 0.5); center = Ellipsoid.WGS84.cartographicToCartesian(center); } var batchIds = getBatchIds(featureTableJson, featureTableBinary); byteOffset += byteOffset % 4; if (numberOfPolygons > 0) { featureTable.featuresLength = numberOfPolygons; var polygonCounts = defaultValue( featureTable.getPropertyArray( "POLYGON_COUNTS", ComponentDatatype$1.UNSIGNED_INT, 1 ), featureTable.getPropertyArray( "POLYGON_COUNT", ComponentDatatype$1.UNSIGNED_INT, 1 ) // Workaround for old vector tilesets using the non-plural name ); if (!defined(polygonCounts)) { throw new RuntimeError( "Feature table property: POLYGON_COUNTS must be defined when POLYGONS_LENGTH is greater than 0" ); } var polygonIndexCounts = defaultValue( featureTable.getPropertyArray( "POLYGON_INDEX_COUNTS", ComponentDatatype$1.UNSIGNED_INT, 1 ), featureTable.getPropertyArray( "POLYGON_INDEX_COUNT", ComponentDatatype$1.UNSIGNED_INT, 1 ) // Workaround for old vector tilesets using the non-plural name ); if (!defined(polygonIndexCounts)) { throw new RuntimeError( "Feature table property: POLYGON_INDEX_COUNTS must be defined when POLYGONS_LENGTH is greater than 0" ); } // Use the counts array to determine how many position values we want. If we used the byte length then // zero padding values would be included and cause the delta zig-zag decoding to fail var numPolygonPositions = polygonCounts.reduce(function (total, count) { return total + count * 2; }, 0); var numPolygonIndices = polygonIndexCounts.reduce(function (total, count) { return total + count; }, 0); var indices = new Uint32Array(arrayBuffer, byteOffset, numPolygonIndices); byteOffset += indicesByteLength; var polygonPositions = new Uint16Array( arrayBuffer, byteOffset, numPolygonPositions ); byteOffset += positionByteLength; var polygonMinimumHeights; var polygonMaximumHeights; if ( defined(featureTableJson.POLYGON_MINIMUM_HEIGHTS) && defined(featureTableJson.POLYGON_MAXIMUM_HEIGHTS) ) { polygonMinimumHeights = featureTable.getPropertyArray( "POLYGON_MINIMUM_HEIGHTS", ComponentDatatype$1.FLOAT, 1 ); polygonMaximumHeights = featureTable.getPropertyArray( "POLYGON_MAXIMUM_HEIGHTS", ComponentDatatype$1.FLOAT, 1 ); } content._polygons = new Vector3DTilePolygons({ positions: polygonPositions, counts: polygonCounts, indexCounts: polygonIndexCounts, indices: indices, minimumHeight: minHeight, maximumHeight: maxHeight, polygonMinimumHeights: polygonMinimumHeights, polygonMaximumHeights: polygonMaximumHeights, center: center, rectangle: rectangle, boundingVolume: content.tile.boundingVolume.boundingVolume, batchTable: batchTable, batchIds: batchIds.polygons, modelMatrix: modelMatrix, }); } if (numberOfPolylines > 0) { featureTable.featuresLength = numberOfPolylines; var polylineCounts = defaultValue( featureTable.getPropertyArray( "POLYLINE_COUNTS", ComponentDatatype$1.UNSIGNED_INT, 1 ), featureTable.getPropertyArray( "POLYLINE_COUNT", ComponentDatatype$1.UNSIGNED_INT, 1 ) // Workaround for old vector tilesets using the non-plural name ); if (!defined(polylineCounts)) { throw new RuntimeError( "Feature table property: POLYLINE_COUNTS must be defined when POLYLINES_LENGTH is greater than 0" ); } var widths = featureTable.getPropertyArray( "POLYLINE_WIDTHS", ComponentDatatype$1.UNSIGNED_SHORT, 1 ); if (!defined(widths)) { widths = new Uint16Array(numberOfPolylines); for (var i = 0; i < numberOfPolylines; ++i) { widths[i] = 2.0; } } // Use the counts array to determine how many position values we want. If we used the byte length then // zero padding values would be included and cause the delta zig-zag decoding to fail var numPolylinePositions = polylineCounts.reduce(function (total, count) { return total + count * 3; }, 0); var polylinePositions = new Uint16Array( arrayBuffer, byteOffset, numPolylinePositions ); byteOffset += polylinePositionByteLength; content._polylines = new Vector3DTilePolylines({ positions: polylinePositions, widths: widths, counts: polylineCounts, batchIds: batchIds.polylines, minimumHeight: minHeight, maximumHeight: maxHeight, center: center, rectangle: rectangle, boundingVolume: content.tile.boundingVolume.boundingVolume, batchTable: batchTable, }); } if (numberOfPoints > 0) { var pointPositions = new Uint16Array( arrayBuffer, byteOffset, numberOfPoints * 3 ); byteOffset += pointsPositionByteLength; content._points = new Vector3DTilePoints({ positions: pointPositions, batchIds: batchIds.points, minimumHeight: minHeight, maximumHeight: maxHeight, rectangle: rectangle, batchTable: batchTable, }); } } function createFeatures(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && featuresLength > 0) { var features = new Array(featuresLength); if (defined(content._polygons)) { content._polygons.createFeatures(content, features); } if (defined(content._polylines)) { content._polylines.createFeatures(content, features); } if (defined(content._points)) { content._points.createFeatures(content, features); } content._features = features; } } Vector3DTileContent.prototype.hasProperty = function (batchId, name) { return this._batchTable.hasProperty(batchId, name); }; Vector3DTileContent.prototype.getFeature = function (batchId) { //>>includeStart('debug', pragmas.debug); var featuresLength = this.featuresLength; if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) { throw new DeveloperError( "batchId is required and between zero and featuresLength - 1 (" + (featuresLength - 1) + ")." ); } //>>includeEnd('debug'); createFeatures(this); return this._features[batchId]; }; Vector3DTileContent.prototype.applyDebugSettings = function (enabled, color) { if (defined(this._polygons)) { this._polygons.applyDebugSettings(enabled, color); } if (defined(this._polylines)) { this._polylines.applyDebugSettings(enabled, color); } if (defined(this._points)) { this._points.applyDebugSettings(enabled, color); } }; Vector3DTileContent.prototype.applyStyle = function (style) { createFeatures(this); if (defined(this._polygons)) { this._polygons.applyStyle(style, this._features); } if (defined(this._polylines)) { this._polylines.applyStyle(style, this._features); } if (defined(this._points)) { this._points.applyStyle(style, this._features); } }; Vector3DTileContent.prototype.update = function (tileset, frameState) { var ready = true; if (defined(this._polygons)) { this._polygons.classificationType = this._tileset.classificationType; this._polygons.debugWireframe = this._tileset.debugWireframe; this._polygons.update(frameState); ready = ready && this._polygons._ready; } if (defined(this._polylines)) { this._polylines.update(frameState); ready = ready && this._polylines._ready; } if (defined(this._points)) { this._points.update(frameState); ready = ready && this._points._ready; } if (defined(this._batchTable) && ready) { this._batchTable.update(tileset, frameState); } if (!defined(this._contentReadyPromise)) { var pointsPromise = defined(this._points) ? this._points.readyPromise : undefined; var polygonPromise = defined(this._polygons) ? this._polygons.readyPromise : undefined; var polylinePromise = defined(this._polylines) ? this._polylines.readyPromise : undefined; var that = this; this._contentReadyPromise = when .all([pointsPromise, polygonPromise, polylinePromise]) .then(function () { that._readyPromise.resolve(that); }); } }; Vector3DTileContent.prototype.isDestroyed = function () { return false; }; Vector3DTileContent.prototype.destroy = function () { this._polygons = this._polygons && this._polygons.destroy(); this._polylines = this._polylines && this._polylines.destroy(); this._points = this._points && this._points.destroy(); this._batchTable = this._batchTable && this._batchTable.destroy(); return destroyObject(this); }; /** * Maps a tile's magic field in its header to a new content object for the tile's payload. * * @private */ var Cesium3DTileContentFactory = { b3dm: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new Batched3DModel3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, pnts: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new PointCloud3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, i3dm: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new Instanced3DModel3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, cmpt: function (tileset, tile, resource, arrayBuffer, byteOffset) { // Send in the factory in order to avoid a cyclical dependency return new Composite3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset, Cesium3DTileContentFactory ); }, json: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new Tileset3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, geom: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new Geometry3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, vctr: function (tileset, tile, resource, arrayBuffer, byteOffset) { return new Vector3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ); }, }; /** * @private */ var Cesium3DTileContentState = { UNLOADED: 0, // Has never been requested LOADING: 1, // Is waiting on a pending request PROCESSING: 2, // Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data. READY: 3, // Ready to render. EXPIRED: 4, // Is expired and will be unloaded once new content is loaded. FAILED: 5, // Request failed. }; var Cesium3DTileContentState$1 = Object.freeze(Cesium3DTileContentState); /** * Hint defining optimization support for a 3D tile * * @enum {Number} * * @private */ var Cesium3DTileOptimizationHint = { NOT_COMPUTED: -1, USE_OPTIMIZATION: 1, SKIP_OPTIMIZATION: 0, }; var Cesium3DTileOptimizationHint$1 = Object.freeze(Cesium3DTileOptimizationHint); /** * Traversal that loads all leaves that intersect the camera frustum. * Used to determine ray-tileset intersections during a pickFromRayMostDetailed call. * * @private */ function Cesium3DTilesetMostDetailedTraversal() {} var traversal$1 = { stack: new ManagedArray(), stackMaximumLength: 0, }; Cesium3DTilesetMostDetailedTraversal.selectTiles = function ( tileset, frameState ) { tileset._selectedTiles.length = 0; tileset._requestedTiles.length = 0; tileset._hasMixedContent = false; var ready = true; var root = tileset.root; root.updateVisibility(frameState); if (!isVisible$1(root)) { return ready; } var stack = traversal$1.stack; stack.push(tileset.root); while (stack.length > 0) { traversal$1.stackMaximumLength = Math.max( traversal$1.stackMaximumLength, stack.length ); var tile = stack.pop(); var add = tile.refine === Cesium3DTileRefine$1.ADD; var replace = tile.refine === Cesium3DTileRefine$1.REPLACE; var traverse = canTraverse$1(tileset, tile); if (traverse) { updateAndPushChildren$1(tileset, tile, stack, frameState); } if (add || (replace && !traverse)) { loadTile$1(tileset, tile); touchTile$1(tileset, tile, frameState); selectDesiredTile$1(tileset, tile, frameState); if (!hasEmptyContent$1(tile) && !tile.contentAvailable) { ready = false; } } visitTile$3(tileset); } traversal$1.stack.trim(traversal$1.stackMaximumLength); return ready; }; function isVisible$1(tile) { return tile._visible && tile._inRequestVolume; } function hasEmptyContent$1(tile) { return tile.hasEmptyContent || tile.hasTilesetContent; } function hasUnloadedContent$1(tile) { return !hasEmptyContent$1(tile) && tile.contentUnloaded; } function canTraverse$1(tileset, tile) { if (tile.children.length === 0) { return false; } if (tile.hasTilesetContent) { // Traverse external tileset to visit its root tile // Don't traverse if the subtree is expired because it will be destroyed return !tile.contentExpired; } if (tile.hasEmptyContent) { return true; } return true; // Keep traversing until a leave is hit } function updateAndPushChildren$1(tileset, tile, stack, frameState) { var children = tile.children; var length = children.length; for (var i = 0; i < length; ++i) { var child = children[i]; child.updateVisibility(frameState); if (isVisible$1(child)) { stack.push(child); } } } function loadTile$1(tileset, tile) { if (hasUnloadedContent$1(tile) || tile.contentExpired) { tile._priority = 0.0; // Highest priority tileset._requestedTiles.push(tile); } } function touchTile$1(tileset, tile, frameState) { if (tile._touchedFrame === frameState.frameNumber) { // Prevents another pass from touching the frame again return; } tileset._cache.touch(tile); tile._touchedFrame = frameState.frameNumber; } function visitTile$3(tileset) { ++tileset.statistics.visited; } function selectDesiredTile$1(tileset, tile, frameState) { if ( tile.contentAvailable && tile.contentVisibility(frameState) !== Intersect$1.OUTSIDE ) { tileset._selectedTiles.push(tile); } } /** * @private */ function Cesium3DTilesetTraversal() {} function isVisible(tile) { return tile._visible && tile._inRequestVolume; } var traversal = { stack: new ManagedArray(), stackMaximumLength: 0, }; var emptyTraversal = { stack: new ManagedArray(), stackMaximumLength: 0, }; var descendantTraversal = { stack: new ManagedArray(), stackMaximumLength: 0, }; var selectionTraversal = { stack: new ManagedArray(), stackMaximumLength: 0, ancestorStack: new ManagedArray(), ancestorStackMaximumLength: 0, }; var descendantSelectionDepth = 2; Cesium3DTilesetTraversal.selectTiles = function (tileset, frameState) { tileset._requestedTiles.length = 0; if (tileset.debugFreezeFrame) { return; } tileset._selectedTiles.length = 0; tileset._selectedTilesToStyle.length = 0; tileset._emptyTiles.length = 0; tileset._hasMixedContent = false; var root = tileset.root; updateTile(tileset, root, frameState); // The root tile is not visible if (!isVisible(root)) { return; } // The tileset doesn't meet the SSE requirement, therefore the tree does not need to be rendered if ( root.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError ) { return; } if (!skipLevelOfDetail(tileset)) { executeBaseTraversal(tileset, root, frameState); } else if (tileset.immediatelyLoadDesiredLevelOfDetail) { executeSkipTraversal(tileset, root, frameState); } else { executeBaseAndSkipTraversal(tileset, root, frameState); } traversal.stack.trim(traversal.stackMaximumLength); emptyTraversal.stack.trim(emptyTraversal.stackMaximumLength); descendantTraversal.stack.trim(descendantTraversal.stackMaximumLength); selectionTraversal.stack.trim(selectionTraversal.stackMaximumLength); selectionTraversal.ancestorStack.trim( selectionTraversal.ancestorStackMaximumLength ); // Update the priority for any requests found during traversal // Update after traversal so that min and max values can be used to normalize priority values var requestedTiles = tileset._requestedTiles; var length = requestedTiles.length; for (var i = 0; i < length; ++i) { requestedTiles[i].updatePriority(); } }; function executeBaseTraversal(tileset, root, frameState) { var baseScreenSpaceError = tileset._maximumScreenSpaceError; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; executeTraversal( tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState ); } function executeSkipTraversal(tileset, root, frameState) { var baseScreenSpaceError = Number.MAX_VALUE; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; executeTraversal( tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState ); traverseAndSelect(tileset, root, frameState); } function executeBaseAndSkipTraversal(tileset, root, frameState) { var baseScreenSpaceError = Math.max( tileset.baseScreenSpaceError, tileset.maximumScreenSpaceError ); var maximumScreenSpaceError = tileset.maximumScreenSpaceError; executeTraversal( tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState ); traverseAndSelect(tileset, root, frameState); } function skipLevelOfDetail(tileset) { return tileset._skipLevelOfDetail; } function addEmptyTile(tileset, tile) { tileset._emptyTiles.push(tile); } function selectTile(tileset, tile, frameState) { if (tile.contentVisibility(frameState) !== Intersect$1.OUTSIDE) { var tileContent = tile.content; if (tileContent.featurePropertiesDirty) { // A feature's property in this tile changed, the tile needs to be re-styled. tileContent.featurePropertiesDirty = false; tile.lastStyleTime = 0; // Force applying the style to this tile tileset._selectedTilesToStyle.push(tile); } else if (tile._selectedFrame < frameState.frameNumber - 1) { // Tile is newly selected; it is selected this frame, but was not selected last frame. tileset._selectedTilesToStyle.push(tile); } tile._selectedFrame = frameState.frameNumber; tileset._selectedTiles.push(tile); } } function selectDescendants(tileset, root, frameState) { var stack = descendantTraversal.stack; stack.push(root); while (stack.length > 0) { descendantTraversal.stackMaximumLength = Math.max( descendantTraversal.stackMaximumLength, stack.length ); var tile = stack.pop(); var children = tile.children; var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { var child = children[i]; if (isVisible(child)) { if (child.contentAvailable) { updateTile(tileset, child, frameState); touchTile(tileset, child, frameState); selectTile(tileset, child, frameState); } else if (child._depth - root._depth < descendantSelectionDepth) { // Continue traversing, but not too far stack.push(child); } } } } } function selectDesiredTile(tileset, tile, frameState) { if (!skipLevelOfDetail(tileset)) { if (tile.contentAvailable) { // The tile can be selected right away and does not require traverseAndSelect selectTile(tileset, tile, frameState); } return; } // If this tile is not loaded attempt to select its ancestor instead var loadedTile = tile.contentAvailable ? tile : tile._ancestorWithContentAvailable; if (defined(loadedTile)) { // Tiles will actually be selected in traverseAndSelect loadedTile._shouldSelect = true; } else { // If no ancestors are ready traverse down and select tiles to minimize empty regions. // This happens often for immediatelyLoadDesiredLevelOfDetail where parent tiles are not necessarily loaded before zooming out. selectDescendants(tileset, tile, frameState); } } function visitTile$2(tileset, tile, frameState) { ++tileset._statistics.visited; tile._visitedFrame = frameState.frameNumber; } function touchTile(tileset, tile, frameState) { if (tile._touchedFrame === frameState.frameNumber) { // Prevents another pass from touching the frame again return; } tileset._cache.touch(tile); tile._touchedFrame = frameState.frameNumber; } function updateMinimumMaximumPriority(tileset, tile) { tileset._maximumPriority.distance = Math.max( tile._priorityHolder._distanceToCamera, tileset._maximumPriority.distance ); tileset._minimumPriority.distance = Math.min( tile._priorityHolder._distanceToCamera, tileset._minimumPriority.distance ); tileset._maximumPriority.depth = Math.max( tile._depth, tileset._maximumPriority.depth ); tileset._minimumPriority.depth = Math.min( tile._depth, tileset._minimumPriority.depth ); tileset._maximumPriority.foveatedFactor = Math.max( tile._priorityHolder._foveatedFactor, tileset._maximumPriority.foveatedFactor ); tileset._minimumPriority.foveatedFactor = Math.min( tile._priorityHolder._foveatedFactor, tileset._minimumPriority.foveatedFactor ); tileset._maximumPriority.reverseScreenSpaceError = Math.max( tile._priorityReverseScreenSpaceError, tileset._maximumPriority.reverseScreenSpaceError ); tileset._minimumPriority.reverseScreenSpaceError = Math.min( tile._priorityReverseScreenSpaceError, tileset._minimumPriority.reverseScreenSpaceError ); } function isOnScreenLongEnough(tileset, tile, frameState) { // Prevent unnecessary loads while camera is moving by getting the ratio of travel distance to tile size. if (!tileset._cullRequestsWhileMoving) { return true; } var sphere = tile.boundingSphere; var diameter = Math.max(sphere.radius * 2.0, 1.0); var camera = frameState.camera; var deltaMagnitude = camera.positionWCDeltaMagnitude !== 0.0 ? camera.positionWCDeltaMagnitude : camera.positionWCDeltaMagnitudeLastFrame; var movementRatio = (tileset.cullRequestsWhileMovingMultiplier * deltaMagnitude) / diameter; // How do n frames of this movement compare to the tile's physical size. return movementRatio < 1.0; } function loadTile(tileset, tile, frameState) { if ( tile._requestedFrame === frameState.frameNumber || (!hasUnloadedContent(tile) && !tile.contentExpired) ) { return; } if (!isOnScreenLongEnough(tileset, tile, frameState)) { return; } var cameraHasNotStoppedMovingLongEnough = frameState.camera.timeSinceMoved < tileset.foveatedTimeDelay; if (tile.priorityDeferred && cameraHasNotStoppedMovingLongEnough) { return; } tile._requestedFrame = frameState.frameNumber; tileset._requestedTiles.push(tile); } function updateVisibility(tileset, tile, frameState) { if (tile._updatedVisibilityFrame === tileset._updatedVisibilityFrame) { // Return early if visibility has already been checked during the traversal. // The visibility may have already been checked if the cullWithChildrenBounds optimization is used. return; } tile.updateVisibility(frameState); tile._updatedVisibilityFrame = tileset._updatedVisibilityFrame; } function anyChildrenVisible(tileset, tile, frameState) { var anyVisible = false; var children = tile.children; var length = children.length; for (var i = 0; i < length; ++i) { var child = children[i]; updateVisibility(tileset, child, frameState); anyVisible = anyVisible || isVisible(child); } return anyVisible; } function meetsScreenSpaceErrorEarly(tileset, tile, frameState) { var parent = tile.parent; if ( !defined(parent) || parent.hasTilesetContent || parent.refine !== Cesium3DTileRefine$1.ADD ) { return false; } // Use parent's geometric error with child's box to see if the tile already meet the SSE return ( tile.getScreenSpaceError(frameState, true) <= tileset._maximumScreenSpaceError ); } function updateTileVisibility(tileset, tile, frameState) { updateVisibility(tileset, tile, frameState); if (!isVisible(tile)) { return; } var hasChildren = tile.children.length > 0; if (tile.hasTilesetContent && hasChildren) { // Use the root tile's visibility instead of this tile's visibility. // The root tile may be culled by the children bounds optimization in which // case this tile should also be culled. var child = tile.children[0]; updateTileVisibility(tileset, child, frameState); tile._visible = child._visible; return; } if (meetsScreenSpaceErrorEarly(tileset, tile, frameState)) { tile._visible = false; return; } // Optimization - if none of the tile's children are visible then this tile isn't visible var replace = tile.refine === Cesium3DTileRefine$1.REPLACE; var useOptimization = tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint$1.USE_OPTIMIZATION; if (replace && useOptimization && hasChildren) { if (!anyChildrenVisible(tileset, tile, frameState)) { ++tileset._statistics.numberOfTilesCulledWithChildrenUnion; tile._visible = false; return; } } } function updateTile(tileset, tile, frameState) { // Reset some of the tile's flags and re-evaluate visibility updateTileVisibility(tileset, tile, frameState); tile.updateExpiration(); // Request priority tile._wasMinPriorityChild = false; tile._priorityHolder = tile; updateMinimumMaximumPriority(tileset, tile); // SkipLOD tile._shouldSelect = false; tile._finalResolution = true; } function updateTileAncestorContentLinks(tile, frameState) { tile._ancestorWithContent = undefined; tile._ancestorWithContentAvailable = undefined; var parent = tile.parent; if (defined(parent)) { // ancestorWithContent is an ancestor that has content or has the potential to have // content. Used in conjunction with tileset.skipLevels to know when to skip a tile. // ancestorWithContentAvailable is an ancestor that is rendered if a desired tile is not loaded. var hasContent = !hasUnloadedContent(parent) || parent._requestedFrame === frameState.frameNumber; tile._ancestorWithContent = hasContent ? parent : parent._ancestorWithContent; tile._ancestorWithContentAvailable = parent.contentAvailable ? parent : parent._ancestorWithContentAvailable; // Links a descendant up to its contentAvailable ancestor as the traversal progresses. } } function hasEmptyContent(tile) { return tile.hasEmptyContent || tile.hasTilesetContent; } function hasUnloadedContent(tile) { return !hasEmptyContent(tile) && tile.contentUnloaded; } function reachedSkippingThreshold(tileset, tile) { var ancestor = tile._ancestorWithContent; return ( !tileset.immediatelyLoadDesiredLevelOfDetail && (tile._priorityProgressiveResolutionScreenSpaceErrorLeaf || (defined(ancestor) && tile._screenSpaceError < ancestor._screenSpaceError / tileset.skipScreenSpaceErrorFactor && tile._depth > ancestor._depth + tileset.skipLevels)) ); } function sortChildrenByDistanceToCamera(a, b) { // Sort by farthest child first since this is going on a stack if (b._distanceToCamera === 0 && a._distanceToCamera === 0) { return b._centerZDepth - a._centerZDepth; } return b._distanceToCamera - a._distanceToCamera; } function updateAndPushChildren(tileset, tile, stack, frameState) { var i; var replace = tile.refine === Cesium3DTileRefine$1.REPLACE; var children = tile.children; var length = children.length; for (i = 0; i < length; ++i) { updateTile(tileset, children[i], frameState); } // Sort by distance to take advantage of early Z and reduce artifacts for skipLevelOfDetail children.sort(sortChildrenByDistanceToCamera); // For traditional replacement refinement only refine if all children are loaded. // Empty tiles are exempt since it looks better if children stream in as they are loaded to fill the empty space. var checkRefines = !skipLevelOfDetail(tileset) && replace && !hasEmptyContent(tile); var refines = true; var anyChildrenVisible = false; // Determining min child var minIndex = -1; var minimumPriority = Number.MAX_VALUE; var child; for (i = 0; i < length; ++i) { child = children[i]; if (isVisible(child)) { stack.push(child); if (child._foveatedFactor < minimumPriority) { minIndex = i; minimumPriority = child._foveatedFactor; } anyChildrenVisible = true; } else if (checkRefines || tileset.loadSiblings) { // Keep non-visible children loaded since they are still needed before the parent can refine. // Or loadSiblings is true so always load tiles regardless of visibility. if (child._foveatedFactor < minimumPriority) { minIndex = i; minimumPriority = child._foveatedFactor; } loadTile(tileset, child, frameState); touchTile(tileset, child, frameState); } if (checkRefines) { var childRefines; if (!child._inRequestVolume) { childRefines = false; } else if (hasEmptyContent(child)) { childRefines = executeEmptyTraversal(tileset, child, frameState); } else { childRefines = child.contentAvailable; } refines = refines && childRefines; } } if (!anyChildrenVisible) { refines = false; } if (minIndex !== -1 && !skipLevelOfDetail(tileset) && replace) { // An ancestor will hold the _foveatedFactor and _distanceToCamera for descendants between itself and its highest priority descendant. Siblings of a min children along the way use this ancestor as their priority holder as well. // Priority of all tiles that refer to the _foveatedFactor and _distanceToCamera stored in the common ancestor will be differentiated based on their _depth. var minPriorityChild = children[minIndex]; minPriorityChild._wasMinPriorityChild = true; var priorityHolder = (tile._wasMinPriorityChild || tile === tileset.root) && minimumPriority <= tile._priorityHolder._foveatedFactor ? tile._priorityHolder : tile; // This is where priority dependency chains are wired up or started anew. priorityHolder._foveatedFactor = Math.min( minPriorityChild._foveatedFactor, priorityHolder._foveatedFactor ); priorityHolder._distanceToCamera = Math.min( minPriorityChild._distanceToCamera, priorityHolder._distanceToCamera ); for (i = 0; i < length; ++i) { child = children[i]; child._priorityHolder = priorityHolder; } } return refines; } function inBaseTraversal(tileset, tile, baseScreenSpaceError) { if (!skipLevelOfDetail(tileset)) { return true; } if (tileset.immediatelyLoadDesiredLevelOfDetail) { return false; } if (!defined(tile._ancestorWithContent)) { // Include root or near-root tiles in the base traversal so there is something to select up to return true; } if (tile._screenSpaceError === 0.0) { // If a leaf, use parent's SSE return tile.parent._screenSpaceError > baseScreenSpaceError; } return tile._screenSpaceError > baseScreenSpaceError; } function canTraverse(tileset, tile) { if (tile.children.length === 0) { return false; } if (tile.hasTilesetContent) { // Traverse external tileset to visit its root tile // Don't traverse if the subtree is expired because it will be destroyed return !tile.contentExpired; } return tile._screenSpaceError > tileset._maximumScreenSpaceError; } function executeTraversal( tileset, root, baseScreenSpaceError, maximumScreenSpaceError, frameState ) { // Depth-first traversal that traverses all visible tiles and marks tiles for selection. // If skipLevelOfDetail is off then a tile does not refine until all children are loaded. // This is the traditional replacement refinement approach and is called the base traversal. // Tiles that have a greater screen space error than the base screen space error are part of the base traversal, // all other tiles are part of the skip traversal. The skip traversal allows for skipping levels of the tree // and rendering children and parent tiles simultaneously. var stack = traversal.stack; stack.push(root); while (stack.length > 0) { traversal.stackMaximumLength = Math.max( traversal.stackMaximumLength, stack.length ); var tile = stack.pop(); updateTileAncestorContentLinks(tile, frameState); var baseTraversal = inBaseTraversal(tileset, tile, baseScreenSpaceError); var add = tile.refine === Cesium3DTileRefine$1.ADD; var replace = tile.refine === Cesium3DTileRefine$1.REPLACE; var parent = tile.parent; var parentRefines = !defined(parent) || parent._refines; var refines = false; if (canTraverse(tileset, tile)) { refines = updateAndPushChildren(tileset, tile, stack, frameState) && parentRefines; } var stoppedRefining = !refines && parentRefines; if (hasEmptyContent(tile)) { // Add empty tile just to show its debug bounding volume // If the tile has tileset content load the external tileset // If the tile cannot refine further select its nearest loaded ancestor addEmptyTile(tileset, tile); loadTile(tileset, tile, frameState); if (stoppedRefining) { selectDesiredTile(tileset, tile, frameState); } } else if (add) { // Additive tiles are always loaded and selected selectDesiredTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); } else if (replace) { if (baseTraversal) { // Always load tiles in the base traversal // Select tiles that can't refine further loadTile(tileset, tile, frameState); if (stoppedRefining) { selectDesiredTile(tileset, tile, frameState); } } else if (stoppedRefining) { // In skip traversal, load and select tiles that can't refine further selectDesiredTile(tileset, tile, frameState); loadTile(tileset, tile, frameState); } else if (reachedSkippingThreshold(tileset, tile)) { // In skip traversal, load tiles that aren't skipped. In practice roughly half the tiles stay unloaded. loadTile(tileset, tile, frameState); } } visitTile$2(tileset, tile, frameState); touchTile(tileset, tile, frameState); tile._refines = refines; } } function executeEmptyTraversal(tileset, root, frameState) { // Depth-first traversal that checks if all nearest descendants with content are loaded. Ignores visibility. var allDescendantsLoaded = true; var stack = emptyTraversal.stack; stack.push(root); while (stack.length > 0) { emptyTraversal.stackMaximumLength = Math.max( emptyTraversal.stackMaximumLength, stack.length ); var tile = stack.pop(); var children = tile.children; var childrenLength = children.length; // Only traverse if the tile is empty - traversal stop at descendants with content var emptyContent = hasEmptyContent(tile); var traverse = emptyContent && canTraverse(tileset, tile); var emptyLeaf = emptyContent && tile.children.length === 0; // Traversal stops but the tile does not have content yet // There will be holes if the parent tries to refine to its children, so don't refine // One exception: a parent may refine even if one of its descendants is an empty leaf if (!traverse && !tile.contentAvailable && !emptyLeaf) { allDescendantsLoaded = false; } updateTile(tileset, tile, frameState); if (!isVisible(tile)) { // Load tiles that aren't visible since they are still needed for the parent to refine loadTile(tileset, tile, frameState); touchTile(tileset, tile, frameState); } if (traverse) { for (var i = 0; i < childrenLength; ++i) { var child = children[i]; stack.push(child); } } } return allDescendantsLoaded; } /** * Traverse the tree and check if their selected frame is the current frame. If so, add it to a selection queue. * This is a preorder traversal so children tiles are selected before ancestor tiles. * * The reason for the preorder traversal is so that tiles can easily be marked with their * selection depth. A tile's _selectionDepth is its depth in the tree where all non-selected tiles are removed. * This property is important for use in the stencil test because we want to render deeper tiles on top of their * ancestors. If a tileset is very deep, the depth is unlikely to fit into the stencil buffer. * * We want to select children before their ancestors because there is no guarantee on the relationship between * the children's z-depth and the ancestor's z-depth. We cannot rely on Z because we want the child to appear on top * of ancestor regardless of true depth. The stencil tests used require children to be drawn first. * * NOTE: 3D Tiles uses 3 bits from the stencil buffer meaning this will not work when there is a chain of * selected tiles that is deeper than 7. This is not very likely. * @private */ function traverseAndSelect(tileset, root, frameState) { var stack = selectionTraversal.stack; var ancestorStack = selectionTraversal.ancestorStack; var lastAncestor; stack.push(root); while (stack.length > 0 || ancestorStack.length > 0) { selectionTraversal.stackMaximumLength = Math.max( selectionTraversal.stackMaximumLength, stack.length ); selectionTraversal.ancestorStackMaximumLength = Math.max( selectionTraversal.ancestorStackMaximumLength, ancestorStack.length ); if (ancestorStack.length > 0) { var waitingTile = ancestorStack.peek(); if (waitingTile._stackLength === stack.length) { ancestorStack.pop(); if (waitingTile !== lastAncestor) { waitingTile._finalResolution = false; } selectTile(tileset, waitingTile, frameState); continue; } } var tile = stack.pop(); if (!defined(tile)) { // stack is empty but ancestorStack isn't continue; } var add = tile.refine === Cesium3DTileRefine$1.ADD; var shouldSelect = tile._shouldSelect; var children = tile.children; var childrenLength = children.length; var traverse = canTraverse(tileset, tile); if (shouldSelect) { if (add) { selectTile(tileset, tile, frameState); } else { tile._selectionDepth = ancestorStack.length; if (tile._selectionDepth > 0) { tileset._hasMixedContent = true; } lastAncestor = tile; if (!traverse) { selectTile(tileset, tile, frameState); continue; } ancestorStack.push(tile); tile._stackLength = stack.length; } } if (traverse) { for (var i = 0; i < childrenLength; ++i) { var child = children[i]; if (isVisible(child)) { stack.push(child); } } } } } /** * The pass in which a 3D Tileset is updated. * * @private */ var Cesium3DTilePass = { RENDER: 0, PICK: 1, SHADOW: 2, PRELOAD: 3, PRELOAD_FLIGHT: 4, REQUEST_RENDER_MODE_DEFER_CHECK: 5, MOST_DETAILED_PRELOAD: 6, MOST_DETAILED_PICK: 7, NUMBER_OF_PASSES: 8, }; var passOptions = new Array(Cesium3DTilePass.NUMBER_OF_PASSES); passOptions[Cesium3DTilePass.RENDER] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: true, requestTiles: true, ignoreCommands: false, }); passOptions[Cesium3DTilePass.PICK] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: false, requestTiles: false, ignoreCommands: false, }); passOptions[Cesium3DTilePass.SHADOW] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: false, requestTiles: true, ignoreCommands: false, }); passOptions[Cesium3DTilePass.PRELOAD] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: false, requestTiles: true, ignoreCommands: true, }); passOptions[Cesium3DTilePass.PRELOAD_FLIGHT] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: false, requestTiles: true, ignoreCommands: true, }); passOptions[Cesium3DTilePass.REQUEST_RENDER_MODE_DEFER_CHECK] = Object.freeze({ traversal: Cesium3DTilesetTraversal, isRender: false, requestTiles: true, ignoreCommands: true, }); passOptions[Cesium3DTilePass.MOST_DETAILED_PRELOAD] = Object.freeze({ traversal: Cesium3DTilesetMostDetailedTraversal, isRender: false, requestTiles: true, ignoreCommands: true, }); passOptions[Cesium3DTilePass.MOST_DETAILED_PICK] = Object.freeze({ traversal: Cesium3DTilesetMostDetailedTraversal, isRender: false, requestTiles: false, ignoreCommands: false, }); Cesium3DTilePass.getPassOptions = function (pass) { return passOptions[pass]; }; var Cesium3DTilePass$1 = Object.freeze(Cesium3DTilePass); /** * Represents empty content for tiles in a * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles} tileset that * do not have content, e.g., because they are used to optimize hierarchical culling. *

* Implements the {@link Cesium3DTileContent} interface. *

* * @alias Empty3DTileContent * @constructor * * @private */ function Empty3DTileContent(tileset, tile) { this._tileset = tileset; this._tile = tile; this.featurePropertiesDirty = false; } Object.defineProperties(Empty3DTileContent.prototype, { featuresLength: { get: function () { return 0; }, }, pointsLength: { get: function () { return 0; }, }, trianglesLength: { get: function () { return 0; }, }, geometryByteLength: { get: function () { return 0; }, }, texturesByteLength: { get: function () { return 0; }, }, batchTableByteLength: { get: function () { return 0; }, }, innerContents: { get: function () { return undefined; }, }, readyPromise: { get: function () { return undefined; }, }, tileset: { get: function () { return this._tileset; }, }, tile: { get: function () { return this._tile; }, }, url: { get: function () { return undefined; }, }, batchTable: { get: function () { return undefined; }, }, }); /** * Part of the {@link Cesium3DTileContent} interface. Empty3DTileContent * always returns false since a tile of this type does not have any features. */ Empty3DTileContent.prototype.hasProperty = function (batchId, name) { return false; }; /** * Part of the {@link Cesium3DTileContent} interface. Empty3DTileContent * always returns undefined since a tile of this type does not have any features. */ Empty3DTileContent.prototype.getFeature = function (batchId) { return undefined; }; Empty3DTileContent.prototype.applyDebugSettings = function (enabled, color) {}; Empty3DTileContent.prototype.applyStyle = function (style) {}; Empty3DTileContent.prototype.update = function (tileset, frameState) {}; Empty3DTileContent.prototype.isDestroyed = function () { return false; }; Empty3DTileContent.prototype.destroy = function () { return destroyObject(this); }; /** * A tile bounding volume specified as a longitude/latitude/height region. * @alias TileBoundingRegion * @constructor * * @param {Object} options Object with the following properties: * @param {Rectangle} options.rectangle The rectangle specifying the longitude and latitude range of the region. * @param {Number} [options.minimumHeight=0.0] The minimum height of the region. * @param {Number} [options.maximumHeight=0.0] The maximum height of the region. * @param {Ellipsoid} [options.ellipsoid=Cesium.Ellipsoid.WGS84] The ellipsoid. * @param {Boolean} [options.computeBoundingVolumes=true] True to compute the {@link TileBoundingRegion#boundingVolume} and * {@link TileBoundingVolume#boundingSphere}. If false, these properties will be undefined. * * @private */ function TileBoundingRegion(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.rectangle", options.rectangle); //>>includeEnd('debug'); this.rectangle = Rectangle.clone(options.rectangle); this.minimumHeight = defaultValue(options.minimumHeight, 0.0); this.maximumHeight = defaultValue(options.maximumHeight, 0.0); /** * The world coordinates of the southwest corner of the tile's rectangle. * * @type {Cartesian3} * @default Cartesian3() */ this.southwestCornerCartesian = new Cartesian3(); /** * The world coordinates of the northeast corner of the tile's rectangle. * * @type {Cartesian3} * @default Cartesian3() */ this.northeastCornerCartesian = new Cartesian3(); /** * A normal that, along with southwestCornerCartesian, defines a plane at the western edge of * the tile. Any position above (in the direction of the normal) this plane is outside the tile. * * @type {Cartesian3} * @default Cartesian3() */ this.westNormal = new Cartesian3(); /** * A normal that, along with southwestCornerCartesian, defines a plane at the southern edge of * the tile. Any position above (in the direction of the normal) this plane is outside the tile. * Because points of constant latitude do not necessary lie in a plane, positions below this * plane are not necessarily inside the tile, but they are close. * * @type {Cartesian3} * @default Cartesian3() */ this.southNormal = new Cartesian3(); /** * A normal that, along with northeastCornerCartesian, defines a plane at the eastern edge of * the tile. Any position above (in the direction of the normal) this plane is outside the tile. * * @type {Cartesian3} * @default Cartesian3() */ this.eastNormal = new Cartesian3(); /** * A normal that, along with northeastCornerCartesian, defines a plane at the eastern edge of * the tile. Any position above (in the direction of the normal) this plane is outside the tile. * Because points of constant latitude do not necessary lie in a plane, positions below this * plane are not necessarily inside the tile, but they are close. * * @type {Cartesian3} * @default Cartesian3() */ this.northNormal = new Cartesian3(); var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); computeBox(this, options.rectangle, ellipsoid); if (defaultValue(options.computeBoundingVolumes, true)) { // An oriented bounding box that encloses this tile's region. This is used to calculate tile visibility. this._orientedBoundingBox = OrientedBoundingBox.fromRectangle( this.rectangle, this.minimumHeight, this.maximumHeight, ellipsoid ); this._boundingSphere = BoundingSphere.fromOrientedBoundingBox( this._orientedBoundingBox ); } } Object.defineProperties(TileBoundingRegion.prototype, { /** * The underlying bounding volume * * @memberof TileBoundingRegion.prototype * * @type {Object} * @readonly */ boundingVolume: { get: function () { return this._orientedBoundingBox; }, }, /** * The underlying bounding sphere * * @memberof TileBoundingRegion.prototype * * @type {BoundingSphere} * @readonly */ boundingSphere: { get: function () { return this._boundingSphere; }, }, }); var cartesian3Scratch$2 = new Cartesian3(); var cartesian3Scratch2 = new Cartesian3(); var cartesian3Scratch3 = new Cartesian3(); var eastWestNormalScratch = new Cartesian3(); var westernMidpointScratch = new Cartesian3(); var easternMidpointScratch = new Cartesian3(); var cartographicScratch$2 = new Cartographic(); var planeScratch = new Plane(Cartesian3.UNIT_X, 0.0); var rayScratch$1 = new Ray(); function computeBox(tileBB, rectangle, ellipsoid) { ellipsoid.cartographicToCartesian( Rectangle.southwest(rectangle), tileBB.southwestCornerCartesian ); ellipsoid.cartographicToCartesian( Rectangle.northeast(rectangle), tileBB.northeastCornerCartesian ); // The middle latitude on the western edge. cartographicScratch$2.longitude = rectangle.west; cartographicScratch$2.latitude = (rectangle.south + rectangle.north) * 0.5; cartographicScratch$2.height = 0.0; var westernMidpointCartesian = ellipsoid.cartographicToCartesian( cartographicScratch$2, westernMidpointScratch ); // Compute the normal of the plane on the western edge of the tile. var westNormal = Cartesian3.cross( westernMidpointCartesian, Cartesian3.UNIT_Z, cartesian3Scratch$2 ); Cartesian3.normalize(westNormal, tileBB.westNormal); // The middle latitude on the eastern edge. cartographicScratch$2.longitude = rectangle.east; var easternMidpointCartesian = ellipsoid.cartographicToCartesian( cartographicScratch$2, easternMidpointScratch ); // Compute the normal of the plane on the eastern edge of the tile. var eastNormal = Cartesian3.cross( Cartesian3.UNIT_Z, easternMidpointCartesian, cartesian3Scratch$2 ); Cartesian3.normalize(eastNormal, tileBB.eastNormal); // Compute the normal of the plane bounding the southern edge of the tile. var westVector = Cartesian3.subtract( westernMidpointCartesian, easternMidpointCartesian, cartesian3Scratch$2 ); var eastWestNormal = Cartesian3.normalize(westVector, eastWestNormalScratch); var south = rectangle.south; var southSurfaceNormal; if (south > 0.0) { // Compute a plane that doesn't cut through the tile. cartographicScratch$2.longitude = (rectangle.west + rectangle.east) * 0.5; cartographicScratch$2.latitude = south; var southCenterCartesian = ellipsoid.cartographicToCartesian( cartographicScratch$2, rayScratch$1.origin ); Cartesian3.clone(eastWestNormal, rayScratch$1.direction); var westPlane = Plane.fromPointNormal( tileBB.southwestCornerCartesian, tileBB.westNormal, planeScratch ); // Find a point that is on the west and the south planes IntersectionTests.rayPlane( rayScratch$1, westPlane, tileBB.southwestCornerCartesian ); southSurfaceNormal = ellipsoid.geodeticSurfaceNormal( southCenterCartesian, cartesian3Scratch2 ); } else { southSurfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic( Rectangle.southeast(rectangle), cartesian3Scratch2 ); } var southNormal = Cartesian3.cross( southSurfaceNormal, westVector, cartesian3Scratch3 ); Cartesian3.normalize(southNormal, tileBB.southNormal); // Compute the normal of the plane bounding the northern edge of the tile. var north = rectangle.north; var northSurfaceNormal; if (north < 0.0) { // Compute a plane that doesn't cut through the tile. cartographicScratch$2.longitude = (rectangle.west + rectangle.east) * 0.5; cartographicScratch$2.latitude = north; var northCenterCartesian = ellipsoid.cartographicToCartesian( cartographicScratch$2, rayScratch$1.origin ); Cartesian3.negate(eastWestNormal, rayScratch$1.direction); var eastPlane = Plane.fromPointNormal( tileBB.northeastCornerCartesian, tileBB.eastNormal, planeScratch ); // Find a point that is on the east and the north planes IntersectionTests.rayPlane( rayScratch$1, eastPlane, tileBB.northeastCornerCartesian ); northSurfaceNormal = ellipsoid.geodeticSurfaceNormal( northCenterCartesian, cartesian3Scratch2 ); } else { northSurfaceNormal = ellipsoid.geodeticSurfaceNormalCartographic( Rectangle.northwest(rectangle), cartesian3Scratch2 ); } var northNormal = Cartesian3.cross( westVector, northSurfaceNormal, cartesian3Scratch3 ); Cartesian3.normalize(northNormal, tileBB.northNormal); } var southwestCornerScratch = new Cartesian3(); var northeastCornerScratch = new Cartesian3(); var negativeUnitY = new Cartesian3(0.0, -1.0, 0.0); var negativeUnitZ = new Cartesian3(0.0, 0.0, -1.0); var vectorScratch$1 = new Cartesian3(); /** * Gets the distance from the camera to the closest point on the tile. This is used for level of detail selection. * * @param {FrameState} frameState The state information of the current rendering frame. * @returns {Number} The distance from the camera to the closest point on the tile, in meters. */ TileBoundingRegion.prototype.distanceToCamera = function (frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); var camera = frameState.camera; var cameraCartesianPosition = camera.positionWC; var cameraCartographicPosition = camera.positionCartographic; var result = 0.0; if (!Rectangle.contains(this.rectangle, cameraCartographicPosition)) { var southwestCornerCartesian = this.southwestCornerCartesian; var northeastCornerCartesian = this.northeastCornerCartesian; var westNormal = this.westNormal; var southNormal = this.southNormal; var eastNormal = this.eastNormal; var northNormal = this.northNormal; if (frameState.mode !== SceneMode$1.SCENE3D) { southwestCornerCartesian = frameState.mapProjection.project( Rectangle.southwest(this.rectangle), southwestCornerScratch ); southwestCornerCartesian.z = southwestCornerCartesian.y; southwestCornerCartesian.y = southwestCornerCartesian.x; southwestCornerCartesian.x = 0.0; northeastCornerCartesian = frameState.mapProjection.project( Rectangle.northeast(this.rectangle), northeastCornerScratch ); northeastCornerCartesian.z = northeastCornerCartesian.y; northeastCornerCartesian.y = northeastCornerCartesian.x; northeastCornerCartesian.x = 0.0; westNormal = negativeUnitY; eastNormal = Cartesian3.UNIT_Y; southNormal = negativeUnitZ; northNormal = Cartesian3.UNIT_Z; } var vectorFromSouthwestCorner = Cartesian3.subtract( cameraCartesianPosition, southwestCornerCartesian, vectorScratch$1 ); var distanceToWestPlane = Cartesian3.dot( vectorFromSouthwestCorner, westNormal ); var distanceToSouthPlane = Cartesian3.dot( vectorFromSouthwestCorner, southNormal ); var vectorFromNortheastCorner = Cartesian3.subtract( cameraCartesianPosition, northeastCornerCartesian, vectorScratch$1 ); var distanceToEastPlane = Cartesian3.dot( vectorFromNortheastCorner, eastNormal ); var distanceToNorthPlane = Cartesian3.dot( vectorFromNortheastCorner, northNormal ); if (distanceToWestPlane > 0.0) { result += distanceToWestPlane * distanceToWestPlane; } else if (distanceToEastPlane > 0.0) { result += distanceToEastPlane * distanceToEastPlane; } if (distanceToSouthPlane > 0.0) { result += distanceToSouthPlane * distanceToSouthPlane; } else if (distanceToNorthPlane > 0.0) { result += distanceToNorthPlane * distanceToNorthPlane; } } var cameraHeight; var minimumHeight; var maximumHeight; if (frameState.mode === SceneMode$1.SCENE3D) { cameraHeight = cameraCartographicPosition.height; minimumHeight = this.minimumHeight; maximumHeight = this.maximumHeight; } else { cameraHeight = cameraCartesianPosition.x; minimumHeight = 0.0; maximumHeight = 0.0; } if (cameraHeight > maximumHeight) { var distanceAboveTop = cameraHeight - maximumHeight; result += distanceAboveTop * distanceAboveTop; } else if (cameraHeight < minimumHeight) { var distanceBelowBottom = minimumHeight - cameraHeight; result += distanceBelowBottom * distanceBelowBottom; } return Math.sqrt(result); }; /** * Determines which side of a plane this box is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ TileBoundingRegion.prototype.intersectPlane = function (plane) { //>>includeStart('debug', pragmas.debug); Check.defined("plane", plane); //>>includeEnd('debug'); return this._orientedBoundingBox.intersectPlane(plane); }; /** * Creates a debug primitive that shows the outline of the tile bounding region. * * @param {Color} color The desired color of the primitive's mesh * @return {Primitive} * * @private */ TileBoundingRegion.prototype.createDebugVolume = function (color) { //>>includeStart('debug', pragmas.debug); Check.defined("color", color); //>>includeEnd('debug'); var modelMatrix = new Matrix4.clone(Matrix4.IDENTITY); var geometry = new RectangleOutlineGeometry({ rectangle: this.rectangle, height: this.minimumHeight, extrudedHeight: this.maximumHeight, }); var instance = new GeometryInstance({ geometry: geometry, id: "outline", modelMatrix: modelMatrix, attributes: { color: ColorGeometryInstanceAttribute.fromColor(color), }, }); return new Primitive({ geometryInstances: instance, appearance: new PerInstanceColorAppearance({ translucent: false, flat: true, }), asynchronous: false, }); }; /** * A tile bounding volume specified as a sphere. * @alias TileBoundingSphere * @constructor * * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the bounding sphere. * @param {Number} [radius=0.0] The radius of the bounding sphere. * * @private */ function TileBoundingSphere(center, radius) { if (radius === 0) { radius = CesiumMath.EPSILON7; } this._boundingSphere = new BoundingSphere(center, radius); } Object.defineProperties(TileBoundingSphere.prototype, { /** * The center of the bounding sphere * * @memberof TileBoundingSphere.prototype * * @type {Cartesian3} * @readonly */ center: { get: function () { return this._boundingSphere.center; }, }, /** * The radius of the bounding sphere * * @memberof TileBoundingSphere.prototype * * @type {Number} * @readonly */ radius: { get: function () { return this._boundingSphere.radius; }, }, /** * The underlying bounding volume * * @memberof TileBoundingSphere.prototype * * @type {Object} * @readonly */ boundingVolume: { get: function () { return this._boundingSphere; }, }, /** * The underlying bounding sphere * * @memberof TileBoundingSphere.prototype * * @type {BoundingSphere} * @readonly */ boundingSphere: { get: function () { return this._boundingSphere; }, }, }); /** * Computes the distance between this bounding sphere and the camera attached to frameState. * * @param {FrameState} frameState The frameState to which the camera is attached. * @returns {Number} The distance between the camera and the bounding sphere in meters. Returns 0 if the camera is inside the bounding volume. * */ TileBoundingSphere.prototype.distanceToCamera = function (frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); var boundingSphere = this._boundingSphere; return Math.max( 0.0, Cartesian3.distance(boundingSphere.center, frameState.camera.positionWC) - boundingSphere.radius ); }; /** * Determines which side of a plane this sphere is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire sphere is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire sphere is * on the opposite side, and {@link Intersect.INTERSECTING} if the sphere * intersects the plane. */ TileBoundingSphere.prototype.intersectPlane = function (plane) { //>>includeStart('debug', pragmas.debug); Check.defined("plane", plane); //>>includeEnd('debug'); return BoundingSphere.intersectPlane(this._boundingSphere, plane); }; /** * Update the bounding sphere after the tile is transformed. * * @param {Cartesian3} center The center of the bounding sphere. * @param {Number} radius The radius of the bounding sphere. */ TileBoundingSphere.prototype.update = function (center, radius) { Cartesian3.clone(center, this._boundingSphere.center); this._boundingSphere.radius = radius; }; /** * Creates a debug primitive that shows the outline of the sphere. * * @param {Color} color The desired color of the primitive's mesh * @return {Primitive} */ TileBoundingSphere.prototype.createDebugVolume = function (color) { //>>includeStart('debug', pragmas.debug); Check.defined("color", color); //>>includeEnd('debug'); var geometry = new SphereOutlineGeometry({ radius: this.radius, }); var modelMatrix = Matrix4.fromTranslation( this.center, new Matrix4.clone(Matrix4.IDENTITY) ); var instance = new GeometryInstance({ geometry: geometry, id: "outline", modelMatrix: modelMatrix, attributes: { color: ColorGeometryInstanceAttribute.fromColor(color), }, }); return new Primitive({ geometryInstances: instance, appearance: new PerInstanceColorAppearance({ translucent: false, flat: true, }), asynchronous: false, }); }; var scratchU = new Cartesian3(); var scratchV = new Cartesian3(); var scratchW = new Cartesian3(); var scratchCartesian$6 = new Cartesian3(); function computeMissingVector(a, b, result) { result = Cartesian3.cross(a, b, result); var magnitude = Cartesian3.magnitude(result); return Cartesian3.multiplyByScalar( result, CesiumMath.EPSILON7 / magnitude, result ); } function findOrthogonalVector(a, result) { var temp = Cartesian3.normalize(a, scratchCartesian$6); var b = Cartesian3.equalsEpsilon(temp, Cartesian3.UNIT_X, CesiumMath.EPSILON6) ? Cartesian3.UNIT_Y : Cartesian3.UNIT_X; return computeMissingVector(a, b, result); } function checkHalfAxes(halfAxes) { var u = Matrix3.getColumn(halfAxes, 0, scratchU); var v = Matrix3.getColumn(halfAxes, 1, scratchV); var w = Matrix3.getColumn(halfAxes, 2, scratchW); var uZero = Cartesian3.equals(u, Cartesian3.ZERO); var vZero = Cartesian3.equals(v, Cartesian3.ZERO); var wZero = Cartesian3.equals(w, Cartesian3.ZERO); if (!uZero && !vZero && !wZero) { return halfAxes; } if (uZero && vZero && wZero) { halfAxes[0] = CesiumMath.EPSILON7; halfAxes[4] = CesiumMath.EPSILON7; halfAxes[8] = CesiumMath.EPSILON7; return halfAxes; } if (uZero && !vZero && !wZero) { u = computeMissingVector(v, w, u); } else if (!uZero && vZero && !wZero) { v = computeMissingVector(u, w, v); } else if (!uZero && !vZero && wZero) { w = computeMissingVector(v, u, w); } else if (!uZero) { v = findOrthogonalVector(u, v); w = computeMissingVector(v, u, w); } else if (!vZero) { u = findOrthogonalVector(v, u); w = computeMissingVector(v, u, w); } else if (!wZero) { u = findOrthogonalVector(w, u); v = computeMissingVector(w, u, v); } Matrix3.setColumn(halfAxes, 0, u, halfAxes); Matrix3.setColumn(halfAxes, 1, v, halfAxes); Matrix3.setColumn(halfAxes, 2, w, halfAxes); return halfAxes; } /** * A tile bounding volume specified as an oriented bounding box. * @alias TileOrientedBoundingBox * @constructor * * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box. * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 * cube centered at the origin. * * @private */ function TileOrientedBoundingBox(center, halfAxes) { halfAxes = checkHalfAxes(halfAxes); this._orientedBoundingBox = new OrientedBoundingBox(center, halfAxes); this._boundingSphere = BoundingSphere.fromOrientedBoundingBox( this._orientedBoundingBox ); } Object.defineProperties(TileOrientedBoundingBox.prototype, { /** * The underlying bounding volume. * * @memberof TileOrientedBoundingBox.prototype * * @type {Object} * @readonly */ boundingVolume: { get: function () { return this._orientedBoundingBox; }, }, /** * The underlying bounding sphere. * * @memberof TileOrientedBoundingBox.prototype * * @type {BoundingSphere} * @readonly */ boundingSphere: { get: function () { return this._boundingSphere; }, }, }); /** * Computes the distance between this bounding box and the camera attached to frameState. * * @param {FrameState} frameState The frameState to which the camera is attached. * @returns {Number} The distance between the camera and the bounding box in meters. Returns 0 if the camera is inside the bounding volume. */ TileOrientedBoundingBox.prototype.distanceToCamera = function (frameState) { //>>includeStart('debug', pragmas.debug); Check.defined("frameState", frameState); //>>includeEnd('debug'); return Math.sqrt( this._orientedBoundingBox.distanceSquaredTo(frameState.camera.positionWC) ); }; /** * Determines which side of a plane this box is located. * * @param {Plane} plane The plane to test against. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is * on the opposite side, and {@link Intersect.INTERSECTING} if the box * intersects the plane. */ TileOrientedBoundingBox.prototype.intersectPlane = function (plane) { //>>includeStart('debug', pragmas.debug); Check.defined("plane", plane); //>>includeEnd('debug'); return this._orientedBoundingBox.intersectPlane(plane); }; /** * Update the bounding box after the tile is transformed. * * @param {Cartesian3} center The center of the box. * @param {Matrix3} halfAxes The three orthogonal half-axes of the bounding box. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2 * cube centered at the origin. */ TileOrientedBoundingBox.prototype.update = function (center, halfAxes) { Cartesian3.clone(center, this._orientedBoundingBox.center); halfAxes = checkHalfAxes(halfAxes); Matrix3.clone(halfAxes, this._orientedBoundingBox.halfAxes); BoundingSphere.fromOrientedBoundingBox( this._orientedBoundingBox, this._boundingSphere ); }; /** * Creates a debug primitive that shows the outline of the box. * * @param {Color} color The desired color of the primitive's mesh * @return {Primitive} */ TileOrientedBoundingBox.prototype.createDebugVolume = function (color) { //>>includeStart('debug', pragmas.debug); Check.defined("color", color); //>>includeEnd('debug'); var geometry = new BoxOutlineGeometry({ // Make a 2x2x2 cube minimum: new Cartesian3(-1.0, -1.0, -1.0), maximum: new Cartesian3(1.0, 1.0, 1.0), }); var modelMatrix = Matrix4.fromRotationTranslation( this.boundingVolume.halfAxes, this.boundingVolume.center ); var instance = new GeometryInstance({ geometry: geometry, id: "outline", modelMatrix: modelMatrix, attributes: { color: ColorGeometryInstanceAttribute.fromColor(color), }, }); return new Primitive({ geometryInstances: instance, appearance: new PerInstanceColorAppearance({ translucent: false, flat: true, }), asynchronous: false, }); }; /** * A tile in a {@link Cesium3DTileset}. When a tile is first created, its content is not loaded; * the content is loaded on-demand when needed based on the view. *

* Do not construct this directly, instead access tiles through {@link Cesium3DTileset#tileVisible}. *

* * @alias Cesium3DTile * @constructor */ function Cesium3DTile(tileset, baseResource, header, parent) { this._tileset = tileset; this._header = header; var contentHeader = header.content; /** * The local transform of this tile. * @type {Matrix4} */ this.transform = defined(header.transform) ? Matrix4.unpack(header.transform) : Matrix4.clone(Matrix4.IDENTITY); var parentTransform = defined(parent) ? parent.computedTransform : tileset.modelMatrix; var computedTransform = Matrix4.multiply( parentTransform, this.transform, new Matrix4() ); var parentInitialTransform = defined(parent) ? parent._initialTransform : Matrix4.IDENTITY; this._initialTransform = Matrix4.multiply( parentInitialTransform, this.transform, new Matrix4() ); /** * The final computed transform of this tile. * @type {Matrix4} * @readonly */ this.computedTransform = computedTransform; this._boundingVolume = this.createBoundingVolume( header.boundingVolume, computedTransform ); this._boundingVolume2D = undefined; var contentBoundingVolume; if (defined(contentHeader) && defined(contentHeader.boundingVolume)) { // Non-leaf tiles may have a content bounding-volume, which is a tight-fit bounding volume // around only the features in the tile. This box is useful for culling for rendering, // but not for culling for traversing the tree since it does not guarantee spatial coherence, i.e., // since it only bounds features in the tile, not the entire tile, children may be // outside of this box. contentBoundingVolume = this.createBoundingVolume( contentHeader.boundingVolume, computedTransform ); } this._contentBoundingVolume = contentBoundingVolume; this._contentBoundingVolume2D = undefined; var viewerRequestVolume; if (defined(header.viewerRequestVolume)) { viewerRequestVolume = this.createBoundingVolume( header.viewerRequestVolume, computedTransform ); } this._viewerRequestVolume = viewerRequestVolume; /** * The error, in meters, introduced if this tile is rendered and its children are not. * This is used to compute screen space error, i.e., the error measured in pixels. * * @type {Number} * @readonly */ this.geometricError = header.geometricError; this._geometricError = header.geometricError; if (!defined(this._geometricError)) { this._geometricError = defined(parent) ? parent.geometricError : tileset._geometricError; Cesium3DTile._deprecationWarning( "geometricErrorUndefined", "Required property geometricError is undefined for this tile. Using parent's geometric error instead." ); } this.updateGeometricErrorScale(); var refine; if (defined(header.refine)) { if (header.refine === "replace" || header.refine === "add") { Cesium3DTile._deprecationWarning( "lowercase-refine", 'This tile uses a lowercase refine "' + header.refine + '". Instead use "' + header.refine.toUpperCase() + '".' ); } refine = header.refine.toUpperCase() === "REPLACE" ? Cesium3DTileRefine$1.REPLACE : Cesium3DTileRefine$1.ADD; } else if (defined(parent)) { // Inherit from parent tile if omitted. refine = parent.refine; } else { refine = Cesium3DTileRefine$1.REPLACE; } /** * Specifies the type of refinement that is used when traversing this tile for rendering. * * @type {Cesium3DTileRefine} * @readonly * @private */ this.refine = refine; /** * Gets the tile's children. * * @type {Cesium3DTile[]} * @readonly */ this.children = []; /** * This tile's parent or undefined if this tile is the root. *

* When a tile's content points to an external tileset JSON file, the external tileset's * root tile's parent is not undefined; instead, the parent references * the tile (with its content pointing to an external tileset JSON file) as if the two tilesets were merged. *

* * @type {Cesium3DTile} * @readonly */ this.parent = parent; var content; var hasEmptyContent; var contentState; var contentResource; var serverKey; baseResource = Resource.createIfNeeded(baseResource); if (defined(contentHeader)) { var contentHeaderUri = contentHeader.uri; if (defined(contentHeader.url)) { Cesium3DTile._deprecationWarning( "contentUrl", 'This tileset JSON uses the "content.url" property which has been deprecated. Use "content.uri" instead.' ); contentHeaderUri = contentHeader.url; } hasEmptyContent = false; contentState = Cesium3DTileContentState$1.UNLOADED; contentResource = baseResource.getDerivedResource({ url: contentHeaderUri, }); serverKey = RequestScheduler.getServerKey( contentResource.getUrlComponent() ); } else { content = new Empty3DTileContent(tileset, this); hasEmptyContent = true; contentState = Cesium3DTileContentState$1.READY; } this._content = content; this._contentResource = contentResource; this._contentState = contentState; this._contentReadyToProcessPromise = undefined; this._contentReadyPromise = undefined; this._expiredContent = undefined; this._serverKey = serverKey; /** * When true, the tile has no content. * * @type {Boolean} * @readonly * * @private */ this.hasEmptyContent = hasEmptyContent; /** * When true, the tile's content points to an external tileset. *

* This is false until the tile's content is loaded. *

* * @type {Boolean} * @readonly * * @private */ this.hasTilesetContent = false; /** * The node in the tileset's LRU cache, used to determine when to unload a tile's content. * * See {@link Cesium3DTilesetCache} * * @type {DoublyLinkedListNode} * @readonly * * @private */ this.cacheNode = undefined; var expire = header.expire; var expireDuration; var expireDate; if (defined(expire)) { expireDuration = expire.duration; if (defined(expire.date)) { expireDate = JulianDate.fromIso8601(expire.date); } } /** * The time in seconds after the tile's content is ready when the content expires and new content is requested. * * @type {Number} */ this.expireDuration = expireDuration; /** * The date when the content expires and new content is requested. * * @type {JulianDate} */ this.expireDate = expireDate; /** * The time when a style was last applied to this tile. * * @type {Number} * * @private */ this.lastStyleTime = 0.0; /** * Marks whether the tile's children bounds are fully contained within the tile's bounds * * @type {Cesium3DTileOptimizationHint} * * @private */ this._optimChildrenWithinParent = Cesium3DTileOptimizationHint$1.NOT_COMPUTED; /** * Tracks if the tile's relationship with a ClippingPlaneCollection has changed with regards * to the ClippingPlaneCollection's state. * * @type {Boolean} * * @private */ this.clippingPlanesDirty = false; /** * Tracks if the tile's request should be deferred until all non-deferred * tiles load. * * @type {Boolean} * * @private */ this.priorityDeferred = false; // Members that are updated every frame for tree traversal and rendering optimizations: this._distanceToCamera = 0.0; this._centerZDepth = 0.0; this._screenSpaceError = 0.0; this._screenSpaceErrorProgressiveResolution = 0.0; // The screen space error at a given screen height of tileset.progressiveResolutionHeightFraction * screenHeight this._visibilityPlaneMask = 0; this._visible = false; this._inRequestVolume = false; this._finalResolution = true; this._depth = 0; this._stackLength = 0; this._selectionDepth = 0; this._updatedVisibilityFrame = 0; this._touchedFrame = 0; this._visitedFrame = 0; this._selectedFrame = 0; this._requestedFrame = 0; this._ancestorWithContent = undefined; this._ancestorWithContentAvailable = undefined; this._refines = false; this._shouldSelect = false; this._isClipped = true; this._clippingPlanesState = 0; // encapsulates (_isClipped, clippingPlanes.enabled) and number/function this._debugBoundingVolume = undefined; this._debugContentBoundingVolume = undefined; this._debugViewerRequestVolume = undefined; this._debugColor = Color.fromRandom({ alpha: 1.0 }); this._debugColorizeTiles = false; this._priority = 0.0; // The priority used for request sorting this._priorityHolder = this; // Reference to the ancestor up the tree that holds the _foveatedFactor and _distanceToCamera for all tiles in the refinement chain. this._priorityProgressiveResolution = false; this._priorityProgressiveResolutionScreenSpaceErrorLeaf = false; this._priorityReverseScreenSpaceError = 0.0; this._foveatedFactor = 0.0; this._wasMinPriorityChild = false; // Needed for knowing when to continue a refinement chain. Gets reset in updateTile in traversal and gets set in updateAndPushChildren in traversal. this._loadTimestamp = new JulianDate(); this._commandsLength = 0; this._color = undefined; this._colorDirty = false; this._request = undefined; } // This can be overridden for testing purposes Cesium3DTile._deprecationWarning = deprecationWarning; Object.defineProperties(Cesium3DTile.prototype, { /** * The tileset containing this tile. * * @memberof Cesium3DTile.prototype * * @type {Cesium3DTileset} * @readonly */ tileset: { get: function () { return this._tileset; }, }, /** * The tile's content. This represents the actual tile's payload, * not the content's metadata in the tileset JSON file. * * @memberof Cesium3DTile.prototype * * @type {Cesium3DTileContent} * @readonly */ content: { get: function () { return this._content; }, }, /** * Get the tile's bounding volume. * * @memberof Cesium3DTile.prototype * * @type {TileBoundingVolume} * @readonly * @private */ boundingVolume: { get: function () { return this._boundingVolume; }, }, /** * Get the bounding volume of the tile's contents. This defaults to the * tile's bounding volume when the content's bounding volume is * undefined. * * @memberof Cesium3DTile.prototype * * @type {TileBoundingVolume} * @readonly * @private */ contentBoundingVolume: { get: function () { return defaultValue(this._contentBoundingVolume, this._boundingVolume); }, }, /** * Get the bounding sphere derived from the tile's bounding volume. * * @memberof Cesium3DTile.prototype * * @type {BoundingSphere} * @readonly */ boundingSphere: { get: function () { return this._boundingVolume.boundingSphere; }, }, /** * Returns the extras property in the tileset JSON for this tile, which contains application specific metadata. * Returns undefined if extras does not exist. * * @memberof Cesium3DTile.prototype * * @type {*} * @readonly * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification#specifying-extensions-and-application-specific-extras|Extras in the 3D Tiles specification.} */ extras: { get: function () { return this._header.extras; }, }, /** * Gets or sets the tile's highlight color. * * @memberof Cesium3DTile.prototype * * @type {Color} * * @default {@link Color.WHITE} * * @private */ color: { get: function () { if (!defined(this._color)) { this._color = new Color(); } return Color.clone(this._color); }, set: function (value) { this._color = Color.clone(value, this._color); this._colorDirty = true; }, }, /** * Determines if the tile has available content to render. true if the tile's * content is ready or if it has expired content that renders while new content loads; otherwise, * false. * * @memberof Cesium3DTile.prototype * * @type {Boolean} * @readonly * * @private */ contentAvailable: { get: function () { return ( (this.contentReady && !this.hasEmptyContent && !this.hasTilesetContent) || (defined(this._expiredContent) && !this.contentFailed) ); }, }, /** * Determines if the tile's content is ready. This is automatically true for * tile's with empty content. * * @memberof Cesium3DTile.prototype * * @type {Boolean} * @readonly * * @private */ contentReady: { get: function () { return this._contentState === Cesium3DTileContentState$1.READY; }, }, /** * Determines if the tile's content has not be requested. true if tile's * content has not be requested; otherwise, false. * * @memberof Cesium3DTile.prototype * * @type {Boolean} * @readonly * * @private */ contentUnloaded: { get: function () { return this._contentState === Cesium3DTileContentState$1.UNLOADED; }, }, /** * Determines if the tile's content is expired. true if tile's * content is expired; otherwise, false. * * @memberof Cesium3DTile.prototype * * @type {Boolean} * @readonly * * @private */ contentExpired: { get: function () { return this._contentState === Cesium3DTileContentState$1.EXPIRED; }, }, /** * Determines if the tile's content failed to load. true if the tile's * content failed to load; otherwise, false. * * @memberof Cesium3DTile.prototype * * @type {Boolean} * @readonly * * @private */ contentFailed: { get: function () { return this._contentState === Cesium3DTileContentState$1.FAILED; }, }, /** * Gets the promise that will be resolved when the tile's content is ready to process. * This happens after the content is downloaded but before the content is ready * to render. *

* The promise remains undefined until the tile's content is requested. *

* * @type {Promise.} * @readonly * * @private */ contentReadyToProcessPromise: { get: function () { if (defined(this._contentReadyToProcessPromise)) { return this._contentReadyToProcessPromise.promise; } return undefined; }, }, /** * Gets the promise that will be resolved when the tile's content is ready to render. *

* The promise remains undefined until the tile's content is requested. *

* * @type {Promise.} * @readonly * * @private */ contentReadyPromise: { get: function () { if (defined(this._contentReadyPromise)) { return this._contentReadyPromise.promise; } return undefined; }, }, /** * Returns the number of draw commands used by this tile. * * @readonly * * @private */ commandsLength: { get: function () { return this._commandsLength; }, }, }); var scratchCartesian$5 = new Cartesian3(); function isPriorityDeferred(tile, frameState) { var tileset = tile._tileset; // If closest point on line is inside the sphere then set foveatedFactor to 0. Otherwise, the dot product is with the line from camera to the point on the sphere that is closest to the line. var camera = frameState.camera; var boundingSphere = tile.boundingSphere; var radius = boundingSphere.radius; var scaledCameraDirection = Cartesian3.multiplyByScalar( camera.directionWC, tile._centerZDepth, scratchCartesian$5 ); var closestPointOnLine = Cartesian3.add( camera.positionWC, scaledCameraDirection, scratchCartesian$5 ); // The distance from the camera's view direction to the tile. var toLine = Cartesian3.subtract( closestPointOnLine, boundingSphere.center, scratchCartesian$5 ); var distanceToCenterLine = Cartesian3.magnitude(toLine); var notTouchingSphere = distanceToCenterLine > radius; // If camera's direction vector is inside the bounding sphere then consider // this tile right along the line of sight and set _foveatedFactor to 0. // Otherwise,_foveatedFactor is one minus the dot product of the camera's direction // and the vector between the camera and the point on the bounding sphere closest to the view line. if (notTouchingSphere) { var toLineNormalized = Cartesian3.normalize(toLine, scratchCartesian$5); var scaledToLine = Cartesian3.multiplyByScalar( toLineNormalized, radius, scratchCartesian$5 ); var closestOnSphere = Cartesian3.add( boundingSphere.center, scaledToLine, scratchCartesian$5 ); var toClosestOnSphere = Cartesian3.subtract( closestOnSphere, camera.positionWC, scratchCartesian$5 ); var toClosestOnSphereNormalize = Cartesian3.normalize( toClosestOnSphere, scratchCartesian$5 ); tile._foveatedFactor = 1.0 - Math.abs(Cartesian3.dot(camera.directionWC, toClosestOnSphereNormalize)); } else { tile._foveatedFactor = 0.0; } // Skip this feature if: non-skipLevelOfDetail and replace refine, if the foveated settings are turned off, if tile is progressive resolution and replace refine and skipLevelOfDetail (will help get rid of ancestor artifacts faster) // Or if the tile is a preload of any kind var replace = tile.refine === Cesium3DTileRefine$1.REPLACE; var skipLevelOfDetail = tileset._skipLevelOfDetail; if ( (replace && !skipLevelOfDetail) || !tileset.foveatedScreenSpaceError || tileset.foveatedConeSize === 1.0 || (tile._priorityProgressiveResolution && replace && skipLevelOfDetail) || tileset._pass === Cesium3DTilePass$1.PRELOAD_FLIGHT || tileset._pass === Cesium3DTilePass$1.PRELOAD ) { return false; } var maximumFovatedFactor = 1.0 - Math.cos(camera.frustum.fov * 0.5); // 0.14 for fov = 60. NOTE very hard to defer vertically foveated tiles since max is based on fovy (which is fov). Lowering the 0.5 to a smaller fraction of the screen height will start to defer vertically foveated tiles. var foveatedConeFactor = tileset.foveatedConeSize * maximumFovatedFactor; // If it's inside the user-defined view cone, then it should not be deferred. if (tile._foveatedFactor <= foveatedConeFactor) { return false; } // Relax SSE based on how big the angle is between the tile and the edge of the foveated cone. var range = maximumFovatedFactor - foveatedConeFactor; var normalizedFoveatedFactor = CesiumMath.clamp( (tile._foveatedFactor - foveatedConeFactor) / range, 0.0, 1.0 ); var sseRelaxation = tileset.foveatedInterpolationCallback( tileset.foveatedMinimumScreenSpaceErrorRelaxation, tileset.maximumScreenSpaceError, normalizedFoveatedFactor ); var sse = tile._screenSpaceError === 0.0 && defined(tile.parent) ? tile.parent._screenSpaceError * 0.5 : tile._screenSpaceError; return tileset.maximumScreenSpaceError - sseRelaxation <= sse; } var scratchJulianDate$1 = new JulianDate(); /** * Get the tile's screen space error. * * @private */ Cesium3DTile.prototype.getScreenSpaceError = function ( frameState, useParentGeometricError, progressiveResolutionHeightFraction ) { var tileset = this._tileset; var heightFraction = defaultValue(progressiveResolutionHeightFraction, 1.0); var parentGeometricError = defined(this.parent) ? this.parent.geometricError : tileset._geometricError; var geometricError = useParentGeometricError ? parentGeometricError : this.geometricError; if (geometricError === 0.0) { // Leaf tiles do not have any error so save the computation return 0.0; } var camera = frameState.camera; var frustum = camera.frustum; var context = frameState.context; var width = context.drawingBufferWidth; var height = context.drawingBufferHeight * heightFraction; var error; if ( frameState.mode === SceneMode$1.SCENE2D || frustum instanceof OrthographicFrustum ) { if (defined(frustum._offCenterFrustum)) { frustum = frustum._offCenterFrustum; } var pixelSize = Math.max(frustum.top - frustum.bottom, frustum.right - frustum.left) / Math.max(width, height); error = geometricError / pixelSize; } else { // Avoid divide by zero when viewer is inside the tile var distance = Math.max(this._distanceToCamera, CesiumMath.EPSILON7); var sseDenominator = camera.frustum.sseDenominator; error = (geometricError * height) / (distance * sseDenominator); if (tileset.dynamicScreenSpaceError) { var density = tileset._dynamicScreenSpaceErrorComputedDensity; var factor = tileset.dynamicScreenSpaceErrorFactor; var dynamicError = CesiumMath.fog(distance, density) * factor; error -= dynamicError; } } error /= frameState.pixelRatio; return error; }; function isPriorityProgressiveResolution(tileset, tile) { if ( tileset.progressiveResolutionHeightFraction <= 0.0 || tileset.progressiveResolutionHeightFraction > 0.5 ) { return false; } var isProgressiveResolutionTile = tile._screenSpaceErrorProgressiveResolution > tileset._maximumScreenSpaceError; // Mark non-SSE leaves tile._priorityProgressiveResolutionScreenSpaceErrorLeaf = false; // Needed for skipLOD var parent = tile.parent; var maximumScreenSpaceError = tileset._maximumScreenSpaceError; var tilePasses = tile._screenSpaceErrorProgressiveResolution <= maximumScreenSpaceError; var parentFails = defined(parent) && parent._screenSpaceErrorProgressiveResolution > maximumScreenSpaceError; if (tilePasses && parentFails) { // A progressive resolution SSE leaf, promote its priority as well tile._priorityProgressiveResolutionScreenSpaceErrorLeaf = true; isProgressiveResolutionTile = true; } return isProgressiveResolutionTile; } function getPriorityReverseScreenSpaceError(tileset, tile) { var parent = tile.parent; var useParentScreenSpaceError = defined(parent) && (!tileset._skipLevelOfDetail || tile._screenSpaceError === 0.0 || parent.hasTilesetContent); var screenSpaceError = useParentScreenSpaceError ? parent._screenSpaceError : tile._screenSpaceError; return tileset.root._screenSpaceError - screenSpaceError; } /** * Update the tile's visibility. * * @private */ Cesium3DTile.prototype.updateVisibility = function (frameState) { var parent = this.parent; var tileset = this._tileset; var parentTransform = defined(parent) ? parent.computedTransform : tileset.modelMatrix; var parentVisibilityPlaneMask = defined(parent) ? parent._visibilityPlaneMask : CullingVolume.MASK_INDETERMINATE; this.updateTransform(parentTransform); this._distanceToCamera = this.distanceToTile(frameState); this._centerZDepth = this.distanceToTileCenter(frameState); this._screenSpaceError = this.getScreenSpaceError(frameState, false); this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError( frameState, false, tileset.progressiveResolutionHeightFraction ); this._visibilityPlaneMask = this.visibility( frameState, parentVisibilityPlaneMask ); // Use parent's plane mask to speed up visibility test this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE; this._inRequestVolume = this.insideViewerRequestVolume(frameState); this._priorityReverseScreenSpaceError = getPriorityReverseScreenSpaceError( tileset, this ); this._priorityProgressiveResolution = isPriorityProgressiveResolution( tileset, this ); this.priorityDeferred = isPriorityDeferred(this, frameState); }; /** * Update whether the tile has expired. * * @private */ Cesium3DTile.prototype.updateExpiration = function () { if (defined(this.expireDate) && this.contentReady && !this.hasEmptyContent) { var now = JulianDate.now(scratchJulianDate$1); if (JulianDate.lessThan(this.expireDate, now)) { this._contentState = Cesium3DTileContentState$1.EXPIRED; this._expiredContent = this._content; } } }; function updateExpireDate(tile) { if (defined(tile.expireDuration)) { var expireDurationDate = JulianDate.now(scratchJulianDate$1); JulianDate.addSeconds( expireDurationDate, tile.expireDuration, expireDurationDate ); if (defined(tile.expireDate)) { if (JulianDate.lessThan(tile.expireDate, expireDurationDate)) { JulianDate.clone(expireDurationDate, tile.expireDate); } } else { tile.expireDate = JulianDate.clone(expireDurationDate); } } } function getContentFailedFunction(tile, tileset) { return function (error) { if (tile._contentState === Cesium3DTileContentState$1.PROCESSING) { --tileset.statistics.numberOfTilesProcessing; } else { --tileset.statistics.numberOfPendingRequests; } tile._contentState = Cesium3DTileContentState$1.FAILED; tile._contentReadyPromise.reject(error); tile._contentReadyToProcessPromise.reject(error); }; } function createPriorityFunction(tile) { return function () { return tile._priority; }; } /** * Requests the tile's content. *

* The request may not be made if the Cesium Request Scheduler can't prioritize it. *

* * @private */ Cesium3DTile.prototype.requestContent = function () { var that = this; var tileset = this._tileset; if (this.hasEmptyContent) { return false; } var resource = this._contentResource.clone(); var expired = this.contentExpired; if (expired) { // Append a query parameter of the tile expiration date to prevent caching resource.setQueryParameters({ expired: this.expireDate.toString(), }); } var request = new Request({ throttle: true, throttleByServer: true, type: RequestType$1.TILES3D, priorityFunction: createPriorityFunction(this), serverKey: this._serverKey, }); this._request = request; resource.request = request; var promise = resource.fetchArrayBuffer(); if (!defined(promise)) { return false; } var contentState = this._contentState; this._contentState = Cesium3DTileContentState$1.LOADING; this._contentReadyToProcessPromise = when.defer(); this._contentReadyPromise = when.defer(); var contentFailedFunction = getContentFailedFunction(this, tileset); promise .then(function (arrayBuffer) { if (that.isDestroyed()) { // Tile is unloaded before the content finishes loading contentFailedFunction(); return; } var uint8Array = new Uint8Array(arrayBuffer); var magic = getMagic(uint8Array); var contentFactory = Cesium3DTileContentFactory[magic]; var content; // Vector and Geometry tile rendering do not support the skip LOD optimization. tileset._disableSkipLevelOfDetail = tileset._disableSkipLevelOfDetail || magic === "vctr" || magic === "geom"; if (defined(contentFactory)) { content = contentFactory( tileset, that, that._contentResource, arrayBuffer, 0 ); } else { // The content may be json instead content = Cesium3DTileContentFactory.json( tileset, that, that._contentResource, arrayBuffer, 0 ); that.hasTilesetContent = true; } if (expired) { that.expireDate = undefined; } that._content = content; that._contentState = Cesium3DTileContentState$1.PROCESSING; that._contentReadyToProcessPromise.resolve(content); return content.readyPromise.then(function (content) { if (that.isDestroyed()) { // Tile is unloaded before the content finishes processing contentFailedFunction(); return; } updateExpireDate(that); // Refresh style for expired content that._selectedFrame = 0; that.lastStyleTime = 0.0; JulianDate.now(that._loadTimestamp); that._contentState = Cesium3DTileContentState$1.READY; that._contentReadyPromise.resolve(content); }); }) .otherwise(function (error) { if (request.state === RequestState$1.CANCELLED) { // Cancelled due to low priority - try again later. that._contentState = contentState; --tileset.statistics.numberOfPendingRequests; ++tileset.statistics.numberOfAttemptedRequests; return; } contentFailedFunction(error); }); return true; }; /** * Unloads the tile's content. * * @private */ Cesium3DTile.prototype.unloadContent = function () { if (this.hasEmptyContent || this.hasTilesetContent) { return; } this._content = this._content && this._content.destroy(); this._contentState = Cesium3DTileContentState$1.UNLOADED; this._contentReadyToProcessPromise = undefined; this._contentReadyPromise = undefined; this.lastStyleTime = 0.0; this.clippingPlanesDirty = this._clippingPlanesState === 0; this._clippingPlanesState = 0; this._debugColorizeTiles = false; this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); }; var scratchProjectedBoundingSphere = new BoundingSphere(); function getBoundingVolume(tile, frameState) { if ( frameState.mode !== SceneMode$1.SCENE3D && !defined(tile._boundingVolume2D) ) { var boundingSphere = tile._boundingVolume.boundingSphere; var sphere = BoundingSphere.projectTo2D( boundingSphere, frameState.mapProjection, scratchProjectedBoundingSphere ); tile._boundingVolume2D = new TileBoundingSphere( sphere.center, sphere.radius ); } return frameState.mode !== SceneMode$1.SCENE3D ? tile._boundingVolume2D : tile._boundingVolume; } function getContentBoundingVolume(tile, frameState) { if ( frameState.mode !== SceneMode$1.SCENE3D && !defined(tile._contentBoundingVolume2D) ) { var boundingSphere = tile._contentBoundingVolume.boundingSphere; var sphere = BoundingSphere.projectTo2D( boundingSphere, frameState.mapProjection, scratchProjectedBoundingSphere ); tile._contentBoundingVolume2D = new TileBoundingSphere( sphere.center, sphere.radius ); } return frameState.mode !== SceneMode$1.SCENE3D ? tile._contentBoundingVolume2D : tile._contentBoundingVolume; } /** * Determines whether the tile's bounding volume intersects the culling volume. * * @param {FrameState} frameState The frame state. * @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check. * @returns {Number} A plane mask as described above in {@link CullingVolume#computeVisibilityWithPlaneMask}. * * @private */ Cesium3DTile.prototype.visibility = function ( frameState, parentVisibilityPlaneMask ) { var cullingVolume = frameState.cullingVolume; var boundingVolume = getBoundingVolume(this, frameState); var tileset = this._tileset; var clippingPlanes = tileset.clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { var intersection = clippingPlanes.computeIntersectionWithBoundingVolume( boundingVolume, tileset.clippingPlanesOriginMatrix ); this._isClipped = intersection !== Intersect$1.INSIDE; if (intersection === Intersect$1.OUTSIDE) { return CullingVolume.MASK_OUTSIDE; } } return cullingVolume.computeVisibilityWithPlaneMask( boundingVolume, parentVisibilityPlaneMask ); }; /** * Assuming the tile's bounding volume intersects the culling volume, determines * whether the tile's content's bounding volume intersects the culling volume. * * @param {FrameState} frameState The frame state. * @returns {Intersect} The result of the intersection: the tile's content is completely outside, completely inside, or intersecting the culling volume. * * @private */ Cesium3DTile.prototype.contentVisibility = function (frameState) { // Assumes the tile's bounding volume intersects the culling volume already, so // just return Intersect.INSIDE if there is no content bounding volume. if (!defined(this._contentBoundingVolume)) { return Intersect$1.INSIDE; } if (this._visibilityPlaneMask === CullingVolume.MASK_INSIDE) { // The tile's bounding volume is completely inside the culling volume so // the content bounding volume must also be inside. return Intersect$1.INSIDE; } // PERFORMANCE_IDEA: is it possible to burn less CPU on this test since we know the // tile's (not the content's) bounding volume intersects the culling volume? var cullingVolume = frameState.cullingVolume; var boundingVolume = getContentBoundingVolume(this, frameState); var tileset = this._tileset; var clippingPlanes = tileset.clippingPlanes; if (defined(clippingPlanes) && clippingPlanes.enabled) { var intersection = clippingPlanes.computeIntersectionWithBoundingVolume( boundingVolume, tileset.clippingPlanesOriginMatrix ); this._isClipped = intersection !== Intersect$1.INSIDE; if (intersection === Intersect$1.OUTSIDE) { return Intersect$1.OUTSIDE; } } return cullingVolume.computeVisibility(boundingVolume); }; /** * Computes the (potentially approximate) distance from the closest point of the tile's bounding volume to the camera. * * @param {FrameState} frameState The frame state. * @returns {Number} The distance, in meters, or zero if the camera is inside the bounding volume. * * @private */ Cesium3DTile.prototype.distanceToTile = function (frameState) { var boundingVolume = getBoundingVolume(this, frameState); return boundingVolume.distanceToCamera(frameState); }; var scratchToTileCenter = new Cartesian3(); /** * Computes the distance from the center of the tile's bounding volume to the camera's plane defined by its position and view direction. * * @param {FrameState} frameState The frame state. * @returns {Number} The distance, in meters. * * @private */ Cesium3DTile.prototype.distanceToTileCenter = function (frameState) { var tileBoundingVolume = getBoundingVolume(this, frameState); var boundingVolume = tileBoundingVolume.boundingVolume; // Gets the underlying OrientedBoundingBox or BoundingSphere var toCenter = Cartesian3.subtract( boundingVolume.center, frameState.camera.positionWC, scratchToTileCenter ); return Cartesian3.dot(frameState.camera.directionWC, toCenter); }; /** * Checks if the camera is inside the viewer request volume. * * @param {FrameState} frameState The frame state. * @returns {Boolean} Whether the camera is inside the volume. * * @private */ Cesium3DTile.prototype.insideViewerRequestVolume = function (frameState) { var viewerRequestVolume = this._viewerRequestVolume; return ( !defined(viewerRequestVolume) || viewerRequestVolume.distanceToCamera(frameState) === 0.0 ); }; var scratchMatrix$2 = new Matrix3(); var scratchScale$2 = new Cartesian3(); var scratchHalfAxes = new Matrix3(); var scratchCenter$3 = new Cartesian3(); var scratchRectangle$5 = new Rectangle(); var scratchOrientedBoundingBox = new OrientedBoundingBox(); var scratchTransform = new Matrix4(); function createBox(box, transform, result) { var center = Cartesian3.fromElements(box[0], box[1], box[2], scratchCenter$3); var halfAxes = Matrix3.fromArray(box, 3, scratchHalfAxes); // Find the transformed center and halfAxes center = Matrix4.multiplyByPoint(transform, center, center); var rotationScale = Matrix4.getMatrix3(transform, scratchMatrix$2); halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes); if (defined(result)) { result.update(center, halfAxes); return result; } return new TileOrientedBoundingBox(center, halfAxes); } function createBoxFromTransformedRegion( region, transform, initialTransform, result ) { var rectangle = Rectangle.unpack(region, 0, scratchRectangle$5); var minimumHeight = region[4]; var maximumHeight = region[5]; var orientedBoundingBox = OrientedBoundingBox.fromRectangle( rectangle, minimumHeight, maximumHeight, Ellipsoid.WGS84, scratchOrientedBoundingBox ); var center = orientedBoundingBox.center; var halfAxes = orientedBoundingBox.halfAxes; // A region bounding volume is not transformed by the transform in the tileset JSON, // but may be transformed by additional transforms applied in Cesium. // This is why the transform is calculated as the difference between the initial transform and the current transform. transform = Matrix4.multiplyTransformation( transform, Matrix4.inverseTransformation(initialTransform, scratchTransform), scratchTransform ); center = Matrix4.multiplyByPoint(transform, center, center); var rotationScale = Matrix4.getMatrix3(transform, scratchMatrix$2); halfAxes = Matrix3.multiply(rotationScale, halfAxes, halfAxes); if (defined(result) && result instanceof TileOrientedBoundingBox) { result.update(center, halfAxes); return result; } return new TileOrientedBoundingBox(center, halfAxes); } function createRegion(region, transform, initialTransform, result) { if ( !Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8) ) { return createBoxFromTransformedRegion( region, transform, initialTransform, result ); } if (defined(result)) { return result; } var rectangleRegion = Rectangle.unpack(region, 0, scratchRectangle$5); return new TileBoundingRegion({ rectangle: rectangleRegion, minimumHeight: region[4], maximumHeight: region[5], }); } function createSphere(sphere, transform, result) { var center = Cartesian3.fromElements( sphere[0], sphere[1], sphere[2], scratchCenter$3 ); var radius = sphere[3]; // Find the transformed center and radius center = Matrix4.multiplyByPoint(transform, center, center); var scale = Matrix4.getScale(transform, scratchScale$2); var uniformScale = Cartesian3.maximumComponent(scale); radius *= uniformScale; if (defined(result)) { result.update(center, radius); return result; } return new TileBoundingSphere(center, radius); } /** * Create a bounding volume from the tile's bounding volume header. * * @param {Object} boundingVolumeHeader The tile's bounding volume header. * @param {Matrix4} transform The transform to apply to the bounding volume. * @param {TileBoundingVolume} [result] The object onto which to store the result. * * @returns {TileBoundingVolume} The modified result parameter or a new TileBoundingVolume instance if none was provided. * * @private */ Cesium3DTile.prototype.createBoundingVolume = function ( boundingVolumeHeader, transform, result ) { if (!defined(boundingVolumeHeader)) { throw new RuntimeError("boundingVolume must be defined"); } if (defined(boundingVolumeHeader.box)) { return createBox(boundingVolumeHeader.box, transform, result); } if (defined(boundingVolumeHeader.region)) { return createRegion( boundingVolumeHeader.region, transform, this._initialTransform, result ); } if (defined(boundingVolumeHeader.sphere)) { return createSphere(boundingVolumeHeader.sphere, transform, result); } throw new RuntimeError( "boundingVolume must contain a sphere, region, or box" ); }; /** * Update the tile's transform. The transform is applied to the tile's bounding volumes. * * @private */ Cesium3DTile.prototype.updateTransform = function (parentTransform) { parentTransform = defaultValue(parentTransform, Matrix4.IDENTITY); var computedTransform = Matrix4.multiply( parentTransform, this.transform, scratchTransform ); var transformChanged = !Matrix4.equals( computedTransform, this.computedTransform ); if (!transformChanged) { return; } Matrix4.clone(computedTransform, this.computedTransform); // Update the bounding volumes var header = this._header; var content = this._header.content; this._boundingVolume = this.createBoundingVolume( header.boundingVolume, this.computedTransform, this._boundingVolume ); if (defined(this._contentBoundingVolume)) { this._contentBoundingVolume = this.createBoundingVolume( content.boundingVolume, this.computedTransform, this._contentBoundingVolume ); } if (defined(this._viewerRequestVolume)) { this._viewerRequestVolume = this.createBoundingVolume( header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume ); } this.updateGeometricErrorScale(); // Destroy the debug bounding volumes. They will be generated fresh. this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); }; Cesium3DTile.prototype.updateGeometricErrorScale = function () { var scale = Matrix4.getScale(this.computedTransform, scratchScale$2); var uniformScale = Cartesian3.maximumComponent(scale); this.geometricError = this._geometricError * uniformScale; }; function applyDebugSettings$1(tile, tileset, frameState, passOptions) { if (!passOptions.isRender) { return; } var hasContentBoundingVolume = defined(tile._header.content) && defined(tile._header.content.boundingVolume); var empty = tile.hasEmptyContent || tile.hasTilesetContent; var showVolume = tileset.debugShowBoundingVolume || (tileset.debugShowContentBoundingVolume && !hasContentBoundingVolume); if (showVolume) { var color; if (!tile._finalResolution) { color = Color.YELLOW; } else if (empty) { color = Color.DARKGRAY; } else { color = Color.WHITE; } if (!defined(tile._debugBoundingVolume)) { tile._debugBoundingVolume = tile._boundingVolume.createDebugVolume(color); } tile._debugBoundingVolume.update(frameState); var attributes = tile._debugBoundingVolume.getGeometryInstanceAttributes( "outline" ); attributes.color = ColorGeometryInstanceAttribute.toValue( color, attributes.color ); } else if (!showVolume && defined(tile._debugBoundingVolume)) { tile._debugBoundingVolume = tile._debugBoundingVolume.destroy(); } if (tileset.debugShowContentBoundingVolume && hasContentBoundingVolume) { if (!defined(tile._debugContentBoundingVolume)) { tile._debugContentBoundingVolume = tile._contentBoundingVolume.createDebugVolume( Color.BLUE ); } tile._debugContentBoundingVolume.update(frameState); } else if ( !tileset.debugShowContentBoundingVolume && defined(tile._debugContentBoundingVolume) ) { tile._debugContentBoundingVolume = tile._debugContentBoundingVolume.destroy(); } if ( tileset.debugShowViewerRequestVolume && defined(tile._viewerRequestVolume) ) { if (!defined(tile._debugViewerRequestVolume)) { tile._debugViewerRequestVolume = tile._viewerRequestVolume.createDebugVolume( Color.YELLOW ); } tile._debugViewerRequestVolume.update(frameState); } else if ( !tileset.debugShowViewerRequestVolume && defined(tile._debugViewerRequestVolume) ) { tile._debugViewerRequestVolume = tile._debugViewerRequestVolume.destroy(); } var debugColorizeTilesOn = (tileset.debugColorizeTiles && !tile._debugColorizeTiles) || defined(tileset._heatmap.tilePropertyName); var debugColorizeTilesOff = !tileset.debugColorizeTiles && tile._debugColorizeTiles; if (debugColorizeTilesOn) { tileset._heatmap.colorize(tile, frameState); // Skipped if tileset._heatmap.tilePropertyName is undefined tile._debugColorizeTiles = true; tile.color = tile._debugColor; } else if (debugColorizeTilesOff) { tile._debugColorizeTiles = false; tile.color = Color.WHITE; } if (tile._colorDirty) { tile._colorDirty = false; tile._content.applyDebugSettings(true, tile._color); } if (debugColorizeTilesOff) { tileset.makeStyleDirty(); // Re-apply style now that colorize is switched off } } function updateContent(tile, tileset, frameState) { var content = tile._content; var expiredContent = tile._expiredContent; if (defined(expiredContent)) { if (!tile.contentReady) { // Render the expired content while the content loads expiredContent.update(tileset, frameState); return; } // New content is ready, destroy expired content tile._expiredContent.destroy(); tile._expiredContent = undefined; } content.update(tileset, frameState); } function updateClippingPlanes(tile, tileset) { // Compute and compare ClippingPlanes state: // - enabled-ness - are clipping planes enabled? is this tile clipped? // - clipping plane count // - clipping function (union v. intersection) var clippingPlanes = tileset.clippingPlanes; var currentClippingPlanesState = 0; if (defined(clippingPlanes) && tile._isClipped && clippingPlanes.enabled) { currentClippingPlanesState = clippingPlanes.clippingPlanesState; } // If clippingPlaneState for tile changed, mark clippingPlanesDirty so content can update if (currentClippingPlanesState !== tile._clippingPlanesState) { tile._clippingPlanesState = currentClippingPlanesState; tile.clippingPlanesDirty = true; } } /** * Get the draw commands needed to render this tile. * * @private */ Cesium3DTile.prototype.update = function (tileset, frameState, passOptions) { var initCommandLength = frameState.commandList.length; updateClippingPlanes(this, tileset); applyDebugSettings$1(this, tileset, frameState, passOptions); updateContent(this, tileset, frameState); this._commandsLength = frameState.commandList.length - initCommandLength; this.clippingPlanesDirty = false; // reset after content update }; var scratchCommandList$1 = []; /** * Processes the tile's content, e.g., create WebGL resources, to move from the PROCESSING to READY state. * * @param {Cesium3DTileset} tileset The tileset containing this tile. * @param {FrameState} frameState The frame state. * * @private */ Cesium3DTile.prototype.process = function (tileset, frameState) { var savedCommandList = frameState.commandList; frameState.commandList = scratchCommandList$1; this._content.update(tileset, frameState); scratchCommandList$1.length = 0; frameState.commandList = savedCommandList; }; function isolateDigits(normalizedValue, numberOfDigits, leftShift) { var scaled = normalizedValue * Math.pow(10, numberOfDigits); var integer = parseInt(scaled); return integer * Math.pow(10, leftShift); } function priorityNormalizeAndClamp(value, minimum, maximum) { return Math.max( CesiumMath.normalize(value, minimum, maximum) - CesiumMath.EPSILON7, 0.0 ); // Subtract epsilon since we only want decimal digits present in the output. } /** * Sets the priority of the tile based on distance and depth * @private */ Cesium3DTile.prototype.updatePriority = function () { var tileset = this.tileset; var preferLeaves = tileset.preferLeaves; var minimumPriority = tileset._minimumPriority; var maximumPriority = tileset._maximumPriority; // Combine priority systems together by mapping them into a base 10 number where each priority controls a specific set of digits in the number. // For number priorities, map them to a 0.xxxxx number then left shift it up into a set number of digits before the decimal point. Chop of the fractional part then left shift again into the position it needs to go. // For blending number priorities, normalize them to 0-1 and interpolate to get a combined 0-1 number, then proceed as normal. // Booleans can just be 0 or 10^leftshift. // Think of digits as penalties since smaller numbers are higher priority. If a tile has some large quantity or has a flag raised it's (usually) penalized for it, expressed as a higher number for the digit. // Priority number format: preloadFlightDigits(1) | foveatedDeferDigits(1) | foveatedDigits(4) | preloadProgressiveResolutionDigits(1) | preferredSortingDigits(4) . depthDigits(the decimal digits) // Certain flags like preferLeaves will flip / turn off certain digits to get desired load order. // Setup leftShifts, digit counts, and scales (for booleans) var digitsForANumber = 4; var digitsForABoolean = 1; var preferredSortingLeftShift = 0; var preferredSortingDigitsCount = digitsForANumber; var foveatedLeftShift = preferredSortingLeftShift + preferredSortingDigitsCount; var foveatedDigitsCount = digitsForANumber; var preloadProgressiveResolutionLeftShift = foveatedLeftShift + foveatedDigitsCount; var preloadProgressiveResolutionDigitsCount = digitsForABoolean; var preloadProgressiveResolutionScale = Math.pow( 10, preloadProgressiveResolutionLeftShift ); var foveatedDeferLeftShift = preloadProgressiveResolutionLeftShift + preloadProgressiveResolutionDigitsCount; var foveatedDeferDigitsCount = digitsForABoolean; var foveatedDeferScale = Math.pow(10, foveatedDeferLeftShift); var preloadFlightLeftShift = foveatedDeferLeftShift + foveatedDeferDigitsCount; var preloadFlightScale = Math.pow(10, preloadFlightLeftShift); // Compute the digits for each priority var depthDigits = priorityNormalizeAndClamp( this._depth, minimumPriority.depth, maximumPriority.depth ); depthDigits = preferLeaves ? 1.0 - depthDigits : depthDigits; // Map 0-1 then convert to digit. Include a distance sort when doing non-skipLOD and replacement refinement, helps things like non-skipLOD photogrammetry var useDistance = !tileset._skipLevelOfDetail && this.refine === Cesium3DTileRefine$1.REPLACE; var normalizedPreferredSorting = useDistance ? priorityNormalizeAndClamp( this._priorityHolder._distanceToCamera, minimumPriority.distance, maximumPriority.distance ) : priorityNormalizeAndClamp( this._priorityReverseScreenSpaceError, minimumPriority.reverseScreenSpaceError, maximumPriority.reverseScreenSpaceError ); var preferredSortingDigits = isolateDigits( normalizedPreferredSorting, preferredSortingDigitsCount, preferredSortingLeftShift ); var preloadProgressiveResolutionDigits = this._priorityProgressiveResolution ? 0 : preloadProgressiveResolutionScale; var normalizedFoveatedFactor = priorityNormalizeAndClamp( this._priorityHolder._foveatedFactor, minimumPriority.foveatedFactor, maximumPriority.foveatedFactor ); var foveatedDigits = isolateDigits( normalizedFoveatedFactor, foveatedDigitsCount, foveatedLeftShift ); var foveatedDeferDigits = this.priorityDeferred ? foveatedDeferScale : 0; var preloadFlightDigits = tileset._pass === Cesium3DTilePass$1.PRELOAD_FLIGHT ? 0 : preloadFlightScale; // Get the final base 10 number this._priority = depthDigits + preferredSortingDigits + preloadProgressiveResolutionDigits + foveatedDigits + foveatedDeferDigits + preloadFlightDigits; }; /** * @private */ Cesium3DTile.prototype.isDestroyed = function () { return false; }; /** * @private */ Cesium3DTile.prototype.destroy = function () { // For the interval between new content being requested and downloaded, expiredContent === content, so don't destroy twice this._content = this._content && this._content.destroy(); this._expiredContent = this._expiredContent && !this._expiredContent.isDestroyed() && this._expiredContent.destroy(); this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy(); this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy(); this._debugViewerRequestVolume = this._debugViewerRequestVolume && this._debugViewerRequestVolume.destroy(); return destroyObject(this); }; /** * Utility functions for computing optimization hints for a {@link Cesium3DTileset}. * * @namespace Cesium3DTileOptimizations * * @private */ var Cesium3DTileOptimizations = {}; var scratchAxis$1 = new Cartesian3(); /** * Evaluates support for the childrenWithinParent optimization. This is used to more tightly cull tilesets if * children bounds are fully contained within the parent. Currently, support for the optimization only works for * oriented bounding boxes, so both the child and parent tile must be either a {@link TileOrientedBoundingBox} or * {@link TileBoundingRegion}. The purpose of this check is to prevent use of a culling optimization when the child * bounds exceed those of the parent. If the child bounds are greater, it is more likely that the optimization will * waste CPU cycles. Bounding spheres are not supported for the reason that the child bounds can very often be * partially outside of the parent bounds. * * @param {Cesium3DTile} tile The tile to check. * @returns {Boolean} Whether the childrenWithinParent optimization is supported. */ Cesium3DTileOptimizations.checkChildrenWithinParent = function (tile) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("tile", tile); //>>includeEnd('debug'); var children = tile.children; var length = children.length; // Check if the parent has an oriented bounding box. var boundingVolume = tile.boundingVolume; if ( boundingVolume instanceof TileOrientedBoundingBox || boundingVolume instanceof TileBoundingRegion ) { var orientedBoundingBox = boundingVolume._orientedBoundingBox; tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint$1.USE_OPTIMIZATION; for (var i = 0; i < length; ++i) { var child = children[i]; // Check if the child has an oriented bounding box. var childBoundingVolume = child.boundingVolume; if ( !( childBoundingVolume instanceof TileOrientedBoundingBox || childBoundingVolume instanceof TileBoundingRegion ) ) { // Do not support if the parent and child both do not have oriented bounding boxes. tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint$1.SKIP_OPTIMIZATION; break; } var childOrientedBoundingBox = childBoundingVolume._orientedBoundingBox; // Compute the axis from the parent to the child. var axis = Cartesian3.subtract( childOrientedBoundingBox.center, orientedBoundingBox.center, scratchAxis$1 ); var axisLength = Cartesian3.magnitude(axis); Cartesian3.divideByScalar(axis, axisLength, axis); // Project the bounding box of the parent onto the axis. Because the axis is a ray from the parent // to the child, the projection parameterized along the ray will be (+/- proj1). var proj1 = Math.abs(orientedBoundingBox.halfAxes[0] * axis.x) + Math.abs(orientedBoundingBox.halfAxes[1] * axis.y) + Math.abs(orientedBoundingBox.halfAxes[2] * axis.z) + Math.abs(orientedBoundingBox.halfAxes[3] * axis.x) + Math.abs(orientedBoundingBox.halfAxes[4] * axis.y) + Math.abs(orientedBoundingBox.halfAxes[5] * axis.z) + Math.abs(orientedBoundingBox.halfAxes[6] * axis.x) + Math.abs(orientedBoundingBox.halfAxes[7] * axis.y) + Math.abs(orientedBoundingBox.halfAxes[8] * axis.z); // Project the bounding box of the child onto the axis. Because the axis is a ray from the parent // to the child, the projection parameterized along the ray will be (+/- proj2) + axis.length. var proj2 = Math.abs(childOrientedBoundingBox.halfAxes[0] * axis.x) + Math.abs(childOrientedBoundingBox.halfAxes[1] * axis.y) + Math.abs(childOrientedBoundingBox.halfAxes[2] * axis.z) + Math.abs(childOrientedBoundingBox.halfAxes[3] * axis.x) + Math.abs(childOrientedBoundingBox.halfAxes[4] * axis.y) + Math.abs(childOrientedBoundingBox.halfAxes[5] * axis.z) + Math.abs(childOrientedBoundingBox.halfAxes[6] * axis.x) + Math.abs(childOrientedBoundingBox.halfAxes[7] * axis.y) + Math.abs(childOrientedBoundingBox.halfAxes[8] * axis.z); // If the child extends the parent's bounds, the optimization is not valid and we skip it. if (proj1 <= proj2 + axisLength) { tile._optimChildrenWithinParent = Cesium3DTileOptimizationHint$1.SKIP_OPTIMIZATION; break; } } } return ( tile._optimChildrenWithinParent === Cesium3DTileOptimizationHint$1.USE_OPTIMIZATION ); }; /** * Stores tiles with content loaded. * * @private */ function Cesium3DTilesetCache() { // [head, sentinel) -> tiles that weren't selected this frame and may be removed from the cache // (sentinel, tail] -> tiles that were selected this frame this._list = new DoublyLinkedList(); this._sentinel = this._list.add(); this._trimTiles = false; } Cesium3DTilesetCache.prototype.reset = function () { // Move sentinel node to the tail so, at the start of the frame, all tiles // may be potentially replaced. Tiles are moved to the right of the sentinel // when they are selected so they will not be replaced. this._list.splice(this._list.tail, this._sentinel); }; Cesium3DTilesetCache.prototype.touch = function (tile) { var node = tile.cacheNode; if (defined(node)) { this._list.splice(this._sentinel, node); } }; Cesium3DTilesetCache.prototype.add = function (tile) { if (!defined(tile.cacheNode)) { tile.cacheNode = this._list.add(tile); } }; Cesium3DTilesetCache.prototype.unloadTile = function ( tileset, tile, unloadCallback ) { var node = tile.cacheNode; if (!defined(node)) { return; } this._list.remove(node); tile.cacheNode = undefined; unloadCallback(tileset, tile); }; Cesium3DTilesetCache.prototype.unloadTiles = function ( tileset, unloadCallback ) { var trimTiles = this._trimTiles; this._trimTiles = false; var list = this._list; var maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024; // Traverse the list only to the sentinel since tiles/nodes to the // right of the sentinel were used this frame. // // The sub-list to the left of the sentinel is ordered from LRU to MRU. var sentinel = this._sentinel; var node = list.head; while ( node !== sentinel && (tileset.totalMemoryUsageInBytes > maximumMemoryUsageInBytes || trimTiles) ) { var tile = node.item; node = node.next; this.unloadTile(tileset, tile, unloadCallback); } }; Cesium3DTilesetCache.prototype.trim = function () { this._trimTiles = true; }; /** * A heatmap colorizer in a {@link Cesium3DTileset}. A tileset can colorize its visible tiles in a heatmap style. * * @alias Cesium3DTilesetHeatmap * @constructor * @private */ function Cesium3DTilesetHeatmap(tilePropertyName) { /** * The tile variable to track for heatmap colorization. * Tile's will be colorized relative to the other visible tile's values for this variable. * * @type {String} */ this.tilePropertyName = tilePropertyName; // Members that are updated every time a tile is colorized this._minimum = Number.MAX_VALUE; this._maximum = -Number.MAX_VALUE; // Members that are updated once every frame this._previousMinimum = Number.MAX_VALUE; this._previousMaximum = -Number.MAX_VALUE; // If defined uses a reference minimum maximum to colorize by instead of using last frames minimum maximum of rendered tiles. // For example, the _loadTimestamp can get a better colorization using setReferenceMinimumMaximum in order to take accurate colored timing diffs of various scenes. this._referenceMinimum = {}; this._referenceMaximum = {}; } /** * Convert to a usable heatmap value (i.e. a number). Ensures that tile values that aren't stored as numbers can be used for colorization. * @private */ function getHeatmapValue(tileValue, tilePropertyName) { var value; if (tilePropertyName === "_loadTimestamp") { value = JulianDate.toDate(tileValue).getTime(); } else { value = tileValue; } return value; } /** * Sets the reference minimum and maximum for the variable name. Converted to numbers before they are stored. * * @param {Object} minimum The minimum reference value. * @param {Object} maximum The maximum reference value. * @param {String} tilePropertyName The tile variable that will use these reference values when it is colorized. */ Cesium3DTilesetHeatmap.prototype.setReferenceMinimumMaximum = function ( minimum, maximum, tilePropertyName ) { this._referenceMinimum[tilePropertyName] = getHeatmapValue( minimum, tilePropertyName ); this._referenceMaximum[tilePropertyName] = getHeatmapValue( maximum, tilePropertyName ); }; function getHeatmapValueAndUpdateMinimumMaximum(heatmap, tile) { var tilePropertyName = heatmap.tilePropertyName; if (defined(tilePropertyName)) { var heatmapValue = getHeatmapValue( tile[tilePropertyName], tilePropertyName ); if (!defined(heatmapValue)) { heatmap.tilePropertyName = undefined; return heatmapValue; } heatmap._maximum = Math.max(heatmapValue, heatmap._maximum); heatmap._minimum = Math.min(heatmapValue, heatmap._minimum); return heatmapValue; } } var heatmapColors = [ new Color(0.1, 0.1, 0.1, 1), // Dark Gray new Color(0.153, 0.278, 0.878, 1), // Blue new Color(0.827, 0.231, 0.49, 1), // Pink new Color(0.827, 0.188, 0.22, 1), // Red new Color(1.0, 0.592, 0.259, 1), // Orange new Color(1.0, 0.843, 0.0, 1), ]; // Yellow /** * Colorize the tile in heat map style based on where it lies within the minimum maximum window. * Heatmap colors are black, blue, pink, red, orange, yellow. 'Cold' or low numbers will be black and blue, 'Hot' or high numbers will be orange and yellow, * @param {Cesium3DTile} tile The tile to colorize relative to last frame's minimum and maximum values of all visible tiles. * @param {FrameState} frameState The frame state. */ Cesium3DTilesetHeatmap.prototype.colorize = function (tile, frameState) { var tilePropertyName = this.tilePropertyName; if ( !defined(tilePropertyName) || !tile.contentAvailable || tile._selectedFrame !== frameState.frameNumber ) { return; } var heatmapValue = getHeatmapValueAndUpdateMinimumMaximum(this, tile); var minimum = this._previousMinimum; var maximum = this._previousMaximum; if (minimum === Number.MAX_VALUE || maximum === -Number.MAX_VALUE) { return; } // Shift the minimum maximum window down to 0 var shiftedMax = maximum - minimum + CesiumMath.EPSILON7; // Prevent divide by 0 var shiftedValue = CesiumMath.clamp(heatmapValue - minimum, 0.0, shiftedMax); // Get position between minimum and maximum and convert that to a position in the color array var zeroToOne = shiftedValue / shiftedMax; var lastIndex = heatmapColors.length - 1.0; var colorPosition = zeroToOne * lastIndex; // Take floor and ceil of the value to get the two colors to lerp between, lerp using the fractional portion var colorPositionFloor = Math.floor(colorPosition); var colorPositionCeil = Math.ceil(colorPosition); var t = colorPosition - colorPositionFloor; var colorZero = heatmapColors[colorPositionFloor]; var colorOne = heatmapColors[colorPositionCeil]; // Perform the lerp var finalColor = Color.clone(Color.WHITE); finalColor.red = CesiumMath.lerp(colorZero.red, colorOne.red, t); finalColor.green = CesiumMath.lerp(colorZero.green, colorOne.green, t); finalColor.blue = CesiumMath.lerp(colorZero.blue, colorOne.blue, t); tile._debugColor = finalColor; }; /** * Resets the tracked minimum maximum values for heatmap colorization. Happens right before tileset traversal. */ Cesium3DTilesetHeatmap.prototype.resetMinimumMaximum = function () { // For heat map colorization var tilePropertyName = this.tilePropertyName; if (defined(tilePropertyName)) { var referenceMinimum = this._referenceMinimum[tilePropertyName]; var referenceMaximum = this._referenceMaximum[tilePropertyName]; var useReference = defined(referenceMinimum) && defined(referenceMaximum); this._previousMinimum = useReference ? referenceMinimum : this._minimum; this._previousMaximum = useReference ? referenceMaximum : this._maximum; this._minimum = Number.MAX_VALUE; this._maximum = -Number.MAX_VALUE; } }; /** * @private */ function Cesium3DTilesetStatistics() { // Rendering statistics this.selected = 0; this.visited = 0; // Loading statistics this.numberOfCommands = 0; this.numberOfAttemptedRequests = 0; this.numberOfPendingRequests = 0; this.numberOfTilesProcessing = 0; this.numberOfTilesWithContentReady = 0; // Number of tiles with content loaded, does not include empty tiles this.numberOfTilesTotal = 0; // Number of tiles in tileset JSON (and other tileset JSON files as they are loaded) this.numberOfLoadedTilesTotal = 0; // Running total of loaded tiles for the lifetime of the session // Features statistics this.numberOfFeaturesSelected = 0; // Number of features rendered this.numberOfFeaturesLoaded = 0; // Number of features in memory this.numberOfPointsSelected = 0; this.numberOfPointsLoaded = 0; this.numberOfTrianglesSelected = 0; // Styling statistics this.numberOfTilesStyled = 0; this.numberOfFeaturesStyled = 0; // Optimization statistics this.numberOfTilesCulledWithChildrenUnion = 0; // Memory statistics this.geometryByteLength = 0; this.texturesByteLength = 0; this.batchTableByteLength = 0; } Cesium3DTilesetStatistics.prototype.clear = function () { this.selected = 0; this.visited = 0; this.numberOfCommands = 0; this.numberOfAttemptedRequests = 0; this.numberOfFeaturesSelected = 0; this.numberOfPointsSelected = 0; this.numberOfTrianglesSelected = 0; this.numberOfTilesStyled = 0; this.numberOfFeaturesStyled = 0; this.numberOfTilesCulledWithChildrenUnion = 0; }; function updatePointAndFeatureCounts(statistics, content, decrement, load) { var contents = content.innerContents; var pointsLength = content.pointsLength; var trianglesLength = content.trianglesLength; var featuresLength = content.featuresLength; var geometryByteLength = content.geometryByteLength; var texturesByteLength = content.texturesByteLength; var batchTableByteLength = content.batchTableByteLength; if (load) { statistics.numberOfFeaturesLoaded += decrement ? -featuresLength : featuresLength; statistics.numberOfPointsLoaded += decrement ? -pointsLength : pointsLength; statistics.geometryByteLength += decrement ? -geometryByteLength : geometryByteLength; statistics.texturesByteLength += decrement ? -texturesByteLength : texturesByteLength; statistics.batchTableByteLength += decrement ? -batchTableByteLength : batchTableByteLength; } else { statistics.numberOfFeaturesSelected += decrement ? -featuresLength : featuresLength; statistics.numberOfPointsSelected += decrement ? -pointsLength : pointsLength; statistics.numberOfTrianglesSelected += decrement ? -trianglesLength : trianglesLength; } if (defined(contents)) { var length = contents.length; for (var i = 0; i < length; ++i) { updatePointAndFeatureCounts(statistics, contents[i], decrement, load); } } } Cesium3DTilesetStatistics.prototype.incrementSelectionCounts = function ( content ) { updatePointAndFeatureCounts(this, content, false, false); }; Cesium3DTilesetStatistics.prototype.incrementLoadCounts = function (content) { updatePointAndFeatureCounts(this, content, false, true); }; Cesium3DTilesetStatistics.prototype.decrementLoadCounts = function (content) { updatePointAndFeatureCounts(this, content, true, true); }; Cesium3DTilesetStatistics.clone = function (statistics, result) { result.selected = statistics.selected; result.visited = statistics.visited; result.numberOfCommands = statistics.numberOfCommands; result.selected = statistics.selected; result.numberOfAttemptedRequests = statistics.numberOfAttemptedRequests; result.numberOfPendingRequests = statistics.numberOfPendingRequests; result.numberOfTilesProcessing = statistics.numberOfTilesProcessing; result.numberOfTilesWithContentReady = statistics.numberOfTilesWithContentReady; result.numberOfTilesTotal = statistics.numberOfTilesTotal; result.numberOfFeaturesSelected = statistics.numberOfFeaturesSelected; result.numberOfFeaturesLoaded = statistics.numberOfFeaturesLoaded; result.numberOfPointsSelected = statistics.numberOfPointsSelected; result.numberOfPointsLoaded = statistics.numberOfPointsLoaded; result.numberOfTrianglesSelected = statistics.numberOfTrianglesSelected; result.numberOfTilesStyled = statistics.numberOfTilesStyled; result.numberOfFeaturesStyled = statistics.numberOfFeaturesStyled; result.numberOfTilesCulledWithChildrenUnion = statistics.numberOfTilesCulledWithChildrenUnion; result.geometryByteLength = statistics.geometryByteLength; result.texturesByteLength = statistics.texturesByteLength; result.batchTableByteLength = statistics.batchTableByteLength; }; /** * @private */ function Cesium3DTileStyleEngine() { this._style = undefined; // The style provided by the user this._styleDirty = false; // true when the style is reassigned this._lastStyleTime = 0; // The "time" when the last style was assigned } Object.defineProperties(Cesium3DTileStyleEngine.prototype, { style: { get: function () { return this._style; }, set: function (value) { if (value === this._style) { return; } this._style = value; this._styleDirty = true; }, }, }); Cesium3DTileStyleEngine.prototype.makeDirty = function () { this._styleDirty = true; }; Cesium3DTileStyleEngine.prototype.resetDirty = function () { this._styleDirty = false; }; Cesium3DTileStyleEngine.prototype.applyStyle = function (tileset) { if (!tileset.ready) { return; } if (defined(this._style) && !this._style.ready) { return; } var styleDirty = this._styleDirty; if (styleDirty) { // Increase "time", so the style is applied to all visible tiles ++this._lastStyleTime; } var lastStyleTime = this._lastStyleTime; var statistics = tileset._statistics; // If a new style was assigned, loop through all the visible tiles; otherwise, loop through // only the tiles that are newly visible, i.e., they are visible this frame, but were not // visible last frame. In many cases, the newly selected tiles list will be short or empty. var tiles = styleDirty ? tileset._selectedTiles : tileset._selectedTilesToStyle; // PERFORMANCE_IDEA: does mouse-over picking basically trash this? We need to style on // pick, for example, because a feature's show may be false. var length = tiles.length; for (var i = 0; i < length; ++i) { var tile = tiles[i]; if (tile.lastStyleTime !== lastStyleTime) { // Apply the style to this tile if it wasn't already applied because: // 1) the user assigned a new style to the tileset // 2) this tile is now visible, but it wasn't visible when the style was first assigned var content = tile.content; tile.lastStyleTime = lastStyleTime; content.applyStyle(this._style); statistics.numberOfFeaturesStyled += content.featuresLength; ++statistics.numberOfTilesStyled; } } }; /** * A {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles tileset}, * used for streaming massive heterogeneous 3D geospatial datasets. * * @alias Cesium3DTileset * @constructor * * @param {Object} options Object with the following properties: * @param {Resource|String|Promise|Promise} options.url The url to a tileset JSON file. * @param {Boolean} [options.show=true] Determines if the tileset will be shown. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] A 4x4 transformation matrix that transforms the tileset's root tile. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from light sources. * @param {Number} [options.maximumScreenSpaceError=16] The maximum screen space error used to drive level of detail refinement. * @param {Number} [options.maximumMemoryUsage=512] The maximum amount of memory in MB that can be used by the tileset. * @param {Boolean} [options.cullWithChildrenBounds=true] Optimization option. Whether to cull tiles using the union of their children bounding volumes. * @param {Boolean} [options.cullRequestsWhileMoving=true] Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * @param {Number} [options.cullRequestsWhileMovingMultiplier=60.0] Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. * @param {Boolean} [options.preloadWhenHidden=false] Preload tiles when tileset.show is false. Loads tiles as if the tileset is visible but does not render them. * @param {Boolean} [options.preloadFlightDestinations=true] Optimization option. Preload tiles at the camera's flight destination while the camera is in flight. * @param {Boolean} [options.preferLeaves=false] Optimization option. Prefer loading of leaves first. * @param {Boolean} [options.dynamicScreenSpaceError=false] Optimization option. Reduce the screen space error for tiles that are further away from the camera. * @param {Number} [options.dynamicScreenSpaceErrorDensity=0.00278] Density used to adjust the dynamic screen space error, similar to fog density. * @param {Number} [options.dynamicScreenSpaceErrorFactor=4.0] A factor used to increase the computed dynamic screen space error. * @param {Number} [options.dynamicScreenSpaceErrorHeightFalloff=0.25] A ratio of the tileset's height at which the density starts to falloff. * @param {Number} [options.progressiveResolutionHeightFraction=0.3] Optimization option. If between (0.0, 0.5], tiles at or above the screen space error for the reduced screen resolution of progressiveResolutionHeightFraction*screenHeight will be prioritized first. This can help get a quick layer of tiles down while full resolution tiles continue to load. * @param {Boolean} [options.foveatedScreenSpaceError=true] Optimization option. Prioritize loading tiles in the center of the screen by temporarily raising the screen space error for tiles around the edge of the screen. Screen space error returns to normal once all the tiles in the center of the screen as determined by the {@link Cesium3DTileset#foveatedConeSize} are loaded. * @param {Number} [options.foveatedConeSize=0.1] Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control the cone size that determines which tiles are deferred. Tiles that are inside this cone are loaded immediately. Tiles outside the cone are potentially deferred based on how far outside the cone they are and their screen space error. This is controlled by {@link Cesium3DTileset#foveatedInterpolationCallback} and {@link Cesium3DTileset#foveatedMinimumScreenSpaceErrorRelaxation}. Setting this to 0.0 means the cone will be the line formed by the camera position and its view direction. Setting this to 1.0 means the cone encompasses the entire field of view of the camera, disabling the effect. * @param {Number} [options.foveatedMinimumScreenSpaceErrorRelaxation=0.0] Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control the starting screen space error relaxation for tiles outside the foveated cone. The screen space error will be raised starting with tileset value up to {@link Cesium3DTileset#maximumScreenSpaceError} based on the provided {@link Cesium3DTileset#foveatedInterpolationCallback}. * @param {Cesium3DTileset.foveatedInterpolationCallback} [options.foveatedInterpolationCallback=Math.lerp] Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control how much to raise the screen space error for tiles outside the foveated cone, interpolating between {@link Cesium3DTileset#foveatedMinimumScreenSpaceErrorRelaxation} and {@link Cesium3DTileset#maximumScreenSpaceError} * @param {Number} [options.foveatedTimeDelay=0.2] Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control how long in seconds to wait after the camera stops moving before deferred tiles start loading in. This time delay prevents requesting tiles around the edges of the screen when the camera is moving. Setting this to 0.0 will immediately request all tiles in any given view. * @param {Boolean} [options.skipLevelOfDetail=false] Optimization option. Determines if level of detail skipping should be applied during the traversal. * @param {Number} [options.baseScreenSpaceError=1024] When skipLevelOfDetail is true, the screen space error that must be reached before skipping levels of detail. * @param {Number} [options.skipScreenSpaceErrorFactor=16] When skipLevelOfDetail is true, a multiplier defining the minimum screen space error to skip. Used in conjunction with skipLevels to determine which tiles to load. * @param {Number} [options.skipLevels=1] When skipLevelOfDetail is true, a constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjunction with skipScreenSpaceErrorFactor to determine which tiles to load. * @param {Boolean} [options.immediatelyLoadDesiredLevelOfDetail=false] When skipLevelOfDetail is true, only tiles that meet the maximum screen space error will ever be downloaded. Skipping factors are ignored and just the desired tiles are loaded. * @param {Boolean} [options.loadSiblings=false] When skipLevelOfDetail is true, determines whether siblings of visible tiles are always downloaded during traversal. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset. * @param {ClassificationType} [options.classificationType] Determines whether terrain, 3D Tiles or both will be classified by this tileset. See {@link Cesium3DTileset#classificationType} for details about restrictions and limitations. * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid determining the size and shape of the globe. * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting. * @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales the diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox. * @param {Cartesian3} [options.lightColor] The light color when shading models. When undefined the scene's light color is used instead. * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. * @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the glTF material's doubleSided property; when false, back face culling is disabled. * @param {String} [options.debugHeatmapTilePropertyName] The tile variable to colorize as a heatmap. All rendered tiles will be colorized relative to each other's specified variable value. * @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering. * @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile. * @param {Boolean} [options.debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile. * @param {Boolean} [options.debugShowContentBoundingVolume=false] For debugging only. When true, renders the bounding volume for each tile's content. * @param {Boolean} [options.debugShowViewerRequestVolume=false] For debugging only. When true, renders the viewer request volume for each tile. * @param {Boolean} [options.debugShowGeometricError=false] For debugging only. When true, draws labels to indicate the geometric error of each tile. * @param {Boolean} [options.debugShowRenderingStatistics=false] For debugging only. When true, draws labels to indicate the number of commands, points, triangles and features for each tile. * @param {Boolean} [options.debugShowMemoryUsage=false] For debugging only. When true, draws labels to indicate the texture and geometry memory in megabytes used by each tile. * @param {Boolean} [options.debugShowUrl=false] For debugging only. When true, draws labels to indicate the url of each tile. * * @exception {DeveloperError} The tileset must be 3D Tiles version 0.0 or 1.0. * * @example * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ * url : 'http://localhost:8002/tilesets/Seattle/tileset.json' * })); * * @example * // Common setting for the skipLevelOfDetail optimization * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ * url : 'http://localhost:8002/tilesets/Seattle/tileset.json', * skipLevelOfDetail : true, * baseScreenSpaceError : 1024, * skipScreenSpaceErrorFactor : 16, * skipLevels : 1, * immediatelyLoadDesiredLevelOfDetail : false, * loadSiblings : false, * cullWithChildrenBounds : true * })); * * @example * // Common settings for the dynamicScreenSpaceError optimization * var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({ * url : 'http://localhost:8002/tilesets/Seattle/tileset.json', * dynamicScreenSpaceError : true, * dynamicScreenSpaceErrorDensity : 0.00278, * dynamicScreenSpaceErrorFactor : 4.0, * dynamicScreenSpaceErrorHeightFalloff : 0.25 * })); * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification|3D Tiles specification} */ function Cesium3DTileset(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); Check.defined("options.url", options.url); //>>includeEnd('debug'); this._url = undefined; this._basePath = undefined; this._root = undefined; this._resource = undefined; this._asset = undefined; // Metadata for the entire tileset this._properties = undefined; // Metadata for per-model/point/etc properties this._geometricError = undefined; // Geometric error when the tree is not rendered at all this._extensionsUsed = undefined; this._extensions = undefined; this._gltfUpAxis = undefined; this._cache = new Cesium3DTilesetCache(); this._processingQueue = []; this._selectedTiles = []; this._emptyTiles = []; this._requestedTiles = []; this._selectedTilesToStyle = []; this._loadTimestamp = undefined; this._timeSinceLoad = 0.0; this._updatedVisibilityFrame = 0; this._updatedModelMatrixFrame = 0; this._modelMatrixChanged = false; this._previousModelMatrix = undefined; this._extras = undefined; this._credits = undefined; this._cullWithChildrenBounds = defaultValue( options.cullWithChildrenBounds, true ); this._allTilesAdditive = true; this._hasMixedContent = false; this._stencilClearCommand = undefined; this._backfaceCommands = new ManagedArray(); this._maximumScreenSpaceError = defaultValue( options.maximumScreenSpaceError, 16 ); this._maximumMemoryUsage = defaultValue(options.maximumMemoryUsage, 512); this._styleEngine = new Cesium3DTileStyleEngine(); this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY); this._statistics = new Cesium3DTilesetStatistics(); this._statisticsLast = new Cesium3DTilesetStatistics(); this._statisticsPerPass = new Array(Cesium3DTilePass$1.NUMBER_OF_PASSES); for (var i = 0; i < Cesium3DTilePass$1.NUMBER_OF_PASSES; ++i) { this._statisticsPerPass[i] = new Cesium3DTilesetStatistics(); } this._requestedTilesInFlight = []; this._maximumPriority = { foveatedFactor: -Number.MAX_VALUE, depth: -Number.MAX_VALUE, distance: -Number.MAX_VALUE, reverseScreenSpaceError: -Number.MAX_VALUE, }; this._minimumPriority = { foveatedFactor: Number.MAX_VALUE, depth: Number.MAX_VALUE, distance: Number.MAX_VALUE, reverseScreenSpaceError: Number.MAX_VALUE, }; this._heatmap = new Cesium3DTilesetHeatmap( options.debugHeatmapTilePropertyName ); /** * Optimization option. Don't request tiles that will likely be unused when they come back because of the camera's movement. This optimization only applies to stationary tilesets. * * @type {Boolean} * @default true */ this.cullRequestsWhileMoving = defaultValue( options.cullRequestsWhileMoving, true ); this._cullRequestsWhileMoving = false; /** * Optimization option. Multiplier used in culling requests while moving. Larger is more aggressive culling, smaller less aggressive culling. * * @type {Number} * @default 60.0 */ this.cullRequestsWhileMovingMultiplier = defaultValue( options.cullRequestsWhileMovingMultiplier, 60.0 ); /** * Optimization option. If between (0.0, 0.5], tiles at or above the screen space error for the reduced screen resolution of progressiveResolutionHeightFraction*screenHeight will be prioritized first. This can help get a quick layer of tiles down while full resolution tiles continue to load. * * @type {Number} * @default 0.3 */ this.progressiveResolutionHeightFraction = CesiumMath.clamp( defaultValue(options.progressiveResolutionHeightFraction, 0.3), 0.0, 0.5 ); /** * Optimization option. Prefer loading of leaves first. * * @type {Boolean} * @default false */ this.preferLeaves = defaultValue(options.preferLeaves, false); this._tilesLoaded = false; this._initialTilesLoaded = false; this._tileDebugLabels = undefined; this._readyPromise = when.defer(); this._classificationType = options.classificationType; this._ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); this._initialClippingPlanesOriginMatrix = Matrix4.IDENTITY; // Computed from the tileset JSON. this._clippingPlanesOriginMatrix = undefined; // Combines the above with any run-time transforms. this._clippingPlanesOriginMatrixDirty = true; //【hongguo】 田洪果 倾斜模型编辑修改 this.modelEditor = defaultValue(options.modelEditor, { 'IsYaPing': [false, false, false, false],//[是否开启编辑,是否开启压平,是否开启裁剪,是否开启淹没] 'editVar':[false, false, false, false],//[是否开启裁剪外部,是否开启淹没全局,] 'floodVar': [0, 0, 0, 0],//[基础淹没高度,当前淹没高度,最大淹没高度,默认高度差(最大淹没高度 - 基础淹没高度)] 'floodColor': [0.0, 0.0, 0.0, 0.5],//[淹没颜色的r(0-1之间),淹没颜色的g,淹没颜色的b,淹没混合系数(建议取值范围0.3-0.7)] 'heightVar':[0,0], //基础压平高度,调整压平高度值 'enable': defaultValue(options.hasEditor,true) }); //【hongguo】 田洪果 倾斜模型编辑修改 this.buildStyle = defaultValue(options.buildStyle, false); //【【hongguo】】 田洪果 建筑物特效 /** * Preload tiles when tileset.show is false. Loads tiles as if the tileset is visible but does not render them. * * @type {Boolean} * @default false */ this.preloadWhenHidden = defaultValue(options.preloadWhenHidden, false); /** * Optimization option. Fetch tiles at the camera's flight destination while the camera is in flight. * * @type {Boolean} * @default true */ this.preloadFlightDestinations = defaultValue( options.preloadFlightDestinations, true ); this._pass = undefined; // Cesium3DTilePass /** * Optimization option. Whether the tileset should refine based on a dynamic screen space error. Tiles that are further * away will be rendered with lower detail than closer tiles. This improves performance by rendering fewer * tiles and making less requests, but may result in a slight drop in visual quality for tiles in the distance. * The algorithm is biased towards "street views" where the camera is close to the ground plane of the tileset and looking * at the horizon. In addition results are more accurate for tightly fitting bounding volumes like box and region. * * @type {Boolean} * @default false */ this.dynamicScreenSpaceError = defaultValue( options.dynamicScreenSpaceError, false ); /** * Optimization option. Prioritize loading tiles in the center of the screen by temporarily raising the * screen space error for tiles around the edge of the screen. Screen space error returns to normal once all * the tiles in the center of the screen as determined by the {@link Cesium3DTileset#foveatedConeSize} are loaded. * * @type {Boolean} * @default true */ this.foveatedScreenSpaceError = defaultValue( options.foveatedScreenSpaceError, true ); this._foveatedConeSize = defaultValue(options.foveatedConeSize, 0.1); this._foveatedMinimumScreenSpaceErrorRelaxation = defaultValue( options.foveatedMinimumScreenSpaceErrorRelaxation, 0.0 ); /** * Gets or sets a callback to control how much to raise the screen space error for tiles outside the foveated cone, * interpolating between {@link Cesium3DTileset#foveatedMinimumScreenSpaceErrorRelaxation} and {@link Cesium3DTileset#maximumScreenSpaceError}. * * @type {Cesium3DTileset.foveatedInterpolationCallback} */ this.foveatedInterpolationCallback = defaultValue( options.foveatedInterpolationCallback, CesiumMath.lerp ); /** * Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control * how long in seconds to wait after the camera stops moving before deferred tiles start loading in. * This time delay prevents requesting tiles around the edges of the screen when the camera is moving. * Setting this to 0.0 will immediately request all tiles in any given view. * * @type {Number} * @default 0.2 */ this.foveatedTimeDelay = defaultValue(options.foveatedTimeDelay, 0.2); /** * A scalar that determines the density used to adjust the dynamic screen space error, similar to {@link Fog}. Increasing this * value has the effect of increasing the maximum screen space error for all tiles, but in a non-linear fashion. * The error starts at 0.0 and increases exponentially until a midpoint is reached, and then approaches 1.0 asymptotically. * This has the effect of keeping high detail in the closer tiles and lower detail in the further tiles, with all tiles * beyond a certain distance all roughly having an error of 1.0. *

* The dynamic error is in the range [0.0, 1.0) and is multiplied by dynamicScreenSpaceErrorFactor to produce the * final dynamic error. This dynamic error is then subtracted from the tile's actual screen space error. *

*

* Increasing dynamicScreenSpaceErrorDensity has the effect of moving the error midpoint closer to the camera. * It is analogous to moving fog closer to the camera. *

* * @type {Number} * @default 0.00278 */ this.dynamicScreenSpaceErrorDensity = 0.00278; /** * A factor used to increase the screen space error of tiles for dynamic screen space error. As this value increases less tiles * are requested for rendering and tiles in the distance will have lower detail. If set to zero, the feature will be disabled. * * @type {Number} * @default 4.0 */ this.dynamicScreenSpaceErrorFactor = 4.0; /** * A ratio of the tileset's height at which the density starts to falloff. If the camera is below this height the * full computed density is applied, otherwise the density falls off. This has the effect of higher density at * street level views. *

* Valid values are between 0.0 and 1.0. *

* * @type {Number} * @default 0.25 */ this.dynamicScreenSpaceErrorHeightFalloff = 0.25; this._dynamicScreenSpaceErrorComputedDensity = 0.0; // Updated based on the camera position and direction /** * Determines whether the tileset casts or receives shadows from light sources. *

* Enabling shadows has a performance impact. A tileset that casts shadows must be rendered twice, once from the camera and again from the light's point of view. *

*

* Shadows are rendered only when {@link Viewer#shadows} is true. *

* * @type {ShadowMode} * @default ShadowMode.ENABLED */ this.shadows = defaultValue(options.shadows, ShadowMode$1.ENABLED); /** * Determines if the tileset will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * Defines how per-feature colors set from the Cesium API or declarative styling blend with the source colors from * the original feature, e.g. glTF material or per-point color in the tile. * * @type {Cesium3DTileColorBlendMode} * @default Cesium3DTileColorBlendMode.HIGHLIGHT */ this.colorBlendMode = Cesium3DTileColorBlendMode$1.HIGHLIGHT; /** * Defines the value used to linearly interpolate between the source color and feature color when the {@link Cesium3DTileset#colorBlendMode} is MIX. * A value of 0.0 results in the source color while a value of 1.0 results in the feature color, with any value in-between * resulting in a mix of the source color and feature color. * * @type {Number} * @default 0.5 */ this.colorBlendAmount = 0.5; /** * Options for controlling point size based on geometric error and eye dome lighting. * @type {PointCloudShading} */ this.pointCloudShading = new PointCloudShading(options.pointCloudShading); this._pointCloudEyeDomeLighting = new PointCloudEyeDomeLighting(); /** * The event fired to indicate progress of loading new tiles. This event is fired when a new tile * is requested, when a requested tile is finished downloading, and when a downloaded tile has been * processed and is ready to render. *

* The number of pending tile requests, numberOfPendingRequests, and number of tiles * processing, numberOfTilesProcessing are passed to the event listener. *

*

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * tileset.loadProgress.addEventListener(function(numberOfPendingRequests, numberOfTilesProcessing) { * if ((numberOfPendingRequests === 0) && (numberOfTilesProcessing === 0)) { * console.log('Stopped loading'); * return; * } * * console.log('Loading: requests: ' + numberOfPendingRequests + ', processing: ' + numberOfTilesProcessing); * }); */ this.loadProgress = new Event(); /** * The event fired to indicate that all tiles that meet the screen space error this frame are loaded. The tileset * is completely loaded for this view. *

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * tileset.allTilesLoaded.addEventListener(function() { * console.log('All tiles are loaded'); * }); * * @see Cesium3DTileset#tilesLoaded */ this.allTilesLoaded = new Event(); /** * The event fired to indicate that all tiles that meet the screen space error this frame are loaded. This event * is fired once when all tiles in the initial view are loaded. *

* This event is fired at the end of the frame after the scene is rendered. *

* * @type {Event} * @default new Event() * * @example * tileset.initialTilesLoaded.addEventListener(function() { * console.log('Initial tiles are loaded'); * }); * * @see Cesium3DTileset#allTilesLoaded */ this.initialTilesLoaded = new Event(); /** * The event fired to indicate that a tile's content was loaded. *

* The loaded {@link Cesium3DTile} is passed to the event listener. *

*

* This event is fired during the tileset traversal while the frame is being rendered * so that updates to the tile take effect in the same frame. Do not create or modify * Cesium entities or primitives during the event listener. *

* * @type {Event} * @default new Event() * * @example * tileset.tileLoad.addEventListener(function(tile) { * console.log('A tile was loaded.'); * }); */ this.tileLoad = new Event(); /** * The event fired to indicate that a tile's content was unloaded. *

* The unloaded {@link Cesium3DTile} is passed to the event listener. *

*

* This event is fired immediately before the tile's content is unloaded while the frame is being * rendered so that the event listener has access to the tile's content. Do not create * or modify Cesium entities or primitives during the event listener. *

* * @type {Event} * @default new Event() * * @example * tileset.tileUnload.addEventListener(function(tile) { * console.log('A tile was unloaded from the cache.'); * }); * * @see Cesium3DTileset#maximumMemoryUsage * @see Cesium3DTileset#trimLoadedTiles */ this.tileUnload = new Event(); /** * The event fired to indicate that a tile's content failed to load. *

* If there are no event listeners, error messages will be logged to the console. *

*

* The error object passed to the listener contains two properties: *

    *
  • url: the url of the failed tile.
  • *
  • message: the error message.
  • *
* * @type {Event} * @default new Event() * * @example * tileset.tileFailed.addEventListener(function(error) { * console.log('An error occurred loading tile: ' + error.url); * console.log('Error: ' + error.message); * }); */ this.tileFailed = new Event(); /** * This event fires once for each visible tile in a frame. This can be used to manually * style a tileset. *

* The visible {@link Cesium3DTile} is passed to the event listener. *

*

* This event is fired during the tileset traversal while the frame is being rendered * so that updates to the tile take effect in the same frame. Do not create or modify * Cesium entities or primitives during the event listener. *

* * @type {Event} * @default new Event() * * @example * tileset.tileVisible.addEventListener(function(tile) { * if (tile.content instanceof Cesium.Batched3DModel3DTileContent) { * console.log('A Batched 3D Model tile is visible.'); * } * }); * * @example * // Apply a red style and then manually set random colors for every other feature when the tile becomes visible. * tileset.style = new Cesium.Cesium3DTileStyle({ * color : 'color("red")' * }); * tileset.tileVisible.addEventListener(function(tile) { * var content = tile.content; * var featuresLength = content.featuresLength; * for (var i = 0; i < featuresLength; i+=2) { * content.getFeature(i).color = Cesium.Color.fromRandom(); * } * }); */ this.tileVisible = new Event(); /** * Optimization option. Determines if level of detail skipping should be applied during the traversal. *

* The common strategy for replacement-refinement traversal is to store all levels of the tree in memory and require * all children to be loaded before the parent can refine. With this optimization levels of the tree can be skipped * entirely and children can be rendered alongside their parents. The tileset requires significantly less memory when * using this optimization. *

* * @type {Boolean} * @default false */ this.skipLevelOfDetail = defaultValue(options.skipLevelOfDetail, false); this._skipLevelOfDetail = this.skipLevelOfDetail; this._disableSkipLevelOfDetail = false; /** * The screen space error that must be reached before skipping levels of detail. *

* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. *

* * @type {Number} * @default 1024 */ this.baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 1024); /** * Multiplier defining the minimum screen space error to skip. * For example, if a tile has screen space error of 100, no tiles will be loaded unless they * are leaves or have a screen space error <= 100 / skipScreenSpaceErrorFactor. *

* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. *

* * @type {Number} * @default 16 */ this.skipScreenSpaceErrorFactor = defaultValue( options.skipScreenSpaceErrorFactor, 16 ); /** * Constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. * For example, if a tile is level 1, no tiles will be loaded unless they are at level greater than 2. *

* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. *

* * @type {Number} * @default 1 */ this.skipLevels = defaultValue(options.skipLevels, 1); /** * When true, only tiles that meet the maximum screen space error will ever be downloaded. * Skipping factors are ignored and just the desired tiles are loaded. *

* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. *

* * @type {Boolean} * @default false */ this.immediatelyLoadDesiredLevelOfDetail = defaultValue( options.immediatelyLoadDesiredLevelOfDetail, false ); /** * Determines whether siblings of visible tiles are always downloaded during traversal. * This may be useful for ensuring that tiles are already available when the viewer turns left/right. *

* Only used when {@link Cesium3DTileset#skipLevelOfDetail} is true. *

* * @type {Boolean} * @default false */ this.loadSiblings = defaultValue(options.loadSiblings, false); this._clippingPlanes = undefined; this.clippingPlanes = options.clippingPlanes; this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0); Cartesian2.clone( options.imageBasedLightingFactor, this._imageBasedLightingFactor ); /** * The light color when shading models. When undefined the scene's light color is used instead. *

* For example, disabling additional light sources by setting model.imageBasedLightingFactor = new Cartesian2(0.0, 0.0) will make the * model much darker. Here, increasing the intensity of the light source will make the model brighter. *

* * @type {Cartesian3} * @default undefined */ this.lightColor = options.lightColor; /** * The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map. * This is used when {@link Cesium3DTileset#specularEnvironmentMaps} and {@link Cesium3DTileset#sphericalHarmonicCoefficients} are not defined. * * @type Number * * @default 0.2 * */ this.luminanceAtZenith = defaultValue(options.luminanceAtZenith, 0.2); /** * The third order spherical harmonic coefficients used for the diffuse color of image-based lighting. When undefined, a diffuse irradiance * computed from the atmosphere color is used. *

* There are nine Cartesian3 coefficients. * The order of the coefficients is: L00, L1-1, L10, L11, L2-2, L2-1, L20, L21, L22 *

* * These values can be obtained by preprocessing the environment map using the cmgen tool of * {@link https://github.com/google/filament/releases|Google's Filament project}. This will also generate a KTX file that can be * supplied to {@link Cesium3DTileset#specularEnvironmentMaps}. * * @type {Cartesian3[]} * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo} * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps} */ this.sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients; /** * A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps. * * @demo {@link https://sandcastle.cesium.com/index.html?src=Image-Based Lighting.html|Sandcastle Image Based Lighting Demo} * @type {String} * @see Cesium3DTileset#sphericalHarmonicCoefficients */ this.specularEnvironmentMaps = options.specularEnvironmentMaps; /** * Whether to cull back-facing geometry. When true, back face culling is determined * by the glTF material's doubleSided property; when false, back face culling is disabled. * * @type {Boolean} * @default true */ this.backFaceCulling = defaultValue(options.backFaceCulling, true); /** * This property is for debugging only; it is not optimized for production use. *

* Determines if only the tiles from last frame should be used for rendering. This * effectively "freezes" the tileset to the previous frame so it is possible to zoom * out and see what was rendered. *

* * @type {Boolean} * @default false */ this.debugFreezeFrame = defaultValue(options.debugFreezeFrame, false); /** * This property is for debugging only; it is not optimized for production use. *

* When true, assigns a random color to each tile. This is useful for visualizing * what features belong to what tiles, especially with additive refinement where features * from parent tiles may be interleaved with features from child tiles. *

* * @type {Boolean} * @default false */ this.debugColorizeTiles = defaultValue(options.debugColorizeTiles, false); /** * This property is for debugging only; it is not optimized for production use. *

* When true, renders each tile's content as a wireframe. *

* * @type {Boolean} * @default false */ this.debugWireframe = defaultValue(options.debugWireframe, false); /** * This property is for debugging only; it is not optimized for production use. *

* When true, renders the bounding volume for each visible tile. The bounding volume is * white if the tile has a content bounding volume or is empty; otherwise, it is red. Tiles that don't meet the * screen space error and are still refining to their descendants are yellow. *

* * @type {Boolean} * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * This property is for debugging only; it is not optimized for production use. *

* When true, renders the bounding volume for each visible tile's content. The bounding volume is * blue if the tile has a content bounding volume; otherwise it is red. *

* * @type {Boolean} * @default false */ this.debugShowContentBoundingVolume = defaultValue( options.debugShowContentBoundingVolume, false ); /** * This property is for debugging only; it is not optimized for production use. *

* When true, renders the viewer request volume for each tile. *

* * @type {Boolean} * @default false */ this.debugShowViewerRequestVolume = defaultValue( options.debugShowViewerRequestVolume, false ); this._tileDebugLabels = undefined; this.debugPickedTileLabelOnly = false; this.debugPickedTile = undefined; this.debugPickPosition = undefined; /** * This property is for debugging only; it is not optimized for production use. *

* When true, draws labels to indicate the geometric error of each tile. *

* * @type {Boolean} * @default false */ this.debugShowGeometricError = defaultValue( options.debugShowGeometricError, false ); /** * This property is for debugging only; it is not optimized for production use. *

* When true, draws labels to indicate the number of commands, points, triangles and features of each tile. *

* * @type {Boolean} * @default false */ this.debugShowRenderingStatistics = defaultValue( options.debugShowRenderingStatistics, false ); /** * This property is for debugging only; it is not optimized for production use. *

* When true, draws labels to indicate the geometry and texture memory usage of each tile. *

* * @type {Boolean} * @default false */ this.debugShowMemoryUsage = defaultValue(options.debugShowMemoryUsage, false); /** * This property is for debugging only; it is not optimized for production use. *

* When true, draws labels to indicate the url of each tile. *

* * @type {Boolean} * @default false */ this.debugShowUrl = defaultValue(options.debugShowUrl, false); var that = this; var resource; when(options.url) .then(function (url) { var basePath; resource = Resource.createIfNeeded(url); that._resource = resource; // ion resources have a credits property we can use for additional attribution. that._credits = resource.credits; if (resource.extension === "json") { basePath = resource.getBaseUri(true); } else if (resource.isDataUri) { basePath = ""; } that._url = resource.url; that._basePath = basePath; return Cesium3DTileset.loadJson(resource); }) .then(function (tilesetJson) { that._root = that.loadTileset(resource, tilesetJson); var gltfUpAxis = defined(tilesetJson.asset.gltfUpAxis) ? Axis$1.fromName(tilesetJson.asset.gltfUpAxis) : Axis$1.Y; var asset = tilesetJson.asset; that._asset = asset; that._properties = tilesetJson.properties; that._geometricError = tilesetJson.geometricError; that._extensionsUsed = tilesetJson.extensionsUsed; that._extensions = tilesetJson.extensions; that._gltfUpAxis = gltfUpAxis; that._extras = tilesetJson.extras; var extras = asset.extras; if ( defined(extras) && defined(extras.cesium) && defined(extras.cesium.credits) ) { var extraCredits = extras.cesium.credits; var credits = that._credits; if (!defined(credits)) { credits = []; that._credits = credits; } for (var i = 0; i < extraCredits.length; ++i) { var credit = extraCredits[i]; credits.push(new Credit(credit.html, credit.showOnScreen)); } } // Save the original, untransformed bounding volume position so we can apply // the tile transform and model matrix at run time var boundingVolume = that._root.createBoundingVolume( tilesetJson.root.boundingVolume, Matrix4.IDENTITY ); var clippingPlanesOrigin = boundingVolume.boundingSphere.center; // If this origin is above the surface of the earth // we want to apply an ENU orientation as our best guess of orientation. // Otherwise, we assume it gets its position/orientation completely from the // root tile transform and the tileset's model matrix var originCartographic = that._ellipsoid.cartesianToCartographic( clippingPlanesOrigin ); if ( defined(originCartographic) && originCartographic.height > ApproximateTerrainHeights._defaultMinTerrainHeight ) { that._initialClippingPlanesOriginMatrix = Transforms.eastNorthUpToFixedFrame( clippingPlanesOrigin ); } that._clippingPlanesOriginMatrix = Matrix4.clone( that._initialClippingPlanesOriginMatrix ); that._readyPromise.resolve(that); }) .otherwise(function (error) { that._readyPromise.reject(error); }); } Object.defineProperties(Cesium3DTileset.prototype, { /** * NOTE: This getter exists so that `Picking.js` can differentiate between * PrimitiveCollection and Cesium3DTileset objects without inflating * the size of the module via `instanceof Cesium3DTileset` * @private */ isCesium3DTileset: { get: function () { return true; }, }, /** * Gets the tileset's asset object property, which contains metadata about the tileset. *

* See the {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification#reference-asset|asset schema reference} * in the 3D Tiles spec for the full set of properties. *

* * @memberof Cesium3DTileset.prototype * * @type {Object} * @readonly * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. */ asset: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); return this._asset; }, }, /** * Gets the tileset's extensions object property. * * @memberof Cesium3DTileset.prototype * * @type {Object} * @readonly * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. */ extensions: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); return this._extensions; }, }, /** * The {@link ClippingPlaneCollection} used to selectively disable rendering the tileset. * * @memberof Cesium3DTileset.prototype * * @type {ClippingPlaneCollection} */ clippingPlanes: { get: function () { return this._clippingPlanes; }, set: function (value) { ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes"); }, }, /** * Gets the tileset's properties dictionary object, which contains metadata about per-feature properties. *

* See the {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification#reference-properties|properties schema reference} * in the 3D Tiles spec for the full set of properties. *

* * @memberof Cesium3DTileset.prototype * * @type {Object} * @readonly * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. * * @example * console.log('Maximum building height: ' + tileset.properties.height.maximum); * console.log('Minimum building height: ' + tileset.properties.height.minimum); * * @see Cesium3DTileFeature#getProperty * @see Cesium3DTileFeature#setProperty */ properties: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); return this._properties; }, }, /** * When true, the tileset's root tile is loaded and the tileset is ready to render. * This is set to true right before {@link Cesium3DTileset#readyPromise} is resolved. * * @memberof Cesium3DTileset.prototype * * @type {Boolean} * @readonly * * @default false */ ready: { get: function () { return defined(this._root); }, }, /** * Gets the promise that will be resolved when the tileset's root tile is loaded and the tileset is ready to render. *

* This promise is resolved at the end of the frame before the first frame the tileset is rendered in. *

* * @memberof Cesium3DTileset.prototype * * @type {Promise.} * @readonly * * @example * tileset.readyPromise.then(function(tileset) { * // tile.properties is not defined until readyPromise resolves. * var properties = tileset.properties; * if (Cesium.defined(properties)) { * for (var name in properties) { * console.log(properties[name]); * } * } * }); */ readyPromise: { get: function () { return this._readyPromise.promise; }, }, /** * When true, all tiles that meet the screen space error this frame are loaded. The tileset is * completely loaded for this view. * * @memberof Cesium3DTileset.prototype * * @type {Boolean} * @readonly * * @default false * * @see Cesium3DTileset#allTilesLoaded */ tilesLoaded: { get: function () { return this._tilesLoaded; }, }, /** * The resource used to fetch the tileset JSON file * * @memberof Cesium3DTileset.prototype * * @type {Resource} * @readonly */ resource: { get: function () { return this._resource; }, }, /** * The base path that non-absolute paths in tileset JSON file are relative to. * * @memberof Cesium3DTileset.prototype * * @type {String} * @readonly * @deprecated */ basePath: { get: function () { deprecationWarning( "Cesium3DTileset.basePath", "Cesium3DTileset.basePath has been deprecated. All tiles are relative to the url of the tileset JSON file that contains them. Use the url property instead." ); return this._basePath; }, }, /** * The style, defined using the * {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language}, * applied to each feature in the tileset. *

* Assign undefined to remove the style, which will restore the visual * appearance of the tileset to its default when no style was applied. *

*

* The style is applied to a tile before the {@link Cesium3DTileset#tileVisible} * event is raised, so code in tileVisible can manually set a feature's * properties (e.g. color and show) after the style is applied. When * a new style is assigned any manually set properties are overwritten. *

* * @memberof Cesium3DTileset.prototype * * @type {Cesium3DTileStyle|undefined} * * @default undefined * * @example * tileset.style = new Cesium.Cesium3DTileStyle({ * color : { * conditions : [ * ['${Height} >= 100', 'color("purple", 0.5)'], * ['${Height} >= 50', 'color("red")'], * ['true', 'color("blue")'] * ] * }, * show : '${Height} > 0', * meta : { * description : '"Building id ${id} has height ${Height}."' * } * }); * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification/Styling|3D Tiles Styling language} */ style: { get: function () { return this._styleEngine.style; }, set: function (value) { this._styleEngine.style = value; }, }, /** * The maximum screen space error used to drive level of detail refinement. This value helps determine when a tile * refines to its descendants, and therefore plays a major role in balancing performance with visual quality. *

* A tile's screen space error is roughly equivalent to the number of pixels wide that would be drawn if a sphere with a * radius equal to the tile's geometric error were rendered at the tile's position. If this value exceeds * maximumScreenSpaceError the tile refines to its descendants. *

*

* Depending on the tileset, maximumScreenSpaceError may need to be tweaked to achieve the right balance. * Higher values provide better performance but lower visual quality. *

* * @memberof Cesium3DTileset.prototype * * @type {Number} * @default 16 * * @exception {DeveloperError} maximumScreenSpaceError must be greater than or equal to zero. */ maximumScreenSpaceError: { get: function () { return this._maximumScreenSpaceError; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals( "maximumScreenSpaceError", value, 0 ); //>>includeEnd('debug'); this._maximumScreenSpaceError = value; }, }, /** * The maximum amount of GPU memory (in MB) that may be used to cache tiles. This value is estimated from * geometry, textures, and batch table textures of loaded tiles. For point clouds, this value also * includes per-point metadata. *

* Tiles not in view are unloaded to enforce this. *

*

* If decreasing this value results in unloading tiles, the tiles are unloaded the next frame. *

*

* If tiles sized more than maximumMemoryUsage are needed * to meet the desired screen space error, determined by {@link Cesium3DTileset#maximumScreenSpaceError}, * for the current view, then the memory usage of the tiles loaded will exceed * maximumMemoryUsage. For example, if the maximum is 256 MB, but * 300 MB of tiles are needed to meet the screen space error, then 300 MB of tiles may be loaded. When * these tiles go out of view, they will be unloaded. *

* * @memberof Cesium3DTileset.prototype * * @type {Number} * @default 512 * * @exception {DeveloperError} maximumMemoryUsage must be greater than or equal to zero. * @see Cesium3DTileset#totalMemoryUsageInBytes */ maximumMemoryUsage: { get: function () { return this._maximumMemoryUsage; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("value", value, 0); //>>includeEnd('debug'); this._maximumMemoryUsage = value; }, }, /** * The root tile. * * @memberOf Cesium3DTileset.prototype * * @type {Cesium3DTile} * @readonly * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. */ root: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); return this._root; }, }, /** * The tileset's bounding sphere. * * @memberof Cesium3DTileset.prototype * * @type {BoundingSphere} * @readonly * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. * * @example * var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ * url : 'http://localhost:8002/tilesets/Seattle/tileset.json' * })); * * tileset.readyPromise.then(function(tileset) { * // Set the camera to view the newly added tileset * viewer.camera.viewBoundingSphere(tileset.boundingSphere, new Cesium.HeadingPitchRange(0, -0.5, 0)); * }); */ boundingSphere: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); this._root.updateTransform(this._modelMatrix); return this._root.boundingSphere; }, }, /** * A 4x4 transformation matrix that transforms the entire tileset. * * @memberof Cesium3DTileset.prototype * * @type {Matrix4} * @default Matrix4.IDENTITY * * @example * // Adjust a tileset's height from the globe's surface. * var heightOffset = 20.0; * var boundingSphere = tileset.boundingSphere; * var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center); * var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0); * var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset); * var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3()); * tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation); */ modelMatrix: { get: function () { return this._modelMatrix; }, set: function (value) { this._modelMatrix = Matrix4.clone(value, this._modelMatrix); }, }, /** * Returns the time, in milliseconds, since the tileset was loaded and first updated. * * @memberof Cesium3DTileset.prototype * * @type {Number} * @readonly */ timeSinceLoad: { get: function () { return this._timeSinceLoad; }, }, /** * The total amount of GPU memory in bytes used by the tileset. This value is estimated from * geometry, texture, and batch table textures of loaded tiles. For point clouds, this value also * includes per-point metadata. * * @memberof Cesium3DTileset.prototype * * @type {Number} * @readonly * * @see Cesium3DTileset#maximumMemoryUsage */ totalMemoryUsageInBytes: { get: function () { var statistics = this._statistics; return ( statistics.texturesByteLength + statistics.geometryByteLength + statistics.batchTableByteLength ); }, }, /** * @private */ clippingPlanesOriginMatrix: { get: function () { if (!defined(this._clippingPlanesOriginMatrix)) { return Matrix4.IDENTITY; } if (this._clippingPlanesOriginMatrixDirty) { Matrix4.multiply( this.root.computedTransform, this._initialClippingPlanesOriginMatrix, this._clippingPlanesOriginMatrix ); this._clippingPlanesOriginMatrixDirty = false; } return this._clippingPlanesOriginMatrix; }, }, /** * @private */ styleEngine: { get: function () { return this._styleEngine; }, }, /** * @private */ statistics: { get: function () { return this._statistics; }, }, /** * Determines whether terrain, 3D Tiles or both will be classified by this tileset. *

* This option is only applied to tilesets containing batched 3D models, geometry data, or vector data. Even when undefined, vector data and geometry data * must render as classifications and will default to rendering on both terrain and other 3D Tiles tilesets. *

*

* When enabled for batched 3D model tilesets, there are a few requirements/limitations on the glTF: *

    *
  • POSITION and _BATCHID semantics are required.
  • *
  • All indices with the same batch id must occupy contiguous sections of the index buffer.
  • *
  • All shaders and techniques are ignored. The generated shader simply multiplies the position by the model-view-projection matrix.
  • *
  • The only supported extensions are CESIUM_RTC and WEB3D_quantized_attributes.
  • *
  • Only one node is supported.
  • *
  • Only one mesh per node is supported.
  • *
  • Only one primitive per mesh is supported.
  • *
*

* * @memberof Cesium3DTileset.prototype * * @type {ClassificationType} * @default undefined * * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. * @readonly */ classificationType: { get: function () { return this._classificationType; }, }, /** * Gets an ellipsoid describing the shape of the globe. * * @memberof Cesium3DTileset.prototype * * @type {Ellipsoid} * @readonly */ ellipsoid: { get: function () { return this._ellipsoid; }, }, /** * Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control the cone size that determines which tiles are deferred. * Tiles that are inside this cone are loaded immediately. Tiles outside the cone are potentially deferred based on how far outside the cone they are and {@link Cesium3DTileset#foveatedInterpolationCallback} and {@link Cesium3DTileset#foveatedMinimumScreenSpaceErrorRelaxation}. * Setting this to 0.0 means the cone will be the line formed by the camera position and its view direction. Setting this to 1.0 means the cone encompasses the entire field of view of the camera, essentially disabling the effect. * * @memberof Cesium3DTileset.prototype * * @type {Number} * @default 0.3 */ foveatedConeSize: { get: function () { return this._foveatedConeSize; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals("foveatedConeSize", value, 0.0); Check.typeOf.number.lessThanOrEquals("foveatedConeSize", value, 1.0); //>>includeEnd('debug'); this._foveatedConeSize = value; }, }, /** * Optimization option. Used when {@link Cesium3DTileset#foveatedScreenSpaceError} is true to control the starting screen space error relaxation for tiles outside the foveated cone. * The screen space error will be raised starting with this value up to {@link Cesium3DTileset#maximumScreenSpaceError} based on the provided {@link Cesium3DTileset#foveatedInterpolationCallback}. * * @memberof Cesium3DTileset.prototype * * @type {Number} * @default 0.0 */ foveatedMinimumScreenSpaceErrorRelaxation: { get: function () { return this._foveatedMinimumScreenSpaceErrorRelaxation; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.number.greaterThanOrEquals( "foveatedMinimumScreenSpaceErrorRelaxation", value, 0.0 ); Check.typeOf.number.lessThanOrEquals( "foveatedMinimumScreenSpaceErrorRelaxation", value, this.maximumScreenSpaceError ); //>>includeEnd('debug'); this._foveatedMinimumScreenSpaceErrorRelaxation = value; }, }, /** * Returns the extras property at the top-level of the tileset JSON, which contains application specific metadata. * Returns undefined if extras does not exist. * * @memberof Cesium3DTileset.prototype * * @exception {DeveloperError} The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true. * * @type {*} * @readonly * * @see {@link https://github.com/CesiumGS/3d-tiles/tree/master/specification#specifying-extensions-and-application-specific-extras|Extras in the 3D Tiles specification.} */ extras: { get: function () { //>>includeStart('debug', pragmas.debug); if (!this.ready) { throw new DeveloperError( "The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true." ); } //>>includeEnd('debug'); return this._extras; }, }, /** * Cesium adds lighting from the earth, sky, atmosphere, and star skybox. This cartesian is used to scale the final * diffuse and specular lighting contribution from those sources to the final color. A value of 0.0 will disable those light sources. * * @memberof Cesium3DTileset.prototype * * @type {Cartesian2} * @default Cartesian2(1.0, 1.0) */ imageBasedLightingFactor: { get: function () { return this._imageBasedLightingFactor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("imageBasedLightingFactor", value); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.x", value.x, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.x", value.x, 1.0 ); Check.typeOf.number.greaterThanOrEquals( "imageBasedLightingFactor.y", value.y, 0.0 ); Check.typeOf.number.lessThanOrEquals( "imageBasedLightingFactor.y", value.y, 1.0 ); //>>includeEnd('debug'); Cartesian2.clone(value, this._imageBasedLightingFactor); }, }, }); /** * Provides a hook to override the method used to request the tileset json * useful when fetching tilesets from remote servers * @param {Resource|String} tilesetUrl The url of the json file to be fetched * @returns {Promise.} A promise that resolves with the fetched json data */ Cesium3DTileset.loadJson = function (tilesetUrl) { var resource = Resource.createIfNeeded(tilesetUrl); return resource.fetchJson(); }; /** * Marks the tileset's {@link Cesium3DTileset#style} as dirty, which forces all * features to re-evaluate the style in the next frame each is visible. */ Cesium3DTileset.prototype.makeStyleDirty = function () { this._styleEngine.makeDirty(); }; /** * Loads the main tileset JSON file or a tileset JSON file referenced from a tile. * * @private */ Cesium3DTileset.prototype.loadTileset = function ( resource, tilesetJson, parentTile ) { var asset = tilesetJson.asset; if (!defined(asset)) { throw new RuntimeError("Tileset must have an asset property."); } if (asset.version !== "0.0" && asset.version !== "1.0") { throw new RuntimeError("The tileset must be 3D Tiles version 0.0 or 1.0."); } var statistics = this._statistics; var tilesetVersion = asset.tilesetVersion; if (defined(tilesetVersion)) { // Append the tileset version to the resource this._basePath += "?v=" + tilesetVersion; resource = resource.clone(); resource.setQueryParameters({ v: tilesetVersion }); } // A tileset JSON file referenced from a tile may exist in a different directory than the root tileset. // Get the basePath relative to the external tileset. var rootTile = new Cesium3DTile(this, resource, tilesetJson.root, parentTile); // If there is a parentTile, add the root of the currently loading tileset // to parentTile's children, and update its _depth. if (defined(parentTile)) { parentTile.children.push(rootTile); rootTile._depth = parentTile._depth + 1; } var stack = []; stack.push(rootTile); while (stack.length > 0) { var tile = stack.pop(); ++statistics.numberOfTilesTotal; this._allTilesAdditive = this._allTilesAdditive && tile.refine === Cesium3DTileRefine$1.ADD; var children = tile._header.children; if (defined(children)) { var length = children.length; for (var i = 0; i < length; ++i) { var childHeader = children[i]; var childTile = new Cesium3DTile(this, resource, childHeader, tile); tile.children.push(childTile); childTile._depth = tile._depth + 1; stack.push(childTile); } } if (this._cullWithChildrenBounds) { Cesium3DTileOptimizations.checkChildrenWithinParent(tile); } } return rootTile; }; var scratchPositionNormal$2 = new Cartesian3(); var scratchCartographic$8 = new Cartographic(); var scratchMatrix$1 = new Matrix4(); var scratchCenter$2 = new Cartesian3(); var scratchPosition$2 = new Cartesian3(); var scratchDirection$1 = new Cartesian3(); function updateDynamicScreenSpaceError(tileset, frameState) { var up; var direction; var height; var minimumHeight; var maximumHeight; var camera = frameState.camera; var root = tileset._root; var tileBoundingVolume = root.contentBoundingVolume; if (tileBoundingVolume instanceof TileBoundingRegion) { up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal$2); direction = camera.directionWC; height = camera.positionCartographic.height; minimumHeight = tileBoundingVolume.minimumHeight; maximumHeight = tileBoundingVolume.maximumHeight; } else { // Transform camera position and direction into the local coordinate system of the tileset var transformLocal = Matrix4.inverseTransformation( root.computedTransform, scratchMatrix$1 ); var ellipsoid = frameState.mapProjection.ellipsoid; var boundingVolume = tileBoundingVolume.boundingVolume; var centerLocal = Matrix4.multiplyByPoint( transformLocal, boundingVolume.center, scratchCenter$2 ); if (Cartesian3.magnitude(centerLocal) > ellipsoid.minimumRadius) { // The tileset is defined in WGS84. Approximate the minimum and maximum height. var centerCartographic = Cartographic.fromCartesian( centerLocal, ellipsoid, scratchCartographic$8 ); up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal$2); direction = camera.directionWC; height = camera.positionCartographic.height; minimumHeight = 0.0; maximumHeight = centerCartographic.height * 2.0; } else { // The tileset is defined in local coordinates (z-up) var positionLocal = Matrix4.multiplyByPoint( transformLocal, camera.positionWC, scratchPosition$2 ); up = Cartesian3.UNIT_Z; direction = Matrix4.multiplyByPointAsVector( transformLocal, camera.directionWC, scratchDirection$1 ); direction = Cartesian3.normalize(direction, direction); height = positionLocal.z; if (tileBoundingVolume instanceof TileOrientedBoundingBox) { // Assuming z-up, the last component stores the half-height of the box var boxHeight = root._header.boundingVolume.box[11]; minimumHeight = centerLocal.z - boxHeight; maximumHeight = centerLocal.z + boxHeight; } else if (tileBoundingVolume instanceof TileBoundingSphere) { var radius = boundingVolume.radius; minimumHeight = centerLocal.z - radius; maximumHeight = centerLocal.z + radius; } } } // The range where the density starts to lessen. Start at the quarter height of the tileset. var heightFalloff = tileset.dynamicScreenSpaceErrorHeightFalloff; var heightClose = minimumHeight + (maximumHeight - minimumHeight) * heightFalloff; var heightFar = maximumHeight; var t = CesiumMath.clamp( (height - heightClose) / (heightFar - heightClose), 0.0, 1.0 ); // Increase density as the camera tilts towards the horizon var dot = Math.abs(Cartesian3.dot(direction, up)); var horizonFactor = 1.0 - dot; // Weaken the horizon factor as the camera height increases, implying the camera is further away from the tileset. // The goal is to increase density for the "street view", not when viewing the tileset from a distance. horizonFactor = horizonFactor * (1.0 - t); var density = tileset.dynamicScreenSpaceErrorDensity; density *= horizonFactor; tileset._dynamicScreenSpaceErrorComputedDensity = density; } /////////////////////////////////////////////////////////////////////////// function requestContent(tileset, tile) { if (tile.hasEmptyContent) { return; } var statistics = tileset._statistics; var expired = tile.contentExpired; var requested = tile.requestContent(); if (!requested) { ++statistics.numberOfAttemptedRequests; return; } if (expired) { if (tile.hasTilesetContent) { destroySubtree(tileset, tile); } else { statistics.decrementLoadCounts(tile.content); --statistics.numberOfTilesWithContentReady; } } ++statistics.numberOfPendingRequests; tileset._requestedTilesInFlight.push(tile); tile.contentReadyToProcessPromise.then(addToProcessingQueue(tileset, tile)); tile.contentReadyPromise .then(handleTileSuccess(tileset, tile)) .otherwise(handleTileFailure(tileset, tile)); } function sortRequestByPriority(a, b) { return a._priority - b._priority; } /** * Perform any pass invariant tasks here. Called after the render pass. * @private */ Cesium3DTileset.prototype.postPassesUpdate = function (frameState) { if (!this.ready) { return; } cancelOutOfViewRequests(this, frameState); raiseLoadProgressEvent(this, frameState); this._cache.unloadTiles(this, unloadTile); this._styleEngine.resetDirty(); }; /** * Perform any pass invariant tasks here. Called before any passes are executed. * @private */ Cesium3DTileset.prototype.prePassesUpdate = function (frameState) { if (!this.ready) { return; } processTiles(this, frameState); // Update clipping planes var clippingPlanes = this._clippingPlanes; this._clippingPlanesOriginMatrixDirty = true; if (defined(clippingPlanes) && clippingPlanes.enabled) { clippingPlanes.update(frameState); } if (!defined(this._loadTimestamp)) { this._loadTimestamp = JulianDate.clone(frameState.time); } this._timeSinceLoad = Math.max( JulianDate.secondsDifference(frameState.time, this._loadTimestamp) * 1000, 0.0 ); this._skipLevelOfDetail = this.skipLevelOfDetail && !defined(this._classificationType) && !this._disableSkipLevelOfDetail && !this._allTilesAdditive; if (this.dynamicScreenSpaceError) { updateDynamicScreenSpaceError(this, frameState); } if (frameState.newFrame) { this._cache.reset(); } }; function cancelOutOfViewRequests(tileset, frameState) { var requestedTilesInFlight = tileset._requestedTilesInFlight; var removeCount = 0; var length = requestedTilesInFlight.length; for (var i = 0; i < length; ++i) { var tile = requestedTilesInFlight[i]; // NOTE: This is framerate dependant so make sure the threshold check is small var outOfView = frameState.frameNumber - tile._touchedFrame >= 1; if (tile._contentState !== Cesium3DTileContentState$1.LOADING) { // No longer fetching from host, don't need to track it anymore. Gets marked as LOADING in Cesium3DTile::requestContent(). ++removeCount; continue; } else if (outOfView) { // RequestScheduler will take care of cancelling it tile._request.cancel(); ++removeCount; continue; } if (removeCount > 0) { requestedTilesInFlight[i - removeCount] = tile; } } requestedTilesInFlight.length -= removeCount; } function requestTiles(tileset, isAsync) { // Sort requests by priority before making any requests. // This makes it less likely that requests will be cancelled after being issued. var requestedTiles = tileset._requestedTiles; var length = requestedTiles.length; requestedTiles.sort(sortRequestByPriority); for (var i = 0; i < length; ++i) { requestContent(tileset, requestedTiles[i]); } } function addToProcessingQueue(tileset, tile) { return function () { tileset._processingQueue.push(tile); --tileset._statistics.numberOfPendingRequests; ++tileset._statistics.numberOfTilesProcessing; }; } function handleTileFailure(tileset, tile) { return function (error) { var url = tile._contentResource.url; var message = defined(error.message) ? error.message : error.toString(); if (tileset.tileFailed.numberOfListeners > 0) { tileset.tileFailed.raiseEvent({ url: url, message: message, }); } else { console.log("A 3D tile failed to load: " + url); console.log("Error: " + message); } }; } function handleTileSuccess(tileset, tile) { return function () { --tileset._statistics.numberOfTilesProcessing; if (!tile.hasTilesetContent) { // RESEARCH_IDEA: ability to unload tiles (without content) for an // external tileset when all the tiles are unloaded. tileset._statistics.incrementLoadCounts(tile.content); ++tileset._statistics.numberOfTilesWithContentReady; ++tileset._statistics.numberOfLoadedTilesTotal; // Add to the tile cache. Previously expired tiles are already in the cache and won't get re-added. tileset._cache.add(tile); } tileset.tileLoad.raiseEvent(tile); }; } function filterProcessingQueue(tileset) { var tiles = tileset._processingQueue; var length = tiles.length; var removeCount = 0; for (var i = 0; i < length; ++i) { var tile = tiles[i]; if (tile._contentState !== Cesium3DTileContentState$1.PROCESSING) { ++removeCount; continue; } if (removeCount > 0) { tiles[i - removeCount] = tile; } } tiles.length -= removeCount; } function processTiles(tileset, frameState) { filterProcessingQueue(tileset); var tiles = tileset._processingQueue; var length = tiles.length; // Process tiles in the PROCESSING state so they will eventually move to the READY state. for (var i = 0; i < length; ++i) { tiles[i].process(tileset, frameState); } } /////////////////////////////////////////////////////////////////////////// var scratchCartesian$4 = new Cartesian3(); var stringOptions$1 = { maximumFractionDigits: 3, }; function formatMemoryString$1(memorySizeInBytes) { var memoryInMegabytes = memorySizeInBytes / 1048576; if (memoryInMegabytes < 1.0) { return memoryInMegabytes.toLocaleString(undefined, stringOptions$1); } return Math.round(memoryInMegabytes).toLocaleString(); } function computeTileLabelPosition(tile) { var boundingVolume = tile.boundingVolume.boundingVolume; var halfAxes = boundingVolume.halfAxes; var radius = boundingVolume.radius; var position = Cartesian3.clone(boundingVolume.center, scratchCartesian$4); if (defined(halfAxes)) { position.x += 0.75 * (halfAxes[0] + halfAxes[3] + halfAxes[6]); position.y += 0.75 * (halfAxes[1] + halfAxes[4] + halfAxes[7]); position.z += 0.75 * (halfAxes[2] + halfAxes[5] + halfAxes[8]); } else if (defined(radius)) { var normal = Cartesian3.normalize(boundingVolume.center, scratchCartesian$4); normal = Cartesian3.multiplyByScalar( normal, 0.75 * radius, scratchCartesian$4 ); position = Cartesian3.add(normal, boundingVolume.center, scratchCartesian$4); } return position; } function addTileDebugLabel(tile, tileset, position) { var labelString = ""; var attributes = 0; if (tileset.debugShowGeometricError) { labelString += "\nGeometric error: " + tile.geometricError; attributes++; } if (tileset.debugShowRenderingStatistics) { labelString += "\nCommands: " + tile.commandsLength; attributes++; // Don't display number of points or triangles if 0. var numberOfPoints = tile.content.pointsLength; if (numberOfPoints > 0) { labelString += "\nPoints: " + tile.content.pointsLength; attributes++; } var numberOfTriangles = tile.content.trianglesLength; if (numberOfTriangles > 0) { labelString += "\nTriangles: " + tile.content.trianglesLength; attributes++; } labelString += "\nFeatures: " + tile.content.featuresLength; attributes++; } if (tileset.debugShowMemoryUsage) { labelString += "\nTexture Memory: " + formatMemoryString$1(tile.content.texturesByteLength); labelString += "\nGeometry Memory: " + formatMemoryString$1(tile.content.geometryByteLength); attributes += 2; } if (tileset.debugShowUrl) { labelString += "\nUrl: " + tile._header.content.uri; attributes++; } var newLabel = { text: labelString.substring(1), position: position, font: 19 - attributes + "px sans-serif", showBackground: true, disableDepthTestDistance: Number.POSITIVE_INFINITY, }; return tileset._tileDebugLabels.add(newLabel); } function updateTileDebugLabels(tileset, frameState) { var i; var tile; var selectedTiles = tileset._selectedTiles; var selectedLength = selectedTiles.length; var emptyTiles = tileset._emptyTiles; var emptyLength = emptyTiles.length; tileset._tileDebugLabels.removeAll(); if (tileset.debugPickedTileLabelOnly) { if (defined(tileset.debugPickedTile)) { var position = defined(tileset.debugPickPosition) ? tileset.debugPickPosition : computeTileLabelPosition(tileset.debugPickedTile); var label = addTileDebugLabel(tileset.debugPickedTile, tileset, position); label.pixelOffset = new Cartesian2(15, -15); // Offset to avoid picking the label. } } else { for (i = 0; i < selectedLength; ++i) { tile = selectedTiles[i]; addTileDebugLabel(tile, tileset, computeTileLabelPosition(tile)); } for (i = 0; i < emptyLength; ++i) { tile = emptyTiles[i]; if (tile.hasTilesetContent) { addTileDebugLabel(tile, tileset, computeTileLabelPosition(tile)); } } } tileset._tileDebugLabels.update(frameState); } function updateTiles(tileset, frameState, passOptions) { tileset._styleEngine.applyStyle(tileset); var isRender = passOptions.isRender; var statistics = tileset._statistics; var commandList = frameState.commandList; var numberOfInitialCommands = commandList.length; var selectedTiles = tileset._selectedTiles; var selectedLength = selectedTiles.length; var emptyTiles = tileset._emptyTiles; var emptyLength = emptyTiles.length; var tileVisible = tileset.tileVisible; var i; var tile; var bivariateVisibilityTest = tileset._skipLevelOfDetail && tileset._hasMixedContent && frameState.context.stencilBuffer && selectedLength > 0; tileset._backfaceCommands.length = 0; if (bivariateVisibilityTest) { if (!defined(tileset._stencilClearCommand)) { tileset._stencilClearCommand = new ClearCommand({ stencil: 0, pass: Pass$1.CESIUM_3D_TILE, renderState: RenderState.fromCache({ stencilMask: StencilConstants$1.SKIP_LOD_MASK, }), }); } commandList.push(tileset._stencilClearCommand); } var lengthBeforeUpdate = commandList.length; for (i = 0; i < selectedLength; ++i) { tile = selectedTiles[i]; // Raise the tileVisible event before update in case the tileVisible event // handler makes changes that update needs to apply to WebGL resources if (isRender) { tileVisible.raiseEvent(tile); } tile.update(tileset, frameState, passOptions); statistics.incrementSelectionCounts(tile.content); ++statistics.selected; } for (i = 0; i < emptyLength; ++i) { tile = emptyTiles[i]; tile.update(tileset, frameState, passOptions); } var addedCommandsLength = commandList.length - lengthBeforeUpdate; tileset._backfaceCommands.trim(); if (bivariateVisibilityTest) { /* * Consider 'effective leaf' tiles as selected tiles that have no selected descendants. They may have children, * but they are currently our effective leaves because they do not have selected descendants. These tiles * are those where with tile._finalResolution === true. * Let 'unresolved' tiles be those with tile._finalResolution === false. * * 1. Render just the backfaces of unresolved tiles in order to lay down z * 2. Render all frontfaces wherever tile._selectionDepth > stencilBuffer. * Replace stencilBuffer with tile._selectionDepth, when passing the z test. * Because children are always drawn before ancestors {@link Cesium3DTilesetTraversal#traverseAndSelect}, * this effectively draws children first and does not draw ancestors if a descendant has already * been drawn at that pixel. * Step 1 prevents child tiles from appearing on top when they are truly behind ancestor content. * If they are behind the backfaces of the ancestor, then they will not be drawn. * * NOTE: Step 2 sometimes causes visual artifacts when backfacing child content has some faces that * partially face the camera and are inside of the ancestor content. Because they are inside, they will * not be culled by the depth writes in Step 1, and because they partially face the camera, the stencil tests * will draw them on top of the ancestor content. * * NOTE: Because we always render backfaces of unresolved tiles, if the camera is looking at the backfaces * of an object, they will always be drawn while loading, even if backface culling is enabled. */ var backfaceCommands = tileset._backfaceCommands.values; var backfaceCommandsLength = backfaceCommands.length; commandList.length += backfaceCommandsLength; // copy commands to the back of the commandList for (i = addedCommandsLength - 1; i >= 0; --i) { commandList[lengthBeforeUpdate + backfaceCommandsLength + i] = commandList[lengthBeforeUpdate + i]; } // move backface commands to the front of the commandList for (i = 0; i < backfaceCommandsLength; ++i) { commandList[lengthBeforeUpdate + i] = backfaceCommands[i]; } } // Number of commands added by each update above addedCommandsLength = commandList.length - numberOfInitialCommands; statistics.numberOfCommands = addedCommandsLength; // Only run EDL if simple attenuation is on if ( isRender && tileset.pointCloudShading.attenuation && tileset.pointCloudShading.eyeDomeLighting && addedCommandsLength > 0 ) { tileset._pointCloudEyeDomeLighting.update( frameState, numberOfInitialCommands, tileset.pointCloudShading, tileset.boundingSphere ); } if (isRender) { if ( tileset.debugShowGeometricError || tileset.debugShowRenderingStatistics || tileset.debugShowMemoryUsage || tileset.debugShowUrl ) { if (!defined(tileset._tileDebugLabels)) { tileset._tileDebugLabels = new LabelCollection(); } updateTileDebugLabels(tileset, frameState); } else { tileset._tileDebugLabels = tileset._tileDebugLabels && tileset._tileDebugLabels.destroy(); } } } var scratchStack = []; function destroySubtree(tileset, tile) { var root = tile; var stack = scratchStack; stack.push(tile); while (stack.length > 0) { tile = stack.pop(); var children = tile.children; var length = children.length; for (var i = 0; i < length; ++i) { stack.push(children[i]); } if (tile !== root) { destroyTile(tileset, tile); --tileset._statistics.numberOfTilesTotal; } } root.children = []; } function unloadTile(tileset, tile) { tileset.tileUnload.raiseEvent(tile); tileset._statistics.decrementLoadCounts(tile.content); --tileset._statistics.numberOfTilesWithContentReady; tile.unloadContent(); } function destroyTile(tileset, tile) { tileset._cache.unloadTile(tileset, tile, unloadTile); tile.destroy(); } /** * Unloads all tiles that weren't selected the previous frame. This can be used to * explicitly manage the tile cache and reduce the total number of tiles loaded below * {@link Cesium3DTileset#maximumMemoryUsage}. *

* Tile unloads occur at the next frame to keep all the WebGL delete calls * within the render loop. *

*/ Cesium3DTileset.prototype.trimLoadedTiles = function () { this._cache.trim(); }; /////////////////////////////////////////////////////////////////////////// function raiseLoadProgressEvent(tileset, frameState) { var statistics = tileset._statistics; var statisticsLast = tileset._statisticsLast; var numberOfPendingRequests = statistics.numberOfPendingRequests; var numberOfTilesProcessing = statistics.numberOfTilesProcessing; var lastNumberOfPendingRequest = statisticsLast.numberOfPendingRequests; var lastNumberOfTilesProcessing = statisticsLast.numberOfTilesProcessing; Cesium3DTilesetStatistics.clone(statistics, statisticsLast); var progressChanged = numberOfPendingRequests !== lastNumberOfPendingRequest || numberOfTilesProcessing !== lastNumberOfTilesProcessing; if (progressChanged) { frameState.afterRender.push(function () { tileset.loadProgress.raiseEvent( numberOfPendingRequests, numberOfTilesProcessing ); }); } tileset._tilesLoaded = statistics.numberOfPendingRequests === 0 && statistics.numberOfTilesProcessing === 0 && statistics.numberOfAttemptedRequests === 0; // Events are raised (added to the afterRender queue) here since promises // may resolve outside of the update loop that then raise events, e.g., // model's readyPromise. if (progressChanged && tileset._tilesLoaded) { frameState.afterRender.push(function () { tileset.allTilesLoaded.raiseEvent(); }); if (!tileset._initialTilesLoaded) { tileset._initialTilesLoaded = true; frameState.afterRender.push(function () { tileset.initialTilesLoaded.raiseEvent(); }); } } } function resetMinimumMaximum(tileset) { tileset._heatmap.resetMinimumMaximum(); tileset._minimumPriority.depth = Number.MAX_VALUE; tileset._maximumPriority.depth = -Number.MAX_VALUE; tileset._minimumPriority.foveatedFactor = Number.MAX_VALUE; tileset._maximumPriority.foveatedFactor = -Number.MAX_VALUE; tileset._minimumPriority.distance = Number.MAX_VALUE; tileset._maximumPriority.distance = -Number.MAX_VALUE; tileset._minimumPriority.reverseScreenSpaceError = Number.MAX_VALUE; tileset._maximumPriority.reverseScreenSpaceError = -Number.MAX_VALUE; } function detectModelMatrixChanged(tileset, frameState) { if ( frameState.frameNumber !== tileset._updatedModelMatrixFrame || !defined(tileset._previousModelMatrix) ) { tileset._updatedModelMatrixFrame = frameState.frameNumber; tileset._modelMatrixChanged = !Matrix4.equals( tileset.modelMatrix, tileset._previousModelMatrix ); if (tileset._modelMatrixChanged) { tileset._previousModelMatrix = Matrix4.clone( tileset.modelMatrix, tileset._previousModelMatrix ); } } } /////////////////////////////////////////////////////////////////////////// function update$1(tileset, frameState, passStatistics, passOptions) { if (frameState.mode === SceneMode$1.MORPHING) { return false; } if (!tileset.ready) { return false; } var statistics = tileset._statistics; statistics.clear(); var isRender = passOptions.isRender; // Resets the visibility check for each pass ++tileset._updatedVisibilityFrame; // Update any tracked min max values resetMinimumMaximum(tileset); detectModelMatrixChanged(tileset, frameState); tileset._cullRequestsWhileMoving = tileset.cullRequestsWhileMoving && !tileset._modelMatrixChanged; var ready = passOptions.traversal.selectTiles(tileset, frameState); if (passOptions.requestTiles) { requestTiles(tileset); } updateTiles(tileset, frameState, passOptions); // Update pass statistics Cesium3DTilesetStatistics.clone(statistics, passStatistics); if (isRender) { var credits = tileset._credits; if (defined(credits) && statistics.selected !== 0) { var length = credits.length; for (var i = 0; i < length; ++i) { frameState.creditDisplay.addCredit(credits[i]); } } } return ready; } /** * @private */ Cesium3DTileset.prototype.update = function (frameState) { this.updateForPass(frameState, frameState.tilesetPassState); }; /** * @private */ Cesium3DTileset.prototype.updateForPass = function ( frameState, tilesetPassState ) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("frameState", frameState); Check.typeOf.object("tilesetPassState", tilesetPassState); //>>includeEnd('debug'); var pass = tilesetPassState.pass; if ( (pass === Cesium3DTilePass$1.PRELOAD && (!this.preloadWhenHidden || this.show)) || (pass === Cesium3DTilePass$1.PRELOAD_FLIGHT && (!this.preloadFlightDestinations || (!this.show && !this.preloadWhenHidden))) || (pass === Cesium3DTilePass$1.REQUEST_RENDER_MODE_DEFER_CHECK && ((!this._cullRequestsWhileMoving && this.foveatedTimeDelay <= 0) || !this.show)) ) { return; } var originalCommandList = frameState.commandList; var originalCamera = frameState.camera; var originalCullingVolume = frameState.cullingVolume; tilesetPassState.ready = false; var passOptions = Cesium3DTilePass$1.getPassOptions(pass); var ignoreCommands = passOptions.ignoreCommands; var commandList = defaultValue( tilesetPassState.commandList, originalCommandList ); var commandStart = commandList.length; frameState.commandList = commandList; frameState.camera = defaultValue(tilesetPassState.camera, originalCamera); frameState.cullingVolume = defaultValue( tilesetPassState.cullingVolume, originalCullingVolume ); var passStatistics = this._statisticsPerPass[pass]; if (this.show || ignoreCommands) { this._pass = pass; tilesetPassState.ready = update$1( this, frameState, passStatistics, passOptions ); } if (ignoreCommands) { commandList.length = commandStart; } frameState.commandList = originalCommandList; frameState.camera = originalCamera; frameState.cullingVolume = originalCullingVolume; }; /** * true if the tileset JSON file lists the extension in extensionsUsed; otherwise, false. * @param {String} extensionName The name of the extension to check. * * @returns {Boolean} true if the tileset JSON file lists the extension in extensionsUsed; otherwise, false. */ Cesium3DTileset.prototype.hasExtension = function (extensionName) { if (!defined(this._extensionsUsed)) { return false; } return this._extensionsUsed.indexOf(extensionName) > -1; }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see Cesium3DTileset#destroy */ Cesium3DTileset.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * tileset = tileset && tileset.destroy(); * * @see Cesium3DTileset#isDestroyed */ Cesium3DTileset.prototype.destroy = function () { this._tileDebugLabels = this._tileDebugLabels && this._tileDebugLabels.destroy(); this._clippingPlanes = this._clippingPlanes && this._clippingPlanes.destroy(); // Traverse the tree and destroy all tiles if (defined(this._root)) { var stack = scratchStack; stack.push(this._root); while (stack.length > 0) { var tile = stack.pop(); tile.destroy(); var children = tile.children; var length = children.length; for (var i = 0; i < length; ++i) { stack.push(children[i]); } } } this._root = undefined; return destroyObject(this); }; var modelMatrixScratch$1 = new Matrix4(); /** * A {@link Visualizer} which maps {@link Entity#tileset} to a {@link Cesium3DTileset}. * @alias Cesium3DTilesetVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function Cesium3DTilesetVisualizer(scene, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError("scene is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( Cesium3DTilesetVisualizer.prototype._onCollectionChanged, this ); this._scene = scene; this._primitives = scene.primitives; this._entityCollection = entityCollection; this._tilesetHash = {}; this._entitiesToVisualize = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates models created this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ Cesium3DTilesetVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var entities = this._entitiesToVisualize.values; var tilesetHash = this._tilesetHash; var primitives = this._primitives; for (var i = 0, len = entities.length; i < len; i++) { var entity = entities[i]; var tilesetGraphics = entity._tileset; var resource; var tilesetData = tilesetHash[entity.id]; var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(tilesetGraphics._show, time, true); var modelMatrix; if (show) { modelMatrix = entity.computeModelMatrix(time, modelMatrixScratch$1); resource = Resource.createIfNeeded( Property.getValueOrUndefined(tilesetGraphics._uri, time) ); } if (!show) { if (defined(tilesetData)) { tilesetData.tilesetPrimitive.show = false; } continue; } var tileset = defined(tilesetData) ? tilesetData.tilesetPrimitive : undefined; if (!defined(tileset) || resource.url !== tilesetData.url) { if (defined(tileset)) { primitives.removeAndDestroy(tileset); delete tilesetHash[entity.id]; } tileset = new Cesium3DTileset({ url: resource, }); tileset.id = entity; primitives.add(tileset); tilesetData = { tilesetPrimitive: tileset, url: resource.url, loadFail: false, }; tilesetHash[entity.id] = tilesetData; checkLoad(tileset, entity, tilesetHash); } tileset.show = true; if (defined(modelMatrix)) { tileset.modelMatrix = modelMatrix; } tileset.maximumScreenSpaceError = Property.getValueOrDefault( tilesetGraphics.maximumScreenSpaceError, time, tileset.maximumScreenSpaceError ); } return true; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ Cesium3DTilesetVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ Cesium3DTilesetVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( Cesium3DTilesetVisualizer.prototype._onCollectionChanged, this ); var entities = this._entitiesToVisualize.values; var tilesetHash = this._tilesetHash; var primitives = this._primitives; for (var i = entities.length - 1; i > -1; i--) { removeTileset(this, entities[i], tilesetHash, primitives); } return destroyObject(this); }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ Cesium3DTilesetVisualizer.prototype.getBoundingSphere = function ( entity, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var tilesetData = this._tilesetHash[entity.id]; if (!defined(tilesetData) || tilesetData.loadFail) { return BoundingSphereState$1.FAILED; } var primitive = tilesetData.tilesetPrimitive; if (!defined(primitive) || !primitive.show) { return BoundingSphereState$1.FAILED; } if (!primitive.ready) { return BoundingSphereState$1.PENDING; } BoundingSphere.clone(primitive.boundingSphere, result); return BoundingSphereState$1.DONE; }; /** * @private */ Cesium3DTilesetVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var entities = this._entitiesToVisualize; var tilesetHash = this._tilesetHash; var primitives = this._primitives; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._tileset)) { entities.set(entity.id, entity); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._tileset)) { entities.set(entity.id, entity); } else { removeTileset(this, entity, tilesetHash, primitives); entities.remove(entity.id); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; removeTileset(this, entity, tilesetHash, primitives); entities.remove(entity.id); } }; function removeTileset(visualizer, entity, tilesetHash, primitives) { var tilesetData = tilesetHash[entity.id]; if (defined(tilesetData)) { primitives.removeAndDestroy(tilesetData.tilesetPrimitive); delete tilesetHash[entity.id]; } } function checkLoad(primitive, entity, tilesetHash) { primitive.readyPromise.otherwise(function (error) { console.error(error); tilesetHash[entity.id].loadFail = true; }); } var defaultEvenColor$1 = Color.WHITE; var defaultOddColor$1 = Color.BLACK; var defaultRepeat$1 = new Cartesian2(2.0, 2.0); /** * A {@link MaterialProperty} that maps to checkerboard {@link Material} uniforms. * @alias CheckerboardMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|Color} [options.evenColor=Color.WHITE] A Property specifying the first {@link Color}. * @param {Property|Color} [options.oddColor=Color.BLACK] A Property specifying the second {@link Color}. * @param {Property|Cartesian2} [options.repeat=new Cartesian2(2.0, 2.0)] A {@link Cartesian2} Property specifying how many times the tiles repeat in each direction. */ function CheckerboardMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._evenColor = undefined; this._evenColorSubscription = undefined; this._oddColor = undefined; this._oddColorSubscription = undefined; this._repeat = undefined; this._repeatSubscription = undefined; this.evenColor = options.evenColor; this.oddColor = options.oddColor; this.repeat = options.repeat; } Object.defineProperties(CheckerboardMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof CheckerboardMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._evenColor) && // Property.isConstant(this._oddColor) && // Property.isConstant(this._repeat) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof CheckerboardMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the first {@link Color}. * @memberof CheckerboardMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ evenColor: createPropertyDescriptor("evenColor"), /** * Gets or sets the Property specifying the second {@link Color}. * @memberof CheckerboardMaterialProperty.prototype * @type {Property|undefined} * @default Color.BLACK */ oddColor: createPropertyDescriptor("oddColor"), /** * Gets or sets the {@link Cartesian2} Property specifying how many times the tiles repeat in each direction. * @memberof CheckerboardMaterialProperty.prototype * @type {Property|undefined} * @default new Cartesian2(2.0, 2.0) */ repeat: createPropertyDescriptor("repeat"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ CheckerboardMaterialProperty.prototype.getType = function (time) { return "Checkerboard"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ CheckerboardMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.lightColor = Property.getValueOrClonedDefault( this._evenColor, time, defaultEvenColor$1, result.lightColor ); result.darkColor = Property.getValueOrClonedDefault( this._oddColor, time, defaultOddColor$1, result.darkColor ); result.repeat = Property.getValueOrDefault(this._repeat, time, defaultRepeat$1); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ CheckerboardMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof CheckerboardMaterialProperty && // Property.equals(this._evenColor, other._evenColor) && // Property.equals(this._oddColor, other._oddColor) && // Property.equals(this._repeat, other._repeat)) ); }; var entityOptionsScratch$1 = { id: undefined, }; function fireChangedEvent(collection) { if (collection._firing) { collection._refire = true; return; } if (collection._suspendCount === 0) { var added = collection._addedEntities; var removed = collection._removedEntities; var changed = collection._changedEntities; if (changed.length !== 0 || added.length !== 0 || removed.length !== 0) { collection._firing = true; do { collection._refire = false; var addedArray = added.values.slice(0); var removedArray = removed.values.slice(0); var changedArray = changed.values.slice(0); added.removeAll(); removed.removeAll(); changed.removeAll(); collection._collectionChanged.raiseEvent( collection, addedArray, removedArray, changedArray ); } while (collection._refire); collection._firing = false; } } } /** * An observable collection of {@link Entity} instances where each entity has a unique id. * @alias EntityCollection * @constructor * * @param {DataSource|CompositeEntityCollection} [owner] The data source (or composite entity collection) which created this collection. */ function EntityCollection(owner) { this._owner = owner; this._entities = new AssociativeArray(); this._addedEntities = new AssociativeArray(); this._removedEntities = new AssociativeArray(); this._changedEntities = new AssociativeArray(); this._suspendCount = 0; this._collectionChanged = new Event(); this._id = createGuid(); this._show = true; this._firing = false; this._refire = false; } /** * Prevents {@link EntityCollection#collectionChanged} events from being raised * until a corresponding call is made to {@link EntityCollection#resumeEvents}, at which * point a single event will be raised that covers all suspended operations. * This allows for many items to be added and removed efficiently. * This function can be safely called multiple times as long as there * are corresponding calls to {@link EntityCollection#resumeEvents}. */ EntityCollection.prototype.suspendEvents = function () { this._suspendCount++; }; /** * Resumes raising {@link EntityCollection#collectionChanged} events immediately * when an item is added or removed. Any modifications made while while events were suspended * will be triggered as a single event when this function is called. * This function is reference counted and can safely be called multiple times as long as there * are corresponding calls to {@link EntityCollection#resumeEvents}. * * @exception {DeveloperError} resumeEvents can not be called before suspendEvents. */ EntityCollection.prototype.resumeEvents = function () { //>>includeStart('debug', pragmas.debug); if (this._suspendCount === 0) { throw new DeveloperError( "resumeEvents can not be called before suspendEvents." ); } //>>includeEnd('debug'); this._suspendCount--; fireChangedEvent(this); }; /** * The signature of the event generated by {@link EntityCollection#collectionChanged}. * @function * * @param {EntityCollection} collection The collection that triggered the event. * @param {Entity[]} added The array of {@link Entity} instances that have been added to the collection. * @param {Entity[]} removed The array of {@link Entity} instances that have been removed from the collection. * @param {Entity[]} changed The array of {@link Entity} instances that have been modified. */ EntityCollection.collectionChangedEventCallback = undefined; Object.defineProperties(EntityCollection.prototype, { /** * Gets the event that is fired when entities are added or removed from the collection. * The generated event is a {@link EntityCollection.collectionChangedEventCallback}. * @memberof EntityCollection.prototype * @readonly * @type {Event} */ collectionChanged: { get: function () { return this._collectionChanged; }, }, /** * Gets a globally unique identifier for this collection. * @memberof EntityCollection.prototype * @readonly * @type {String} */ id: { get: function () { return this._id; }, }, /** * Gets the array of Entity instances in the collection. * This array should not be modified directly. * @memberof EntityCollection.prototype * @readonly * @type {Entity[]} */ values: { get: function () { return this._entities.values; }, }, /** * Gets whether or not this entity collection should be * displayed. When true, each entity is only displayed if * its own show property is also true. * @memberof EntityCollection.prototype * @type {Boolean} */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (value === this._show) { return; } //Since entity.isShowing includes the EntityCollection.show state //in its calculation, we need to loop over the entities array //twice, once to get the old showing value and a second time //to raise the changed event. this.suspendEvents(); var i; var oldShows = []; var entities = this._entities.values; var entitiesLength = entities.length; for (i = 0; i < entitiesLength; i++) { oldShows.push(entities[i].isShowing); } this._show = value; for (i = 0; i < entitiesLength; i++) { var oldShow = oldShows[i]; var entity = entities[i]; if (oldShow !== entity.isShowing) { entity.definitionChanged.raiseEvent( entity, "isShowing", entity.isShowing, oldShow ); } } this.resumeEvents(); }, }, /** * Gets the owner of this entity collection, ie. the data source or composite entity collection which created it. * @memberof EntityCollection.prototype * @readonly * @type {DataSource|CompositeEntityCollection} */ owner: { get: function () { return this._owner; }, }, }); /** * Computes the maximum availability of the entities in the collection. * If the collection contains a mix of infinitely available data and non-infinite data, * it will return the interval pertaining to the non-infinite data only. If all * data is infinite, an infinite interval will be returned. * * @returns {TimeInterval} The availability of entities in the collection. */ EntityCollection.prototype.computeAvailability = function () { var startTime = Iso8601.MAXIMUM_VALUE; var stopTime = Iso8601.MINIMUM_VALUE; var entities = this._entities.values; for (var i = 0, len = entities.length; i < len; i++) { var entity = entities[i]; var availability = entity.availability; if (defined(availability)) { var start = availability.start; var stop = availability.stop; if ( JulianDate.lessThan(start, startTime) && !start.equals(Iso8601.MINIMUM_VALUE) ) { startTime = start; } if ( JulianDate.greaterThan(stop, stopTime) && !stop.equals(Iso8601.MAXIMUM_VALUE) ) { stopTime = stop; } } } if (Iso8601.MAXIMUM_VALUE.equals(startTime)) { startTime = Iso8601.MINIMUM_VALUE; } if (Iso8601.MINIMUM_VALUE.equals(stopTime)) { stopTime = Iso8601.MAXIMUM_VALUE; } return new TimeInterval({ start: startTime, stop: stopTime, }); }; /** * Add an entity to the collection. * * @param {Entity | Entity.ConstructorOptions} entity The entity to be added. * @returns {Entity} The entity that was added. * @exception {DeveloperError} An entity with already exists in this collection. */ EntityCollection.prototype.add = function (entity) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } //>>includeEnd('debug'); if (!(entity instanceof Entity)) { entity = new Entity(entity); } var id = entity.id; var entities = this._entities; if (entities.contains(id)) { throw new RuntimeError( "An entity with id " + id + " already exists in this collection." ); } entity.entityCollection = this; entities.set(id, entity); if (!this._removedEntities.remove(id)) { this._addedEntities.set(id, entity); } entity.definitionChanged.addEventListener( EntityCollection.prototype._onEntityDefinitionChanged, this ); fireChangedEvent(this); return entity; }; /** * Removes an entity from the collection. * * @param {Entity} entity The entity to be removed. * @returns {Boolean} true if the item was removed, false if it did not exist in the collection. */ EntityCollection.prototype.remove = function (entity) { if (!defined(entity)) { return false; } return this.removeById(entity.id); }; /** * Returns true if the provided entity is in this collection, false otherwise. * * @param {Entity} entity The entity. * @returns {Boolean} true if the provided entity is in this collection, false otherwise. */ EntityCollection.prototype.contains = function (entity) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required"); } //>>includeEnd('debug'); return this._entities.get(entity.id) === entity; }; /** * Removes an entity with the provided id from the collection. * * @param {String} id The id of the entity to remove. * @returns {Boolean} true if the item was removed, false if no item with the provided id existed in the collection. */ EntityCollection.prototype.removeById = function (id) { if (!defined(id)) { return false; } var entities = this._entities; var entity = entities.get(id); if (!this._entities.remove(id)) { return false; } if (!this._addedEntities.remove(id)) { this._removedEntities.set(id, entity); this._changedEntities.remove(id); } this._entities.remove(id); entity.definitionChanged.removeEventListener( EntityCollection.prototype._onEntityDefinitionChanged, this ); fireChangedEvent(this); return true; }; /** * Removes all Entities from the collection. */ EntityCollection.prototype.removeAll = function () { //The event should only contain items added before events were suspended //and the contents of the collection. var entities = this._entities; var entitiesLength = entities.length; var array = entities.values; var addedEntities = this._addedEntities; var removed = this._removedEntities; for (var i = 0; i < entitiesLength; i++) { var existingItem = array[i]; var existingItemId = existingItem.id; var addedItem = addedEntities.get(existingItemId); if (!defined(addedItem)) { existingItem.definitionChanged.removeEventListener( EntityCollection.prototype._onEntityDefinitionChanged, this ); removed.set(existingItemId, existingItem); } } entities.removeAll(); addedEntities.removeAll(); this._changedEntities.removeAll(); fireChangedEvent(this); }; /** * Gets an entity with the specified id. * * @param {String} id The id of the entity to retrieve. * @returns {Entity|undefined} The entity with the provided id or undefined if the id did not exist in the collection. */ EntityCollection.prototype.getById = function (id) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } //>>includeEnd('debug'); return this._entities.get(id); }; /** * Gets an entity with the specified id or creates it and adds it to the collection if it does not exist. * * @param {String} id The id of the entity to retrieve or create. * @returns {Entity} The new or existing object. */ EntityCollection.prototype.getOrCreateEntity = function (id) { //>>includeStart('debug', pragmas.debug); if (!defined(id)) { throw new DeveloperError("id is required."); } //>>includeEnd('debug'); var entity = this._entities.get(id); if (!defined(entity)) { entityOptionsScratch$1.id = id; entity = new Entity(entityOptionsScratch$1); this.add(entity); } return entity; }; EntityCollection.prototype._onEntityDefinitionChanged = function (entity) { var id = entity.id; if (!this._addedEntities.contains(id)) { this._changedEntities.set(id, entity); } fireChangedEvent(this); }; var entityOptionsScratch = { id: undefined, }; var entityIdScratch = new Array(2); function clean(entity) { var propertyNames = entity.propertyNames; var propertyNamesLength = propertyNames.length; for (var i = 0; i < propertyNamesLength; i++) { entity[propertyNames[i]] = undefined; } entity._name = undefined; entity._availability = undefined; } function subscribeToEntity(that, eventHash, collectionId, entity) { entityIdScratch[0] = collectionId; entityIdScratch[1] = entity.id; eventHash[ JSON.stringify(entityIdScratch) ] = entity.definitionChanged.addEventListener( CompositeEntityCollection.prototype._onDefinitionChanged, that ); } function unsubscribeFromEntity(that, eventHash, collectionId, entity) { entityIdScratch[0] = collectionId; entityIdScratch[1] = entity.id; var id = JSON.stringify(entityIdScratch); eventHash[id](); eventHash[id] = undefined; } function recomposite(that) { that._shouldRecomposite = true; if (that._suspendCount !== 0) { return; } var collections = that._collections; var collectionsLength = collections.length; var collectionsCopy = that._collectionsCopy; var collectionsCopyLength = collectionsCopy.length; var i; var entity; var entities; var iEntities; var collection; var composite = that._composite; var newEntities = new EntityCollection(that); var eventHash = that._eventHash; var collectionId; for (i = 0; i < collectionsCopyLength; i++) { collection = collectionsCopy[i]; collection.collectionChanged.removeEventListener( CompositeEntityCollection.prototype._onCollectionChanged, that ); entities = collection.values; collectionId = collection.id; for (iEntities = entities.length - 1; iEntities > -1; iEntities--) { entity = entities[iEntities]; unsubscribeFromEntity(that, eventHash, collectionId, entity); } } for (i = collectionsLength - 1; i >= 0; i--) { collection = collections[i]; collection.collectionChanged.addEventListener( CompositeEntityCollection.prototype._onCollectionChanged, that ); //Merge all of the existing entities. entities = collection.values; collectionId = collection.id; for (iEntities = entities.length - 1; iEntities > -1; iEntities--) { entity = entities[iEntities]; subscribeToEntity(that, eventHash, collectionId, entity); var compositeEntity = newEntities.getById(entity.id); if (!defined(compositeEntity)) { compositeEntity = composite.getById(entity.id); if (!defined(compositeEntity)) { entityOptionsScratch.id = entity.id; compositeEntity = new Entity(entityOptionsScratch); } else { clean(compositeEntity); } newEntities.add(compositeEntity); } compositeEntity.merge(entity); } } that._collectionsCopy = collections.slice(0); composite.suspendEvents(); composite.removeAll(); var newEntitiesArray = newEntities.values; for (i = 0; i < newEntitiesArray.length; i++) { composite.add(newEntitiesArray[i]); } composite.resumeEvents(); } /** * Non-destructively composites multiple {@link EntityCollection} instances into a single collection. * If a Entity with the same ID exists in multiple collections, it is non-destructively * merged into a single new entity instance. If an entity has the same property in multiple * collections, the property of the Entity in the last collection of the list it * belongs to is used. CompositeEntityCollection can be used almost anywhere that a * EntityCollection is used. * * @alias CompositeEntityCollection * @constructor * * @param {EntityCollection[]} [collections] The initial list of EntityCollection instances to merge. * @param {DataSource|CompositeEntityCollection} [owner] The data source (or composite entity collection) which created this collection. */ function CompositeEntityCollection(collections, owner) { this._owner = owner; this._composite = new EntityCollection(this); this._suspendCount = 0; this._collections = defined(collections) ? collections.slice() : []; this._collectionsCopy = []; this._id = createGuid(); this._eventHash = {}; recomposite(this); this._shouldRecomposite = false; } Object.defineProperties(CompositeEntityCollection.prototype, { /** * Gets the event that is fired when entities are added or removed from the collection. * The generated event is a {@link EntityCollection.collectionChangedEventCallback}. * @memberof CompositeEntityCollection.prototype * @readonly * @type {Event} */ collectionChanged: { get: function () { return this._composite._collectionChanged; }, }, /** * Gets a globally unique identifier for this collection. * @memberof CompositeEntityCollection.prototype * @readonly * @type {String} */ id: { get: function () { return this._id; }, }, /** * Gets the array of Entity instances in the collection. * This array should not be modified directly. * @memberof CompositeEntityCollection.prototype * @readonly * @type {Entity[]} */ values: { get: function () { return this._composite.values; }, }, /** * Gets the owner of this composite entity collection, ie. the data source or composite entity collection which created it. * @memberof CompositeEntityCollection.prototype * @readonly * @type {DataSource|CompositeEntityCollection} */ owner: { get: function () { return this._owner; }, }, }); /** * Adds a collection to the composite. * * @param {EntityCollection} collection the collection to add. * @param {Number} [index] the index to add the collection at. If omitted, the collection will * added on top of all existing collections. * * @exception {DeveloperError} index, if supplied, must be greater than or equal to zero and less than or equal to the number of collections. */ CompositeEntityCollection.prototype.addCollection = function ( collection, index ) { var hasIndex = defined(index); //>>includeStart('debug', pragmas.debug); if (!defined(collection)) { throw new DeveloperError("collection is required."); } if (hasIndex) { if (index < 0) { throw new DeveloperError("index must be greater than or equal to zero."); } else if (index > this._collections.length) { throw new DeveloperError( "index must be less than or equal to the number of collections." ); } } //>>includeEnd('debug'); if (!hasIndex) { index = this._collections.length; this._collections.push(collection); } else { this._collections.splice(index, 0, collection); } recomposite(this); }; /** * Removes a collection from this composite, if present. * * @param {EntityCollection} collection The collection to remove. * @returns {Boolean} true if the collection was in the composite and was removed, * false if the collection was not in the composite. */ CompositeEntityCollection.prototype.removeCollection = function (collection) { var index = this._collections.indexOf(collection); if (index !== -1) { this._collections.splice(index, 1); recomposite(this); return true; } return false; }; /** * Removes all collections from this composite. */ CompositeEntityCollection.prototype.removeAllCollections = function () { this._collections.length = 0; recomposite(this); }; /** * Checks to see if the composite contains a given collection. * * @param {EntityCollection} collection the collection to check for. * @returns {Boolean} true if the composite contains the collection, false otherwise. */ CompositeEntityCollection.prototype.containsCollection = function (collection) { return this._collections.indexOf(collection) !== -1; }; /** * Returns true if the provided entity is in this collection, false otherwise. * * @param {Entity} entity The entity. * @returns {Boolean} true if the provided entity is in this collection, false otherwise. */ CompositeEntityCollection.prototype.contains = function (entity) { return this._composite.contains(entity); }; /** * Determines the index of a given collection in the composite. * * @param {EntityCollection} collection The collection to find the index of. * @returns {Number} The index of the collection in the composite, or -1 if the collection does not exist in the composite. */ CompositeEntityCollection.prototype.indexOfCollection = function (collection) { return this._collections.indexOf(collection); }; /** * Gets a collection by index from the composite. * * @param {Number} index the index to retrieve. */ CompositeEntityCollection.prototype.getCollection = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required.", "index"); } //>>includeEnd('debug'); return this._collections[index]; }; /** * Gets the number of collections in this composite. */ CompositeEntityCollection.prototype.getCollectionsLength = function () { return this._collections.length; }; function getCollectionIndex(collections, collection) { //>>includeStart('debug', pragmas.debug); if (!defined(collection)) { throw new DeveloperError("collection is required."); } //>>includeEnd('debug'); var index = collections.indexOf(collection); //>>includeStart('debug', pragmas.debug); if (index === -1) { throw new DeveloperError("collection is not in this composite."); } //>>includeEnd('debug'); return index; } function swapCollections(composite, i, j) { var arr = composite._collections; i = CesiumMath.clamp(i, 0, arr.length - 1); j = CesiumMath.clamp(j, 0, arr.length - 1); if (i === j) { return; } var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; recomposite(composite); } /** * Raises a collection up one position in the composite. * * @param {EntityCollection} collection the collection to move. * * @exception {DeveloperError} collection is not in this composite. */ CompositeEntityCollection.prototype.raiseCollection = function (collection) { var index = getCollectionIndex(this._collections, collection); swapCollections(this, index, index + 1); }; /** * Lowers a collection down one position in the composite. * * @param {EntityCollection} collection the collection to move. * * @exception {DeveloperError} collection is not in this composite. */ CompositeEntityCollection.prototype.lowerCollection = function (collection) { var index = getCollectionIndex(this._collections, collection); swapCollections(this, index, index - 1); }; /** * Raises a collection to the top of the composite. * * @param {EntityCollection} collection the collection to move. * * @exception {DeveloperError} collection is not in this composite. */ CompositeEntityCollection.prototype.raiseCollectionToTop = function ( collection ) { var index = getCollectionIndex(this._collections, collection); if (index === this._collections.length - 1) { return; } this._collections.splice(index, 1); this._collections.push(collection); recomposite(this); }; /** * Lowers a collection to the bottom of the composite. * * @param {EntityCollection} collection the collection to move. * * @exception {DeveloperError} collection is not in this composite. */ CompositeEntityCollection.prototype.lowerCollectionToBottom = function ( collection ) { var index = getCollectionIndex(this._collections, collection); if (index === 0) { return; } this._collections.splice(index, 1); this._collections.splice(0, 0, collection); recomposite(this); }; /** * Prevents {@link EntityCollection#collectionChanged} events from being raised * until a corresponding call is made to {@link EntityCollection#resumeEvents}, at which * point a single event will be raised that covers all suspended operations. * This allows for many items to be added and removed efficiently. * While events are suspended, recompositing of the collections will * also be suspended, as this can be a costly operation. * This function can be safely called multiple times as long as there * are corresponding calls to {@link EntityCollection#resumeEvents}. */ CompositeEntityCollection.prototype.suspendEvents = function () { this._suspendCount++; this._composite.suspendEvents(); }; /** * Resumes raising {@link EntityCollection#collectionChanged} events immediately * when an item is added or removed. Any modifications made while while events were suspended * will be triggered as a single event when this function is called. This function also ensures * the collection is recomposited if events are also resumed. * This function is reference counted and can safely be called multiple times as long as there * are corresponding calls to {@link EntityCollection#resumeEvents}. * * @exception {DeveloperError} resumeEvents can not be called before suspendEvents. */ CompositeEntityCollection.prototype.resumeEvents = function () { //>>includeStart('debug', pragmas.debug); if (this._suspendCount === 0) { throw new DeveloperError( "resumeEvents can not be called before suspendEvents." ); } //>>includeEnd('debug'); this._suspendCount--; // recomposite before triggering events (but only if required for performance) that might depend on a composited collection if (this._shouldRecomposite && this._suspendCount === 0) { recomposite(this); this._shouldRecomposite = false; } this._composite.resumeEvents(); }; /** * Computes the maximum availability of the entities in the collection. * If the collection contains a mix of infinitely available data and non-infinite data, * It will return the interval pertaining to the non-infinite data only. If all * data is infinite, an infinite interval will be returned. * * @returns {TimeInterval} The availability of entities in the collection. */ CompositeEntityCollection.prototype.computeAvailability = function () { return this._composite.computeAvailability(); }; /** * Gets an entity with the specified id. * * @param {String} id The id of the entity to retrieve. * @returns {Entity|undefined} The entity with the provided id or undefined if the id did not exist in the collection. */ CompositeEntityCollection.prototype.getById = function (id) { return this._composite.getById(id); }; CompositeEntityCollection.prototype._onCollectionChanged = function ( collection, added, removed ) { var collections = this._collectionsCopy; var collectionsLength = collections.length; var composite = this._composite; composite.suspendEvents(); var i; var q; var entity; var compositeEntity; var removedLength = removed.length; var eventHash = this._eventHash; var collectionId = collection.id; for (i = 0; i < removedLength; i++) { var removedEntity = removed[i]; unsubscribeFromEntity(this, eventHash, collectionId, removedEntity); var removedId = removedEntity.id; //Check if the removed entity exists in any of the remaining collections //If so, we clean and remerge it. for (q = collectionsLength - 1; q >= 0; q--) { entity = collections[q].getById(removedId); if (defined(entity)) { if (!defined(compositeEntity)) { compositeEntity = composite.getById(removedId); clean(compositeEntity); } compositeEntity.merge(entity); } } //We never retrieved the compositeEntity, which means it no longer //exists in any of the collections, remove it from the composite. if (!defined(compositeEntity)) { composite.removeById(removedId); } compositeEntity = undefined; } var addedLength = added.length; for (i = 0; i < addedLength; i++) { var addedEntity = added[i]; subscribeToEntity(this, eventHash, collectionId, addedEntity); var addedId = addedEntity.id; //We know the added entity exists in at least one collection, //but we need to check all collections and re-merge in order //to maintain the priority of properties. for (q = collectionsLength - 1; q >= 0; q--) { entity = collections[q].getById(addedId); if (defined(entity)) { if (!defined(compositeEntity)) { compositeEntity = composite.getById(addedId); if (!defined(compositeEntity)) { entityOptionsScratch.id = addedId; compositeEntity = new Entity(entityOptionsScratch); composite.add(compositeEntity); } else { clean(compositeEntity); } } compositeEntity.merge(entity); } } compositeEntity = undefined; } composite.resumeEvents(); }; CompositeEntityCollection.prototype._onDefinitionChanged = function ( entity, propertyName, newValue, oldValue ) { var collections = this._collections; var composite = this._composite; var collectionsLength = collections.length; var id = entity.id; var compositeEntity = composite.getById(id); var compositeProperty = compositeEntity[propertyName]; var newProperty = !defined(compositeProperty); var firstTime = true; for (var q = collectionsLength - 1; q >= 0; q--) { var innerEntity = collections[q].getById(entity.id); if (defined(innerEntity)) { var property = innerEntity[propertyName]; if (defined(property)) { if (firstTime) { firstTime = false; //We only want to clone if the property is also mergeable. //This ensures that leaf properties are referenced and not copied, //which is the entire point of compositing. if (defined(property.merge) && defined(property.clone)) { compositeProperty = property.clone(compositeProperty); } else { compositeProperty = property; break; } } compositeProperty.merge(property); } } } if ( newProperty && compositeEntity.propertyNames.indexOf(propertyName) === -1 ) { compositeEntity.addProperty(propertyName); } compositeEntity[propertyName] = compositeProperty; }; function subscribeAll(property, eventHelper, definitionChanged, intervals) { function callback() { definitionChanged.raiseEvent(property); } var items = []; eventHelper.removeAll(); var length = intervals.length; for (var i = 0; i < length; i++) { var interval = intervals.get(i); if (defined(interval.data) && items.indexOf(interval.data) === -1) { eventHelper.add(interval.data.definitionChanged, callback); } } } /** * A {@link Property} which is defined by a {@link TimeIntervalCollection}, where the * data property of each {@link TimeInterval} is another Property instance which is * evaluated at the provided time. * * @alias CompositeProperty * @constructor * * * @example * var constantProperty = ...; * var sampledProperty = ...; * * //Create a composite property from two previously defined properties * //where the property is valid on August 1st, 2012 and uses a constant * //property for the first half of the day and a sampled property for the * //remaining half. * var composite = new Cesium.CompositeProperty(); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T00:00:00.00Z/2012-08-01T12:00:00.00Z', * data : constantProperty * })); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T12:00:00.00Z/2012-08-02T00:00:00.00Z', * isStartIncluded : false, * isStopIncluded : false, * data : sampledProperty * })); * * @see CompositeMaterialProperty * @see CompositePositionProperty */ function CompositeProperty() { this._eventHelper = new EventHelper(); this._definitionChanged = new Event(); this._intervals = new TimeIntervalCollection(); this._intervals.changedEvent.addEventListener( CompositeProperty.prototype._intervalsChanged, this ); } Object.defineProperties(CompositeProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof CompositeProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._intervals.isEmpty; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value. * @memberof CompositeProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the interval collection. * @memberof CompositeProperty.prototype * * @type {TimeIntervalCollection} */ intervals: { get: function () { return this._intervals; }, }, }); /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ CompositeProperty.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required"); } //>>includeEnd('debug'); var innerProperty = this._intervals.findDataForIntervalContainingDate(time); if (defined(innerProperty)) { return innerProperty.getValue(time, result); } return undefined; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ CompositeProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof CompositeProperty && // this._intervals.equals(other._intervals, Property.equals)) ); }; /** * @private */ CompositeProperty.prototype._intervalsChanged = function () { subscribeAll( this, this._eventHelper, this._definitionChanged, this._intervals ); this._definitionChanged.raiseEvent(this); }; /** * A {@link CompositeProperty} which is also a {@link MaterialProperty}. * * @alias CompositeMaterialProperty * @constructor */ function CompositeMaterialProperty() { this._definitionChanged = new Event(); this._composite = new CompositeProperty(); this._composite.definitionChanged.addEventListener( CompositeMaterialProperty.prototype._raiseDefinitionChanged, this ); } Object.defineProperties(CompositeMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof CompositeMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._composite.isConstant; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value. * @memberof CompositeMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the interval collection. * @memberof CompositeMaterialProperty.prototype * * @type {TimeIntervalCollection} */ intervals: { get: function () { return this._composite._intervals; }, }, }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ CompositeMaterialProperty.prototype.getType = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required"); } //>>includeEnd('debug'); var innerProperty = this._composite._intervals.findDataForIntervalContainingDate( time ); if (defined(innerProperty)) { return innerProperty.getType(time); } return undefined; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ CompositeMaterialProperty.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required"); } //>>includeEnd('debug'); var innerProperty = this._composite._intervals.findDataForIntervalContainingDate( time ); if (defined(innerProperty)) { return innerProperty.getValue(time, result); } return undefined; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ CompositeMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof CompositeMaterialProperty && // this._composite.equals(other._composite, Property.equals)) ); }; /** * @private */ CompositeMaterialProperty.prototype._raiseDefinitionChanged = function () { this._definitionChanged.raiseEvent(this); }; /** * A {@link CompositeProperty} which is also a {@link PositionProperty}. * * @alias CompositePositionProperty * @constructor * * @param {ReferenceFrame} [referenceFrame=ReferenceFrame.FIXED] The reference frame in which the position is defined. */ function CompositePositionProperty(referenceFrame) { this._referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); this._definitionChanged = new Event(); this._composite = new CompositeProperty(); this._composite.definitionChanged.addEventListener( CompositePositionProperty.prototype._raiseDefinitionChanged, this ); } Object.defineProperties(CompositePositionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof CompositePositionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._composite.isConstant; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value. * @memberof CompositePositionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the interval collection. * @memberof CompositePositionProperty.prototype * * @type {TimeIntervalCollection} */ intervals: { get: function () { return this._composite.intervals; }, }, /** * Gets or sets the reference frame which this position presents itself as. * Each PositionProperty making up this object has it's own reference frame, * so this property merely exposes a "preferred" reference frame for clients * to use. * @memberof CompositePositionProperty.prototype * * @type {ReferenceFrame} */ referenceFrame: { get: function () { return this._referenceFrame; }, set: function (value) { this._referenceFrame = value; }, }, }); /** * Gets the value of the property at the provided time in the fixed frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ CompositePositionProperty.prototype.getValue = function (time, result) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; /** * Gets the value of the property at the provided time and in the provided reference frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ CompositePositionProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!defined(referenceFrame)) { throw new DeveloperError("referenceFrame is required."); } //>>includeEnd('debug'); var innerProperty = this._composite._intervals.findDataForIntervalContainingDate( time ); if (defined(innerProperty)) { return innerProperty.getValueInReferenceFrame(time, referenceFrame, result); } return undefined; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ CompositePositionProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof CompositePositionProperty && // this._referenceFrame === other._referenceFrame && // this._composite.equals(other._composite, Property.equals)) ); }; /** * @private */ CompositePositionProperty.prototype._raiseDefinitionChanged = function () { this._definitionChanged.raiseEvent(this); }; var defaultZIndex$1 = new ConstantProperty(0); /** * An abstract class for updating ground geometry entities. * @constructor * @alias GroundGeometryUpdater * @param {Object} options An object with the following properties: * @param {Entity} options.entity The entity containing the geometry to be visualized. * @param {Scene} options.scene The scene where visualization is taking place. * @param {Object} options.geometryOptions Options for the geometry * @param {String} options.geometryPropertyName The geometry property name * @param {String[]} options.observedPropertyNames The entity properties this geometry cares about */ function GroundGeometryUpdater(options) { GeometryUpdater.call(this, options); this._zIndex = 0; this._terrainOffsetProperty = undefined; } if (defined(Object.create)) { GroundGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); GroundGeometryUpdater.prototype.constructor = GroundGeometryUpdater; } Object.defineProperties(GroundGeometryUpdater.prototype, { /** * Gets the zindex * @type {Number} * @memberof GroundGeometryUpdater.prototype * @readonly */ zIndex: { get: function () { return this._zIndex; }, }, /** * Gets the terrain offset property * @type {TerrainOffsetProperty} * @memberof GroundGeometryUpdater.prototype * @readonly * @private */ terrainOffsetProperty: { get: function () { return this._terrainOffsetProperty; }, }, }); GroundGeometryUpdater.prototype._isOnTerrain = function (entity, geometry) { return ( this._fillEnabled && !defined(geometry.height) && !defined(geometry.extrudedHeight) && GroundPrimitive.isSupported(this._scene) ); }; GroundGeometryUpdater.prototype._getIsClosed = function (options) { var height = options.height; var extrudedHeight = options.extrudedHeight; return height === 0 || (defined(extrudedHeight) && extrudedHeight !== height); }; GroundGeometryUpdater.prototype._computeCenter = DeveloperError.throwInstantiationError; GroundGeometryUpdater.prototype._onEntityPropertyChanged = function ( entity, propertyName, newValue, oldValue ) { GeometryUpdater.prototype._onEntityPropertyChanged.call( this, entity, propertyName, newValue, oldValue ); if (this._observedPropertyNames.indexOf(propertyName) === -1) { return; } var geometry = this._entity[this._geometryPropertyName]; if (!defined(geometry)) { return; } if ( defined(geometry.zIndex) && (defined(geometry.height) || defined(geometry.extrudedHeight)) ) { oneTimeWarning(oneTimeWarning.geometryZIndex); } this._zIndex = defaultValue(geometry.zIndex, defaultZIndex$1); if (defined(this._terrainOffsetProperty)) { this._terrainOffsetProperty.destroy(); this._terrainOffsetProperty = undefined; } var heightReferenceProperty = geometry.heightReference; var extrudedHeightReferenceProperty = geometry.extrudedHeightReference; if ( defined(heightReferenceProperty) || defined(extrudedHeightReferenceProperty) ) { var centerPosition = new CallbackProperty( this._computeCenter.bind(this), !this._dynamic ); this._terrainOffsetProperty = new TerrainOffsetProperty( this._scene, centerPosition, heightReferenceProperty, extrudedHeightReferenceProperty ); } }; /** * Destroys and resources used by the object. Once an object is destroyed, it should not be used. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ GroundGeometryUpdater.prototype.destroy = function () { if (defined(this._terrainOffsetProperty)) { this._terrainOffsetProperty.destroy(); this._terrainOffsetProperty = undefined; } GeometryUpdater.prototype.destroy.call(this); }; /** * @private */ GroundGeometryUpdater.getGeometryHeight = function (height, heightReference) { //>>includeStart('debug', pragmas.debug); Check.defined("heightReference", heightReference); //>>includeEnd('debug'); if (!defined(height)) { if (heightReference !== HeightReference$1.NONE) { oneTimeWarning(oneTimeWarning.geometryHeightReference); } return; } if (heightReference !== HeightReference$1.CLAMP_TO_GROUND) { return height; } return 0.0; }; /** * @private */ GroundGeometryUpdater.getGeometryExtrudedHeight = function ( extrudedHeight, extrudedHeightReference ) { //>>includeStart('debug', pragmas.debug); Check.defined("extrudedHeightReference", extrudedHeightReference); //>>includeEnd('debug'); if (!defined(extrudedHeight)) { if (extrudedHeightReference !== HeightReference$1.NONE) { oneTimeWarning(oneTimeWarning.geometryExtrudedHeightReference); } return; } if (extrudedHeightReference !== HeightReference$1.CLAMP_TO_GROUND) { return extrudedHeight; } return GroundGeometryUpdater.CLAMP_TO_GROUND; }; /** * @private */ GroundGeometryUpdater.CLAMP_TO_GROUND = "clamp"; /** * @private */ GroundGeometryUpdater.computeGeometryOffsetAttribute = function ( height, heightReference, extrudedHeight, extrudedHeightReference ) { if (!defined(height) || !defined(heightReference)) { heightReference = HeightReference$1.NONE; } if (!defined(extrudedHeight) || !defined(extrudedHeightReference)) { extrudedHeightReference = HeightReference$1.NONE; } var n = 0; if (heightReference !== HeightReference$1.NONE) { n++; } if (extrudedHeightReference === HeightReference$1.RELATIVE_TO_GROUND) { n++; } if (n === 2) { return GeometryOffsetAttribute$1.ALL; } if (n === 1) { return GeometryOffsetAttribute$1.TOP; } return undefined; }; var scratchColor$d = new Color(); var defaultOffset$9 = Cartesian3.ZERO; var offsetScratch$8 = new Cartesian3(); var scratchRectangle$4 = new Rectangle(); function CorridorGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.positions = undefined; this.width = undefined; this.cornerType = undefined; this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for corridors. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias CorridorGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function CorridorGeometryUpdater(entity, scene) { GroundGeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new CorridorGeometryOptions(entity), geometryPropertyName: "corridor", observedPropertyNames: ["availability", "corridor"], }); this._onEntityPropertyChanged(entity, "corridor", entity.corridor, undefined); } if (defined(Object.create)) { CorridorGeometryUpdater.prototype = Object.create( GroundGeometryUpdater.prototype ); CorridorGeometryUpdater.prototype.constructor = CorridorGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ CorridorGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( this._distanceDisplayConditionProperty.getValue(time) ), offset: undefined, color: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$d); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$9, offsetScratch$8 ) ); } return new GeometryInstance({ id: entity, geometry: new CorridorGeometry(this._options), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ CorridorGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$d ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( this._distanceDisplayConditionProperty.getValue(time) ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$9, offsetScratch$8 ) ); } return new GeometryInstance({ id: entity, geometry: new CorridorOutlineGeometry(this._options), attributes: attributes, }); }; CorridorGeometryUpdater.prototype._computeCenter = function (time, result) { var positions = Property.getValueOrUndefined( this._entity.corridor.positions, time ); if (!defined(positions) || positions.length === 0) { return; } return Cartesian3.clone( positions[Math.floor(positions.length / 2.0)], result ); }; CorridorGeometryUpdater.prototype._isHidden = function (entity, corridor) { return ( !defined(corridor.positions) || !defined(corridor.width) || GeometryUpdater.prototype._isHidden.call(this, entity, corridor) ); }; CorridorGeometryUpdater.prototype._isDynamic = function (entity, corridor) { return ( !corridor.positions.isConstant || // !Property.isConstant(corridor.height) || // !Property.isConstant(corridor.extrudedHeight) || // !Property.isConstant(corridor.granularity) || // !Property.isConstant(corridor.width) || // !Property.isConstant(corridor.outlineWidth) || // !Property.isConstant(corridor.cornerType) || // !Property.isConstant(corridor.zIndex) || // (this._onTerrain && !Property.isConstant(this._materialProperty) && !(this._materialProperty instanceof ColorMaterialProperty)) ); }; CorridorGeometryUpdater.prototype._setStaticOptions = function ( entity, corridor ) { var heightValue = Property.getValueOrUndefined( corridor.height, Iso8601.MINIMUM_VALUE ); var heightReferenceValue = Property.getValueOrDefault( corridor.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( corridor.extrudedHeight, Iso8601.MINIMUM_VALUE ); var extrudedHeightReferenceValue = Property.getValueOrDefault( corridor.extrudedHeightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } var options = this._options; options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.positions = corridor.positions.getValue( Iso8601.MINIMUM_VALUE, options.positions ); options.width = corridor.width.getValue(Iso8601.MINIMUM_VALUE); options.granularity = Property.getValueOrUndefined( corridor.granularity, Iso8601.MINIMUM_VALUE ); options.cornerType = Property.getValueOrUndefined( corridor.cornerType, Iso8601.MINIMUM_VALUE ); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( CorridorGeometry.computeRectangle(options, scratchRectangle$4) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; CorridorGeometryUpdater.DynamicGeometryUpdater = DynamicCorridorGeometryUpdater; /** * @private */ function DynamicCorridorGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicCorridorGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicCorridorGeometryUpdater.prototype.constructor = DynamicCorridorGeometryUpdater; } DynamicCorridorGeometryUpdater.prototype._isHidden = function ( entity, corridor, time ) { var options = this._options; return ( !defined(options.positions) || !defined(options.width) || DynamicGeometryUpdater$1.prototype._isHidden.call( this, entity, corridor, time ) ); }; DynamicCorridorGeometryUpdater.prototype._setOptions = function ( entity, corridor, time ) { var options = this._options; var heightValue = Property.getValueOrUndefined(corridor.height, time); var heightReferenceValue = Property.getValueOrDefault( corridor.heightReference, time, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( corridor.extrudedHeight, time ); var extrudedHeightReferenceValue = Property.getValueOrDefault( corridor.extrudedHeightReference, time, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } options.positions = Property.getValueOrUndefined(corridor.positions, time); options.width = Property.getValueOrUndefined(corridor.width, time); options.granularity = Property.getValueOrUndefined( corridor.granularity, time ); options.cornerType = Property.getValueOrUndefined(corridor.cornerType, time); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( CorridorGeometry.computeRectangle(options, scratchRectangle$4) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; /** * Defines the interface for data sources, which turn arbitrary data into a * {@link EntityCollection} for generic consumption. This object is an interface * for documentation purposes and is not intended to be instantiated directly. * @alias DataSource * @constructor * * @see Entity * @see DataSourceDisplay */ function DataSource() { DeveloperError.throwInstantiationError(); } Object.defineProperties(DataSource.prototype, { /** * Gets a human-readable name for this instance. * @memberof DataSource.prototype * @type {String} */ name: { get: DeveloperError.throwInstantiationError, }, /** * Gets the preferred clock settings for this data source. * @memberof DataSource.prototype * @type {DataSourceClock} */ clock: { get: DeveloperError.throwInstantiationError, }, /** * Gets the collection of {@link Entity} instances. * @memberof DataSource.prototype * @type {EntityCollection} */ entities: { get: DeveloperError.throwInstantiationError, }, /** * Gets a value indicating if the data source is currently loading data. * @memberof DataSource.prototype * @type {Boolean} */ isLoading: { get: DeveloperError.throwInstantiationError, }, /** * Gets an event that will be raised when the underlying data changes. * @memberof DataSource.prototype * @type {Event} */ changedEvent: { get: DeveloperError.throwInstantiationError, }, /** * Gets an event that will be raised if an error is encountered during processing. * @memberof DataSource.prototype * @type {Event} */ errorEvent: { get: DeveloperError.throwInstantiationError, }, /** * Gets an event that will be raised when the value of isLoading changes. * @memberof DataSource.prototype * @type {Event} */ loadingEvent: { get: DeveloperError.throwInstantiationError, }, /** * Gets whether or not this data source should be displayed. * @memberof DataSource.prototype * @type {Boolean} */ show: { get: DeveloperError.throwInstantiationError, }, /** * Gets or sets the clustering options for this data source. This object can be shared between multiple data sources. * * @memberof DataSource.prototype * @type {EntityCluster} */ clustering: { get: DeveloperError.throwInstantiationError, }, }); /** * Updates the data source to the provided time. This function is optional and * is not required to be implemented. It is provided for data sources which * retrieve data based on the current animation time or scene state. * If implemented, update will be called by {@link DataSourceDisplay} once a frame. * * @param {JulianDate} time The simulation time. * @returns {Boolean} True if this data source is ready to be displayed at the provided time, false otherwise. */ DataSource.prototype.update = function (time) { DeveloperError.throwInstantiationError(); }; /** * @private */ DataSource.setLoading = function (dataSource, isLoading) { if (dataSource._isLoading !== isLoading) { if (isLoading) { dataSource._entityCollection.suspendEvents(); } else { dataSource._entityCollection.resumeEvents(); } dataSource._isLoading = isLoading; dataSource._loading.raiseEvent(dataSource, isLoading); } }; /** * A graphical point positioned in the 3D scene, that is created * and rendered using a {@link PointPrimitiveCollection}. A point is created and its initial * properties are set by calling {@link PointPrimitiveCollection#add}. * * @alias PointPrimitive * * @performance Reading a property, e.g., {@link PointPrimitive#show}, is constant time. * Assigning to a property is constant time but results in * CPU to GPU traffic when {@link PointPrimitiveCollection#update} is called. The per-pointPrimitive traffic is * the same regardless of how many properties were updated. If most pointPrimitives in a collection need to be * updated, it may be more efficient to clear the collection with {@link PointPrimitiveCollection#removeAll} * and add new pointPrimitives instead of modifying each one. * * @exception {DeveloperError} scaleByDistance.far must be greater than scaleByDistance.near * @exception {DeveloperError} translucencyByDistance.far must be greater than translucencyByDistance.near * @exception {DeveloperError} distanceDisplayCondition.far must be greater than distanceDisplayCondition.near * * @see PointPrimitiveCollection * @see PointPrimitiveCollection#add * * @internalConstructor * @class * * @demo {@link https://sandcastle.cesium.com/index.html?src=Points.html|Cesium Sandcastle Points Demo} */ function PointPrimitive(options, pointPrimitiveCollection) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); //>>includeStart('debug', pragmas.debug); if ( defined(options.disableDepthTestDistance) && options.disableDepthTestDistance < 0.0 ) { throw new DeveloperError( "disableDepthTestDistance must be greater than or equal to 0.0." ); } //>>includeEnd('debug'); var translucencyByDistance = options.translucencyByDistance; var scaleByDistance = options.scaleByDistance; var distanceDisplayCondition = options.distanceDisplayCondition; if (defined(translucencyByDistance)) { //>>includeStart('debug', pragmas.debug); if (translucencyByDistance.far <= translucencyByDistance.near) { throw new DeveloperError( "translucencyByDistance.far must be greater than translucencyByDistance.near." ); } //>>includeEnd('debug'); translucencyByDistance = NearFarScalar.clone(translucencyByDistance); } if (defined(scaleByDistance)) { //>>includeStart('debug', pragmas.debug); if (scaleByDistance.far <= scaleByDistance.near) { throw new DeveloperError( "scaleByDistance.far must be greater than scaleByDistance.near." ); } //>>includeEnd('debug'); scaleByDistance = NearFarScalar.clone(scaleByDistance); } if (defined(distanceDisplayCondition)) { //>>includeStart('debug', pragmas.debug); if (distanceDisplayCondition.far <= distanceDisplayCondition.near) { throw new DeveloperError( "distanceDisplayCondition.far must be greater than distanceDisplayCondition.near." ); } //>>includeEnd('debug'); distanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition ); } this._show = defaultValue(options.show, true); this._position = Cartesian3.clone( defaultValue(options.position, Cartesian3.ZERO) ); this._actualPosition = Cartesian3.clone(this._position); // For columbus view and 2D this._color = Color.clone(defaultValue(options.color, Color.WHITE)); this._outlineColor = Color.clone( defaultValue(options.outlineColor, Color.TRANSPARENT) ); this._outlineWidth = defaultValue(options.outlineWidth, 0.0); this._pixelSize = defaultValue(options.pixelSize, 10.0); this._scaleByDistance = scaleByDistance; this._translucencyByDistance = translucencyByDistance; this._distanceDisplayCondition = distanceDisplayCondition; this._disableDepthTestDistance = defaultValue( options.disableDepthTestDistance, 0.0 ); this._id = options.id; this._collection = defaultValue(options.collection, pointPrimitiveCollection); this._clusterShow = true; this._pickId = undefined; this._pointPrimitiveCollection = pointPrimitiveCollection; this._dirty = false; this._index = -1; //Used only by PointPrimitiveCollection } var SHOW_INDEX$1 = (PointPrimitive.SHOW_INDEX = 0); var POSITION_INDEX$1 = (PointPrimitive.POSITION_INDEX = 1); var COLOR_INDEX$1 = (PointPrimitive.COLOR_INDEX = 2); var OUTLINE_COLOR_INDEX$1 = (PointPrimitive.OUTLINE_COLOR_INDEX = 3); var OUTLINE_WIDTH_INDEX$1 = (PointPrimitive.OUTLINE_WIDTH_INDEX = 4); var PIXEL_SIZE_INDEX$1 = (PointPrimitive.PIXEL_SIZE_INDEX = 5); var SCALE_BY_DISTANCE_INDEX$1 = (PointPrimitive.SCALE_BY_DISTANCE_INDEX = 6); var TRANSLUCENCY_BY_DISTANCE_INDEX$1 = (PointPrimitive.TRANSLUCENCY_BY_DISTANCE_INDEX = 7); var DISTANCE_DISPLAY_CONDITION_INDEX$1 = (PointPrimitive.DISTANCE_DISPLAY_CONDITION_INDEX = 8); var DISABLE_DEPTH_DISTANCE_INDEX$1 = (PointPrimitive.DISABLE_DEPTH_DISTANCE_INDEX = 9); PointPrimitive.NUMBER_OF_PROPERTIES = 10; function makeDirty(pointPrimitive, propertyChanged) { var pointPrimitiveCollection = pointPrimitive._pointPrimitiveCollection; if (defined(pointPrimitiveCollection)) { pointPrimitiveCollection._updatePointPrimitive( pointPrimitive, propertyChanged ); pointPrimitive._dirty = true; } } Object.defineProperties(PointPrimitive.prototype, { /** * Determines if this point will be shown. Use this to hide or show a point, instead * of removing it and re-adding it to the collection. * @memberof PointPrimitive.prototype * @type {Boolean} */ show: { get: function () { return this._show; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._show !== value) { this._show = value; makeDirty(this, SHOW_INDEX$1); } }, }, /** * Gets or sets the Cartesian position of this point. * @memberof PointPrimitive.prototype * @type {Cartesian3} */ position: { get: function () { return this._position; }, set: function (value) { //>>includeStart('debug', pragmas.debug) if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var position = this._position; if (!Cartesian3.equals(position, value)) { Cartesian3.clone(value, position); Cartesian3.clone(value, this._actualPosition); makeDirty(this, POSITION_INDEX$1); } }, }, /** * Gets or sets near and far scaling properties of a point based on the point's distance from the camera. * A point's scale will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the point's scale remains clamped to the nearest bound. This scale * multiplies the pixelSize and outlineWidth to affect the total size of the point. If undefined, * scaleByDistance will be disabled. * @memberof PointPrimitive.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a pointPrimitive's scaleByDistance to scale to 15 when the * // camera is 1500 meters from the pointPrimitive and disappear as * // the camera distance approaches 8.0e6 meters. * p.scaleByDistance = new Cesium.NearFarScalar(1.5e2, 15, 8.0e6, 0.0); * * @example * // Example 2. * // disable scaling by distance * p.scaleByDistance = undefined; */ scaleByDistance: { get: function () { return this._scaleByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var scaleByDistance = this._scaleByDistance; if (!NearFarScalar.equals(scaleByDistance, value)) { this._scaleByDistance = NearFarScalar.clone(value, scaleByDistance); makeDirty(this, SCALE_BY_DISTANCE_INDEX$1); } }, }, /** * Gets or sets near and far translucency properties of a point based on the point's distance from the camera. * A point's translucency will interpolate between the {@link NearFarScalar#nearValue} and * {@link NearFarScalar#farValue} while the camera distance falls within the lower and upper bounds * of the specified {@link NearFarScalar#near} and {@link NearFarScalar#far}. * Outside of these ranges the point's translucency remains clamped to the nearest bound. If undefined, * translucencyByDistance will be disabled. * @memberof PointPrimitive.prototype * @type {NearFarScalar} * * @example * // Example 1. * // Set a point's translucency to 1.0 when the * // camera is 1500 meters from the point and disappear as * // the camera distance approaches 8.0e6 meters. * p.translucencyByDistance = new Cesium.NearFarScalar(1.5e2, 1.0, 8.0e6, 0.0); * * @example * // Example 2. * // disable translucency by distance * p.translucencyByDistance = undefined; */ translucencyByDistance: { get: function () { return this._translucencyByDistance; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError( "far distance must be greater than near distance." ); } //>>includeEnd('debug'); var translucencyByDistance = this._translucencyByDistance; if (!NearFarScalar.equals(translucencyByDistance, value)) { this._translucencyByDistance = NearFarScalar.clone( value, translucencyByDistance ); makeDirty(this, TRANSLUCENCY_BY_DISTANCE_INDEX$1); } }, }, /** * Gets or sets the inner size of the point in pixels. * @memberof PointPrimitive.prototype * @type {Number} */ pixelSize: { get: function () { return this._pixelSize; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._pixelSize !== value) { this._pixelSize = value; makeDirty(this, PIXEL_SIZE_INDEX$1); } }, }, /** * Gets or sets the inner color of the point. * The red, green, blue, and alpha values are indicated by value's red, green, * blue, and alpha properties as shown in Example 1. These components range from 0.0 * (no intensity) to 1.0 (full intensity). * @memberof PointPrimitive.prototype * @type {Color} * * @example * // Example 1. Assign yellow. * p.color = Cesium.Color.YELLOW; * * @example * // Example 2. Make a pointPrimitive 50% translucent. * p.color = new Cesium.Color(1.0, 1.0, 1.0, 0.5); */ color: { get: function () { return this._color; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var color = this._color; if (!Color.equals(color, value)) { Color.clone(value, color); makeDirty(this, COLOR_INDEX$1); } }, }, /** * Gets or sets the outline color of the point. * @memberof PointPrimitive.prototype * @type {Color} */ outlineColor: { get: function () { return this._outlineColor; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); var outlineColor = this._outlineColor; if (!Color.equals(outlineColor, value)) { Color.clone(value, outlineColor); makeDirty(this, OUTLINE_COLOR_INDEX$1); } }, }, /** * Gets or sets the outline width in pixels. This width adds to pixelSize, * increasing the total size of the point. * @memberof PointPrimitive.prototype * @type {Number} */ outlineWidth: { get: function () { return this._outlineWidth; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); } //>>includeEnd('debug'); if (this._outlineWidth !== value) { this._outlineWidth = value; makeDirty(this, OUTLINE_WIDTH_INDEX$1); } }, }, /** * Gets or sets the condition specifying at what distance from the camera that this point will be displayed. * @memberof PointPrimitive.prototype * @type {DistanceDisplayCondition} * @default undefined */ distanceDisplayCondition: { get: function () { return this._distanceDisplayCondition; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (defined(value) && value.far <= value.near) { throw new DeveloperError("far must be greater than near"); } //>>includeEnd('debug'); if ( !DistanceDisplayCondition.equals(this._distanceDisplayCondition, value) ) { this._distanceDisplayCondition = DistanceDisplayCondition.clone( value, this._distanceDisplayCondition ); makeDirty(this, DISTANCE_DISPLAY_CONDITION_INDEX$1); } }, }, /** * Gets or sets the distance from the camera at which to disable the depth test to, for example, prevent clipping against terrain. * When set to zero, the depth test is always applied. When set to Number.POSITIVE_INFINITY, the depth test is never applied. * @memberof PointPrimitive.prototype * @type {Number} * @default 0.0 */ disableDepthTestDistance: { get: function () { return this._disableDepthTestDistance; }, set: function (value) { if (this._disableDepthTestDistance !== value) { //>>includeStart('debug', pragmas.debug); if (!defined(value) || value < 0.0) { throw new DeveloperError( "disableDepthTestDistance must be greater than or equal to 0.0." ); } //>>includeEnd('debug'); this._disableDepthTestDistance = value; makeDirty(this, DISABLE_DEPTH_DISTANCE_INDEX$1); } }, }, /** * Gets or sets the user-defined value returned when the point is picked. * @memberof PointPrimitive.prototype * @type {*} */ id: { get: function () { return this._id; }, set: function (value) { this._id = value; if (defined(this._pickId)) { this._pickId.object.id = value; } }, }, /** * @private */ pickId: { get: function () { return this._pickId; }, }, /** * Determines whether or not this point will be shown or hidden because it was clustered. * @memberof PointPrimitive.prototype * @type {Boolean} * @private */ clusterShow: { get: function () { return this._clusterShow; }, set: function (value) { if (this._clusterShow !== value) { this._clusterShow = value; makeDirty(this, SHOW_INDEX$1); } }, }, }); PointPrimitive.prototype.getPickId = function (context) { if (!defined(this._pickId)) { this._pickId = context.createPickId({ primitive: this, collection: this._collection, id: this._id, }); } return this._pickId; }; PointPrimitive.prototype._getActualPosition = function () { return this._actualPosition; }; PointPrimitive.prototype._setActualPosition = function (value) { Cartesian3.clone(value, this._actualPosition); makeDirty(this, POSITION_INDEX$1); }; var tempCartesian3 = new Cartesian4(); PointPrimitive._computeActualPosition = function ( position, frameState, modelMatrix ) { if (frameState.mode === SceneMode$1.SCENE3D) { return position; } Matrix4.multiplyByPoint(modelMatrix, position, tempCartesian3); return SceneTransforms.computeActualWgs84Position(frameState, tempCartesian3); }; var scratchCartesian4$2 = new Cartesian4(); // This function is basically a stripped-down JavaScript version of PointPrimitiveCollectionVS.glsl PointPrimitive._computeScreenSpacePosition = function ( modelMatrix, position, scene, result ) { // Model to world coordinates var positionWorld = Matrix4.multiplyByVector( modelMatrix, Cartesian4.fromElements( position.x, position.y, position.z, 1, scratchCartesian4$2 ), scratchCartesian4$2 ); var positionWC = SceneTransforms.wgs84ToWindowCoordinates( scene, positionWorld, result ); return positionWC; }; /** * Computes the screen-space position of the point's origin. * The screen space origin is the top, left corner of the canvas; x increases from * left to right, and y increases from top to bottom. * * @param {Scene} scene The scene. * @param {Cartesian2} [result] The object onto which to store the result. * @returns {Cartesian2} The screen-space position of the point. * * @exception {DeveloperError} PointPrimitive must be in a collection. * * @example * console.log(p.computeScreenSpacePosition(scene).toString()); */ PointPrimitive.prototype.computeScreenSpacePosition = function (scene, result) { var pointPrimitiveCollection = this._pointPrimitiveCollection; if (!defined(result)) { result = new Cartesian2(); } //>>includeStart('debug', pragmas.debug); if (!defined(pointPrimitiveCollection)) { throw new DeveloperError("PointPrimitive must be in a collection."); } if (!defined(scene)) { throw new DeveloperError("scene is required."); } //>>includeEnd('debug'); var modelMatrix = pointPrimitiveCollection.modelMatrix; var windowCoordinates = PointPrimitive._computeScreenSpacePosition( modelMatrix, this._actualPosition, scene, result ); if (!defined(windowCoordinates)) { return undefined; } windowCoordinates.y = scene.canvas.clientHeight - windowCoordinates.y; return windowCoordinates; }; /** * Gets a point's screen space bounding box centered around screenSpacePosition. * @param {PointPrimitive} point The point to get the screen space bounding box for. * @param {Cartesian2} screenSpacePosition The screen space center of the label. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The screen space bounding box. * * @private */ PointPrimitive.getScreenSpaceBoundingBox = function ( point, screenSpacePosition, result ) { var size = point.pixelSize; var halfSize = size * 0.5; var x = screenSpacePosition.x - halfSize; var y = screenSpacePosition.y - halfSize; var width = size; var height = size; if (!defined(result)) { result = new BoundingRectangle(); } result.x = x; result.y = y; result.width = width; result.height = height; return result; }; /** * Determines if this point equals another point. Points are equal if all their properties * are equal. Points in different collections can be equal. * * @param {PointPrimitive} other The point to compare for equality. * @returns {Boolean} true if the points are equal; otherwise, false. */ PointPrimitive.prototype.equals = function (other) { return ( this === other || (defined(other) && this._id === other._id && Cartesian3.equals(this._position, other._position) && Color.equals(this._color, other._color) && this._pixelSize === other._pixelSize && this._outlineWidth === other._outlineWidth && this._show === other._show && Color.equals(this._outlineColor, other._outlineColor) && NearFarScalar.equals(this._scaleByDistance, other._scaleByDistance) && NearFarScalar.equals( this._translucencyByDistance, other._translucencyByDistance ) && DistanceDisplayCondition.equals( this._distanceDisplayCondition, other._distanceDisplayCondition ) && this._disableDepthTestDistance === other._disableDepthTestDistance) ); }; PointPrimitive.prototype._destroy = function () { this._pickId = this._pickId && this._pickId.destroy(); this._pointPrimitiveCollection = undefined; }; //This file is automatically rebuilt by the Cesium build process. var PointPrimitiveCollectionFS = "varying vec4 v_color;\n\ varying vec4 v_outlineColor;\n\ varying float v_innerPercent;\n\ varying float v_pixelDistance;\n\ varying vec4 v_pickColor;\n\ \n\ void main()\n\ {\n\ // The distance in UV space from this fragment to the center of the point, at most 0.5.\n\ float distanceToCenter = length(gl_PointCoord - vec2(0.5));\n\ // The max distance stops one pixel shy of the edge to leave space for anti-aliasing.\n\ float maxDistance = max(0.0, 0.5 - v_pixelDistance);\n\ float wholeAlpha = 1.0 - smoothstep(maxDistance, 0.5, distanceToCenter);\n\ float innerAlpha = 1.0 - smoothstep(maxDistance * v_innerPercent, 0.5 * v_innerPercent, distanceToCenter);\n\ \n\ vec4 color = mix(v_outlineColor, v_color, innerAlpha);\n\ color.a *= wholeAlpha;\n\ \n\ // Fully transparent parts of the billboard are not pickable.\n\ #if !defined(OPAQUE) && !defined(TRANSLUCENT)\n\ if (color.a < 0.005) // matches 0/255 and 1/255\n\ {\n\ discard;\n\ }\n\ #else\n\ // The billboard is rendered twice. The opaque pass discards translucent fragments\n\ // and the translucent pass discards opaque fragments.\n\ #ifdef OPAQUE\n\ if (color.a < 0.995) // matches < 254/255\n\ {\n\ discard;\n\ }\n\ #else\n\ if (color.a >= 0.995) // matches 254/255 and 255/255\n\ {\n\ discard;\n\ }\n\ #endif\n\ #endif\n\ \n\ gl_FragColor = czm_gammaCorrect(color);\n\ czm_writeLogDepth();\n\ }\n\ "; //This file is automatically rebuilt by the Cesium build process. var PointPrimitiveCollectionVS = "uniform float u_maxTotalPointSize;\n\ \n\ attribute vec4 positionHighAndSize;\n\ attribute vec4 positionLowAndOutline;\n\ attribute vec4 compressedAttribute0; // color, outlineColor, pick color\n\ attribute vec4 compressedAttribute1; // show, translucency by distance, some free space\n\ attribute vec4 scaleByDistance; // near, nearScale, far, farScale\n\ attribute vec3 distanceDisplayConditionAndDisableDepth; // near, far, disableDepthTestDistance\n\ \n\ varying vec4 v_color;\n\ varying vec4 v_outlineColor;\n\ varying float v_innerPercent;\n\ varying float v_pixelDistance;\n\ varying vec4 v_pickColor;\n\ \n\ const float SHIFT_LEFT8 = 256.0;\n\ const float SHIFT_RIGHT8 = 1.0 / 256.0;\n\ \n\ void main()\n\ {\n\ // Modifying this shader may also require modifications to PointPrimitive._computeScreenSpacePosition\n\ \n\ // unpack attributes\n\ vec3 positionHigh = positionHighAndSize.xyz;\n\ vec3 positionLow = positionLowAndOutline.xyz;\n\ float outlineWidthBothSides = 2.0 * positionLowAndOutline.w;\n\ float totalSize = positionHighAndSize.w + outlineWidthBothSides;\n\ float outlinePercent = outlineWidthBothSides / totalSize;\n\ // Scale in response to browser-zoom.\n\ totalSize *= czm_pixelRatio;\n\ // Add padding for anti-aliasing on both sides.\n\ totalSize += 3.0;\n\ \n\ float temp = compressedAttribute1.x * SHIFT_RIGHT8;\n\ float show = floor(temp);\n\ \n\ #ifdef EYE_DISTANCE_TRANSLUCENCY\n\ vec4 translucencyByDistance;\n\ translucencyByDistance.x = compressedAttribute1.z;\n\ translucencyByDistance.z = compressedAttribute1.w;\n\ \n\ translucencyByDistance.y = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0;\n\ \n\ temp = compressedAttribute1.y * SHIFT_RIGHT8;\n\ translucencyByDistance.w = ((temp - floor(temp)) * SHIFT_LEFT8) / 255.0;\n\ #endif\n\ \n\ ///////////////////////////////////////////////////////////////////////////\n\ \n\ vec4 color;\n\ vec4 outlineColor;\n\ vec4 pickColor;\n\ \n\ // compressedAttribute0.z => pickColor.rgb\n\ \n\ temp = compressedAttribute0.z * SHIFT_RIGHT8;\n\ pickColor.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ pickColor.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ pickColor.r = floor(temp);\n\ \n\ // compressedAttribute0.x => color.rgb\n\ \n\ temp = compressedAttribute0.x * SHIFT_RIGHT8;\n\ color.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ color.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ color.r = floor(temp);\n\ \n\ // compressedAttribute0.y => outlineColor.rgb\n\ \n\ temp = compressedAttribute0.y * SHIFT_RIGHT8;\n\ outlineColor.b = (temp - floor(temp)) * SHIFT_LEFT8;\n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ outlineColor.g = (temp - floor(temp)) * SHIFT_LEFT8;\n\ outlineColor.r = floor(temp);\n\ \n\ // compressedAttribute0.w => color.a, outlineColor.a, pickColor.a\n\ \n\ temp = compressedAttribute0.w * SHIFT_RIGHT8;\n\ pickColor.a = (temp - floor(temp)) * SHIFT_LEFT8;\n\ pickColor = pickColor / 255.0;\n\ \n\ temp = floor(temp) * SHIFT_RIGHT8;\n\ outlineColor.a = (temp - floor(temp)) * SHIFT_LEFT8;\n\ outlineColor /= 255.0;\n\ color.a = floor(temp);\n\ color /= 255.0;\n\ \n\ ///////////////////////////////////////////////////////////////////////////\n\ \n\ vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);\n\ vec4 positionEC = czm_modelViewRelativeToEye * p;\n\ \n\ ///////////////////////////////////////////////////////////////////////////\n\ \n\ #if defined(EYE_DISTANCE_SCALING) || defined(EYE_DISTANCE_TRANSLUCENCY) || defined(DISTANCE_DISPLAY_CONDITION) || defined(DISABLE_DEPTH_DISTANCE)\n\ float lengthSq;\n\ if (czm_sceneMode == czm_sceneMode2D)\n\ {\n\ // 2D camera distance is a special case\n\ // treat all billboards as flattened to the z=0.0 plane\n\ lengthSq = czm_eyeHeight2D.y;\n\ }\n\ else\n\ {\n\ lengthSq = dot(positionEC.xyz, positionEC.xyz);\n\ }\n\ #endif\n\ \n\ #ifdef EYE_DISTANCE_SCALING\n\ totalSize *= czm_nearFarScalar(scaleByDistance, lengthSq);\n\ #endif\n\ // Clamp to max point size.\n\ totalSize = min(totalSize, u_maxTotalPointSize);\n\ // If size is too small, push vertex behind near plane for clipping.\n\ // Note that context.minimumAliasedPointSize \"will be at most 1.0\".\n\ if (totalSize < 1.0)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ totalSize = 1.0;\n\ }\n\ \n\ float translucency = 1.0;\n\ #ifdef EYE_DISTANCE_TRANSLUCENCY\n\ translucency = czm_nearFarScalar(translucencyByDistance, lengthSq);\n\ // push vertex behind near plane for clipping\n\ if (translucency < 0.004)\n\ {\n\ positionEC.xyz = vec3(0.0);\n\ }\n\ #endif\n\ \n\ #ifdef DISTANCE_DISPLAY_CONDITION\n\ float nearSq = distanceDisplayConditionAndDisableDepth.x;\n\ float farSq = distanceDisplayConditionAndDisableDepth.y;\n\ if (lengthSq < nearSq || lengthSq > farSq) {\n\ // push vertex behind camera to force it to be clipped\n\ positionEC.xyz = vec3(0.0, 0.0, 1.0);\n\ }\n\ #endif\n\ \n\ gl_Position = czm_projection * positionEC;\n\ czm_vertexLogDepth();\n\ \n\ #ifdef DISABLE_DEPTH_DISTANCE\n\ float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z;\n\ if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0)\n\ {\n\ disableDepthTestDistance = czm_minimumDisableDepthTestDistance;\n\ }\n\ \n\ if (disableDepthTestDistance != 0.0)\n\ {\n\ // Don't try to \"multiply both sides\" by w. Greater/less-than comparisons won't work for negative values of w.\n\ float zclip = gl_Position.z / gl_Position.w;\n\ bool clipped = (zclip < -1.0 || zclip > 1.0);\n\ if (!clipped && (disableDepthTestDistance < 0.0 || (lengthSq > 0.0 && lengthSq < disableDepthTestDistance)))\n\ {\n\ // Position z on the near plane.\n\ gl_Position.z = -gl_Position.w;\n\ #ifdef LOG_DEPTH\n\ czm_vertexLogDepth(vec4(czm_currentFrustum.x));\n\ #endif\n\ }\n\ }\n\ #endif\n\ \n\ v_color = color;\n\ v_color.a *= translucency * show;\n\ v_outlineColor = outlineColor;\n\ v_outlineColor.a *= translucency * show;\n\ \n\ v_innerPercent = 1.0 - outlinePercent;\n\ v_pixelDistance = 2.0 / totalSize;\n\ gl_PointSize = totalSize * show;\n\ gl_Position *= show;\n\ \n\ v_pickColor = pickColor;\n\ }\n\ "; var SHOW_INDEX = PointPrimitive.SHOW_INDEX; var POSITION_INDEX = PointPrimitive.POSITION_INDEX; var COLOR_INDEX = PointPrimitive.COLOR_INDEX; var OUTLINE_COLOR_INDEX = PointPrimitive.OUTLINE_COLOR_INDEX; var OUTLINE_WIDTH_INDEX = PointPrimitive.OUTLINE_WIDTH_INDEX; var PIXEL_SIZE_INDEX = PointPrimitive.PIXEL_SIZE_INDEX; var SCALE_BY_DISTANCE_INDEX = PointPrimitive.SCALE_BY_DISTANCE_INDEX; var TRANSLUCENCY_BY_DISTANCE_INDEX = PointPrimitive.TRANSLUCENCY_BY_DISTANCE_INDEX; var DISTANCE_DISPLAY_CONDITION_INDEX = PointPrimitive.DISTANCE_DISPLAY_CONDITION_INDEX; var DISABLE_DEPTH_DISTANCE_INDEX = PointPrimitive.DISABLE_DEPTH_DISTANCE_INDEX; var NUMBER_OF_PROPERTIES = PointPrimitive.NUMBER_OF_PROPERTIES; var attributeLocations$1 = { positionHighAndSize: 0, positionLowAndOutline: 1, compressedAttribute0: 2, // color, outlineColor, pick color compressedAttribute1: 3, // show, translucency by distance, some free space scaleByDistance: 4, distanceDisplayConditionAndDisableDepth: 5, }; /** * A renderable collection of points. *

* Points are added and removed from the collection using {@link PointPrimitiveCollection#add} * and {@link PointPrimitiveCollection#remove}. * * @alias PointPrimitiveCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms each point from model to world coordinates. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown. * @param {BlendOption} [options.blendOption=BlendOption.OPAQUE_AND_TRANSLUCENT] The point blending option. The default * is used for rendering both opaque and translucent points. However, if either all of the points are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve performance by up to 2x. * @param {Boolean} [options.show=true] Determines if the primitives in the collection will be shown. * * @performance For best performance, prefer a few collections, each with many points, to * many collections with only a few points each. Organize collections so that points * with the same update frequency are in the same collection, i.e., points that do not * change should be in one collection; points that change every frame should be in another * collection; and so on. * * * @example * // Create a pointPrimitive collection with two points * var points = scene.primitives.add(new Cesium.PointPrimitiveCollection()); * points.add({ * position : new Cesium.Cartesian3(1.0, 2.0, 3.0), * color : Cesium.Color.YELLOW * }); * points.add({ * position : new Cesium.Cartesian3(4.0, 5.0, 6.0), * color : Cesium.Color.CYAN * }); * * @see PointPrimitiveCollection#add * @see PointPrimitiveCollection#remove * @see PointPrimitive */ function PointPrimitiveCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._sp = undefined; this._spTranslucent = undefined; this._rsOpaque = undefined; this._rsTranslucent = undefined; this._vaf = undefined; this._pointPrimitives = []; this._pointPrimitivesToUpdate = []; this._pointPrimitivesToUpdateIndex = 0; this._pointPrimitivesRemoved = false; this._createVertexArray = false; this._shaderScaleByDistance = false; this._compiledShaderScaleByDistance = false; this._shaderTranslucencyByDistance = false; this._compiledShaderTranslucencyByDistance = false; this._shaderDistanceDisplayCondition = false; this._compiledShaderDistanceDisplayCondition = false; this._shaderDisableDepthDistance = false; this._compiledShaderDisableDepthDistance = false; this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxPixelSize = 1.0; this._baseVolume = new BoundingSphere(); this._baseVolumeWC = new BoundingSphere(); this._baseVolume2D = new BoundingSphere(); this._boundingVolume = new BoundingSphere(); this._boundingVolumeDirty = false; this._colorCommands = []; /** * Determines if primitives in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * The 4x4 transformation matrix that transforms each point in this collection from model to world coordinates. * When this is the identity matrix, the pointPrimitives are drawn in world coordinates, i.e., Earth's WGS84 coordinates. * Local reference frames can be used by providing a different transformation matrix, like that returned * by {@link Transforms.eastNorthUpToFixedFrame}. * * @type {Matrix4} * @default {@link Matrix4.IDENTITY} * * * @example * var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); * pointPrimitives.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); * pointPrimitives.add({ * color : Cesium.Color.ORANGE, * position : new Cesium.Cartesian3(0.0, 0.0, 0.0) // center * }); * pointPrimitives.add({ * color : Cesium.Color.YELLOW, * position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0) // east * }); * pointPrimitives.add({ * color : Cesium.Color.GREEN, * position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0) // north * }); * pointPrimitives.add({ * color : Cesium.Color.CYAN, * position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0) // up * }); * * @see Transforms.eastNorthUpToFixedFrame */ this.modelMatrix = Matrix4.clone( defaultValue(options.modelMatrix, Matrix4.IDENTITY) ); this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); /** * This property is for debugging only; it is not for production use nor is it optimized. *

* Draws the bounding sphere for each draw command in the primitive. *

* * @type {Boolean} * * @default false */ this.debugShowBoundingVolume = defaultValue( options.debugShowBoundingVolume, false ); /** * The point blending option. The default is used for rendering both opaque and translucent points. * However, if either all of the points are completely opaque or all are completely translucent, * setting the technique to BlendOption.OPAQUE or BlendOption.TRANSLUCENT can improve * performance by up to 2x. * @type {BlendOption} * @default BlendOption.OPAQUE_AND_TRANSLUCENT */ this.blendOption = defaultValue( options.blendOption, BlendOption$1.OPAQUE_AND_TRANSLUCENT ); this._blendOption = undefined; this._mode = SceneMode$1.SCENE3D; this._maxTotalPointSize = 1; // The buffer usage for each attribute is determined based on the usage of the attribute over time. this._buffersUsage = [ BufferUsage$1.STATIC_DRAW, // SHOW_INDEX BufferUsage$1.STATIC_DRAW, // POSITION_INDEX BufferUsage$1.STATIC_DRAW, // COLOR_INDEX BufferUsage$1.STATIC_DRAW, // OUTLINE_COLOR_INDEX BufferUsage$1.STATIC_DRAW, // OUTLINE_WIDTH_INDEX BufferUsage$1.STATIC_DRAW, // PIXEL_SIZE_INDEX BufferUsage$1.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX BufferUsage$1.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX BufferUsage$1.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX ]; var that = this; this._uniforms = { u_maxTotalPointSize: function () { return that._maxTotalPointSize; }, }; } Object.defineProperties(PointPrimitiveCollection.prototype, { /** * Returns the number of points in this collection. This is commonly used with * {@link PointPrimitiveCollection#get} to iterate over all the points * in the collection. * @memberof PointPrimitiveCollection.prototype * @type {Number} */ length: { get: function () { removePointPrimitives(this); return this._pointPrimitives.length; }, }, }); function destroyPointPrimitives(pointPrimitives) { var length = pointPrimitives.length; for (var i = 0; i < length; ++i) { if (pointPrimitives[i]) { pointPrimitives[i]._destroy(); } } } /** * Creates and adds a point with the specified initial properties to the collection. * The added point is returned so it can be modified or removed from the collection later. * * @param {Object}[options] A template describing the point's properties as shown in Example 1. * @returns {PointPrimitive} The point that was added to the collection. * * @performance Calling add is expected constant time. However, the collection's vertex buffer * is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For * best performance, add as many pointPrimitives as possible before calling update. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Example 1: Add a point, specifying all the default values. * var p = pointPrimitives.add({ * show : true, * position : Cesium.Cartesian3.ZERO, * pixelSize : 10.0, * color : Cesium.Color.WHITE, * outlineColor : Cesium.Color.TRANSPARENT, * outlineWidth : 0.0, * id : undefined * }); * * @example * // Example 2: Specify only the point's cartographic position. * var p = pointPrimitives.add({ * position : Cesium.Cartesian3.fromDegrees(longitude, latitude, height) * }); * * @see PointPrimitiveCollection#remove * @see PointPrimitiveCollection#removeAll */ PointPrimitiveCollection.prototype.add = function (options) { var p = new PointPrimitive(options, this); p._index = this._pointPrimitives.length; this._pointPrimitives.push(p); this._createVertexArray = true; return p; }; /** * Removes a point from the collection. * * @param {PointPrimitive} pointPrimitive The point to remove. * @returns {Boolean} true if the point was removed; false if the point was not found in the collection. * * @performance Calling remove is expected constant time. However, the collection's vertex buffer * is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For * best performance, remove as many points as possible before calling update. * If you intend to temporarily hide a point, it is usually more efficient to call * {@link PointPrimitive#show} instead of removing and re-adding the point. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * var p = pointPrimitives.add(...); * pointPrimitives.remove(p); // Returns true * * @see PointPrimitiveCollection#add * @see PointPrimitiveCollection#removeAll * @see PointPrimitive#show */ PointPrimitiveCollection.prototype.remove = function (pointPrimitive) { if (this.contains(pointPrimitive)) { this._pointPrimitives[pointPrimitive._index] = null; // Removed later this._pointPrimitivesRemoved = true; this._createVertexArray = true; pointPrimitive._destroy(); return true; } return false; }; /** * Removes all points from the collection. * * @performance O(n). It is more efficient to remove all the points * from a collection and then add new ones than to create a new collection entirely. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * pointPrimitives.add(...); * pointPrimitives.add(...); * pointPrimitives.removeAll(); * * @see PointPrimitiveCollection#add * @see PointPrimitiveCollection#remove */ PointPrimitiveCollection.prototype.removeAll = function () { destroyPointPrimitives(this._pointPrimitives); this._pointPrimitives = []; this._pointPrimitivesToUpdate = []; this._pointPrimitivesToUpdateIndex = 0; this._pointPrimitivesRemoved = false; this._createVertexArray = true; }; function removePointPrimitives(pointPrimitiveCollection) { if (pointPrimitiveCollection._pointPrimitivesRemoved) { pointPrimitiveCollection._pointPrimitivesRemoved = false; var newPointPrimitives = []; var pointPrimitives = pointPrimitiveCollection._pointPrimitives; var length = pointPrimitives.length; for (var i = 0, j = 0; i < length; ++i) { var pointPrimitive = pointPrimitives[i]; if (pointPrimitive) { pointPrimitive._index = j++; newPointPrimitives.push(pointPrimitive); } } pointPrimitiveCollection._pointPrimitives = newPointPrimitives; } } PointPrimitiveCollection.prototype._updatePointPrimitive = function ( pointPrimitive, propertyChanged ) { if (!pointPrimitive._dirty) { this._pointPrimitivesToUpdate[ this._pointPrimitivesToUpdateIndex++ ] = pointPrimitive; } ++this._propertiesChanged[propertyChanged]; }; /** * Check whether this collection contains a given point. * * @param {PointPrimitive} [pointPrimitive] The point to check for. * @returns {Boolean} true if this collection contains the point, false otherwise. * * @see PointPrimitiveCollection#get */ PointPrimitiveCollection.prototype.contains = function (pointPrimitive) { return ( defined(pointPrimitive) && pointPrimitive._pointPrimitiveCollection === this ); }; /** * Returns the point in the collection at the specified index. Indices are zero-based * and increase as points are added. Removing a point shifts all points after * it to the left, changing their indices. This function is commonly used with * {@link PointPrimitiveCollection#length} to iterate over all the points * in the collection. * * @param {Number} index The zero-based index of the point. * @returns {PointPrimitive} The point at the specified index. * * @performance Expected constant time. If points were removed from the collection and * {@link PointPrimitiveCollection#update} was not called, an implicit O(n) * operation is performed. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Toggle the show property of every point in the collection * var len = pointPrimitives.length; * for (var i = 0; i < len; ++i) { * var p = pointPrimitives.get(i); * p.show = !p.show; * } * * @see PointPrimitiveCollection#length */ PointPrimitiveCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); removePointPrimitives(this); return this._pointPrimitives[index]; }; PointPrimitiveCollection.prototype.computeNewBuffersUsage = function () { var buffersUsage = this._buffersUsage; var usageChanged = false; var properties = this._propertiesChanged; for (var k = 0; k < NUMBER_OF_PROPERTIES; ++k) { var newUsage = properties[k] === 0 ? BufferUsage$1.STATIC_DRAW : BufferUsage$1.STREAM_DRAW; usageChanged = usageChanged || buffersUsage[k] !== newUsage; buffersUsage[k] = newUsage; } return usageChanged; }; function createVAF(context, numberOfPointPrimitives, buffersUsage) { return new VertexArrayFacade( context, [ { index: attributeLocations$1.positionHighAndSize, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[POSITION_INDEX], }, { index: attributeLocations$1.positionLowAndShow, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[POSITION_INDEX], }, { index: attributeLocations$1.compressedAttribute0, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[COLOR_INDEX], }, { index: attributeLocations$1.compressedAttribute1, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[TRANSLUCENCY_BY_DISTANCE_INDEX], }, { index: attributeLocations$1.scaleByDistance, componentsPerAttribute: 4, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[SCALE_BY_DISTANCE_INDEX], }, { index: attributeLocations$1.distanceDisplayConditionAndDisableDepth, componentsPerAttribute: 3, componentDatatype: ComponentDatatype$1.FLOAT, usage: buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX], }, ], numberOfPointPrimitives ); // 1 vertex per pointPrimitive } /////////////////////////////////////////////////////////////////////////// // PERFORMANCE_IDEA: Save memory if a property is the same for all pointPrimitives, use a latched attribute state, // instead of storing it in a vertex buffer. var writePositionScratch = new EncodedCartesian3(); function writePositionSizeAndOutline( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { var i = pointPrimitive._index; var position = pointPrimitive._getActualPosition(); if (pointPrimitiveCollection._mode === SceneMode$1.SCENE3D) { BoundingSphere.expand( pointPrimitiveCollection._baseVolume, position, pointPrimitiveCollection._baseVolume ); pointPrimitiveCollection._boundingVolumeDirty = true; } EncodedCartesian3.fromCartesian(position, writePositionScratch); var pixelSize = pointPrimitive.pixelSize; var outlineWidth = pointPrimitive.outlineWidth; pointPrimitiveCollection._maxPixelSize = Math.max( pointPrimitiveCollection._maxPixelSize, pixelSize + outlineWidth ); var positionHighWriter = vafWriters[attributeLocations$1.positionHighAndSize]; var high = writePositionScratch.high; positionHighWriter(i, high.x, high.y, high.z, pixelSize); var positionLowWriter = vafWriters[attributeLocations$1.positionLowAndOutline]; var low = writePositionScratch.low; positionLowWriter(i, low.x, low.y, low.z, outlineWidth); } var LEFT_SHIFT16 = 65536.0; // 2^16 var LEFT_SHIFT8 = 256.0; // 2^8 function writeCompressedAttrib0( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { var i = pointPrimitive._index; var color = pointPrimitive.color; var pickColor = pointPrimitive.getPickId(context).color; var outlineColor = pointPrimitive.outlineColor; var red = Color.floatToByte(color.red); var green = Color.floatToByte(color.green); var blue = Color.floatToByte(color.blue); var compressed0 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue; red = Color.floatToByte(outlineColor.red); green = Color.floatToByte(outlineColor.green); blue = Color.floatToByte(outlineColor.blue); var compressed1 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue; red = Color.floatToByte(pickColor.red); green = Color.floatToByte(pickColor.green); blue = Color.floatToByte(pickColor.blue); var compressed2 = red * LEFT_SHIFT16 + green * LEFT_SHIFT8 + blue; var compressed3 = Color.floatToByte(color.alpha) * LEFT_SHIFT16 + Color.floatToByte(outlineColor.alpha) * LEFT_SHIFT8 + Color.floatToByte(pickColor.alpha); var writer = vafWriters[attributeLocations$1.compressedAttribute0]; writer(i, compressed0, compressed1, compressed2, compressed3); } function writeCompressedAttrib1( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { var i = pointPrimitive._index; var near = 0.0; var nearValue = 1.0; var far = 1.0; var farValue = 1.0; var translucency = pointPrimitive.translucencyByDistance; if (defined(translucency)) { near = translucency.near; nearValue = translucency.nearValue; far = translucency.far; farValue = translucency.farValue; if (nearValue !== 1.0 || farValue !== 1.0) { // translucency by distance calculation in shader need not be enabled // until a pointPrimitive with near and far !== 1.0 is found pointPrimitiveCollection._shaderTranslucencyByDistance = true; } } var show = pointPrimitive.show && pointPrimitive.clusterShow; // If the color alphas are zero, do not show this pointPrimitive. This lets us avoid providing // color during the pick pass and also eliminates a discard in the fragment shader. if ( pointPrimitive.color.alpha === 0.0 && pointPrimitive.outlineColor.alpha === 0.0 ) { show = false; } nearValue = CesiumMath.clamp(nearValue, 0.0, 1.0); nearValue = nearValue === 1.0 ? 255.0 : (nearValue * 255.0) | 0; var compressed0 = (show ? 1.0 : 0.0) * LEFT_SHIFT8 + nearValue; farValue = CesiumMath.clamp(farValue, 0.0, 1.0); farValue = farValue === 1.0 ? 255.0 : (farValue * 255.0) | 0; var compressed1 = farValue; var writer = vafWriters[attributeLocations$1.compressedAttribute1]; writer(i, compressed0, compressed1, near, far); } function writeScaleByDistance( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { var i = pointPrimitive._index; var writer = vafWriters[attributeLocations$1.scaleByDistance]; var near = 0.0; var nearValue = 1.0; var far = 1.0; var farValue = 1.0; var scale = pointPrimitive.scaleByDistance; if (defined(scale)) { near = scale.near; nearValue = scale.nearValue; far = scale.far; farValue = scale.farValue; if (nearValue !== 1.0 || farValue !== 1.0) { // scale by distance calculation in shader need not be enabled // until a pointPrimitive with near and far !== 1.0 is found pointPrimitiveCollection._shaderScaleByDistance = true; } } writer(i, near, nearValue, far, farValue); } function writeDistanceDisplayConditionAndDepthDisable( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { var i = pointPrimitive._index; var writer = vafWriters[attributeLocations$1.distanceDisplayConditionAndDisableDepth]; var near = 0.0; var far = Number.MAX_VALUE; var distanceDisplayCondition = pointPrimitive.distanceDisplayCondition; if (defined(distanceDisplayCondition)) { near = distanceDisplayCondition.near; far = distanceDisplayCondition.far; near *= near; far *= far; pointPrimitiveCollection._shaderDistanceDisplayCondition = true; } var disableDepthTestDistance = pointPrimitive.disableDepthTestDistance; disableDepthTestDistance *= disableDepthTestDistance; if (disableDepthTestDistance > 0.0) { pointPrimitiveCollection._shaderDisableDepthDistance = true; if (disableDepthTestDistance === Number.POSITIVE_INFINITY) { disableDepthTestDistance = -1.0; } } writer(i, near, far, disableDepthTestDistance); } function writePointPrimitive( pointPrimitiveCollection, context, vafWriters, pointPrimitive ) { writePositionSizeAndOutline( pointPrimitiveCollection, context, vafWriters, pointPrimitive ); writeCompressedAttrib0( pointPrimitiveCollection, context, vafWriters, pointPrimitive ); writeCompressedAttrib1( pointPrimitiveCollection, context, vafWriters, pointPrimitive ); writeScaleByDistance( pointPrimitiveCollection, context, vafWriters, pointPrimitive ); writeDistanceDisplayConditionAndDepthDisable( pointPrimitiveCollection, context, vafWriters, pointPrimitive ); } function recomputeActualPositions( pointPrimitiveCollection, pointPrimitives, length, frameState, modelMatrix, recomputeBoundingVolume ) { var boundingVolume; if (frameState.mode === SceneMode$1.SCENE3D) { boundingVolume = pointPrimitiveCollection._baseVolume; pointPrimitiveCollection._boundingVolumeDirty = true; } else { boundingVolume = pointPrimitiveCollection._baseVolume2D; } var positions = []; for (var i = 0; i < length; ++i) { var pointPrimitive = pointPrimitives[i]; var position = pointPrimitive.position; var actualPosition = PointPrimitive._computeActualPosition( position, frameState, modelMatrix ); if (defined(actualPosition)) { pointPrimitive._setActualPosition(actualPosition); if (recomputeBoundingVolume) { positions.push(actualPosition); } else { BoundingSphere.expand(boundingVolume, actualPosition, boundingVolume); } } } if (recomputeBoundingVolume) { BoundingSphere.fromPoints(positions, boundingVolume); } } function updateMode(pointPrimitiveCollection, frameState) { var mode = frameState.mode; var pointPrimitives = pointPrimitiveCollection._pointPrimitives; var pointPrimitivesToUpdate = pointPrimitiveCollection._pointPrimitivesToUpdate; var modelMatrix = pointPrimitiveCollection._modelMatrix; if ( pointPrimitiveCollection._createVertexArray || pointPrimitiveCollection._mode !== mode || (mode !== SceneMode$1.SCENE3D && !Matrix4.equals(modelMatrix, pointPrimitiveCollection.modelMatrix)) ) { pointPrimitiveCollection._mode = mode; Matrix4.clone(pointPrimitiveCollection.modelMatrix, modelMatrix); pointPrimitiveCollection._createVertexArray = true; if ( mode === SceneMode$1.SCENE3D || mode === SceneMode$1.SCENE2D || mode === SceneMode$1.COLUMBUS_VIEW ) { recomputeActualPositions( pointPrimitiveCollection, pointPrimitives, pointPrimitives.length, frameState, modelMatrix, true ); } } else if (mode === SceneMode$1.MORPHING) { recomputeActualPositions( pointPrimitiveCollection, pointPrimitives, pointPrimitives.length, frameState, modelMatrix, true ); } else if (mode === SceneMode$1.SCENE2D || mode === SceneMode$1.COLUMBUS_VIEW) { recomputeActualPositions( pointPrimitiveCollection, pointPrimitivesToUpdate, pointPrimitiveCollection._pointPrimitivesToUpdateIndex, frameState, modelMatrix, false ); } } function updateBoundingVolume(collection, frameState, boundingVolume) { var pixelSize = frameState.camera.getPixelSize( boundingVolume, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight ); var size = pixelSize * collection._maxPixelSize; boundingVolume.radius += size; } var scratchWriterArray = []; /** * @private */ PointPrimitiveCollection.prototype.update = function (frameState) { removePointPrimitives(this); if (!this.show) { return; } this._maxTotalPointSize = ContextLimits.maximumAliasedPointSize; updateMode(this, frameState); var pointPrimitives = this._pointPrimitives; var pointPrimitivesLength = pointPrimitives.length; var pointPrimitivesToUpdate = this._pointPrimitivesToUpdate; var pointPrimitivesToUpdateLength = this._pointPrimitivesToUpdateIndex; var properties = this._propertiesChanged; var createVertexArray = this._createVertexArray; var vafWriters; var context = frameState.context; var pass = frameState.passes; var picking = pass.pick; // PERFORMANCE_IDEA: Round robin multiple buffers. if (createVertexArray || (!picking && this.computeNewBuffersUsage())) { this._createVertexArray = false; for (var k = 0; k < NUMBER_OF_PROPERTIES; ++k) { properties[k] = 0; } this._vaf = this._vaf && this._vaf.destroy(); if (pointPrimitivesLength > 0) { // PERFORMANCE_IDEA: Instead of creating a new one, resize like std::vector. this._vaf = createVAF(context, pointPrimitivesLength, this._buffersUsage); vafWriters = this._vaf.writers; // Rewrite entire buffer if pointPrimitives were added or removed. for (var i = 0; i < pointPrimitivesLength; ++i) { var pointPrimitive = this._pointPrimitives[i]; pointPrimitive._dirty = false; // In case it needed an update. writePointPrimitive(this, context, vafWriters, pointPrimitive); } this._vaf.commit(); } this._pointPrimitivesToUpdateIndex = 0; } else if (pointPrimitivesToUpdateLength > 0) { // PointPrimitives were modified, but none were added or removed. var writers = scratchWriterArray; writers.length = 0; if ( properties[POSITION_INDEX] || properties[OUTLINE_WIDTH_INDEX] || properties[PIXEL_SIZE_INDEX] ) { writers.push(writePositionSizeAndOutline); } if (properties[COLOR_INDEX] || properties[OUTLINE_COLOR_INDEX]) { writers.push(writeCompressedAttrib0); } if (properties[SHOW_INDEX] || properties[TRANSLUCENCY_BY_DISTANCE_INDEX]) { writers.push(writeCompressedAttrib1); } if (properties[SCALE_BY_DISTANCE_INDEX]) { writers.push(writeScaleByDistance); } if ( properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE_INDEX] ) { writers.push(writeDistanceDisplayConditionAndDepthDisable); } var numWriters = writers.length; vafWriters = this._vaf.writers; if (pointPrimitivesToUpdateLength / pointPrimitivesLength > 0.1) { // If more than 10% of pointPrimitive change, rewrite the entire buffer. // PERFORMANCE_IDEA: I totally made up 10% :). for (var m = 0; m < pointPrimitivesToUpdateLength; ++m) { var b = pointPrimitivesToUpdate[m]; b._dirty = false; for (var n = 0; n < numWriters; ++n) { writers[n](this, context, vafWriters, b); } } this._vaf.commit(); } else { for (var h = 0; h < pointPrimitivesToUpdateLength; ++h) { var bb = pointPrimitivesToUpdate[h]; bb._dirty = false; for (var o = 0; o < numWriters; ++o) { writers[o](this, context, vafWriters, bb); } this._vaf.subCommit(bb._index, 1); } this._vaf.endSubCommits(); } this._pointPrimitivesToUpdateIndex = 0; } // If the number of total pointPrimitives ever shrinks considerably // Truncate pointPrimitivesToUpdate so that we free memory that we're // not going to be using. if (pointPrimitivesToUpdateLength > pointPrimitivesLength * 1.5) { pointPrimitivesToUpdate.length = pointPrimitivesLength; } if (!defined(this._vaf) || !defined(this._vaf.va)) { return; } if (this._boundingVolumeDirty) { this._boundingVolumeDirty = false; BoundingSphere.transform( this._baseVolume, this.modelMatrix, this._baseVolumeWC ); } var boundingVolume; var modelMatrix = Matrix4.IDENTITY; if (frameState.mode === SceneMode$1.SCENE3D) { modelMatrix = this.modelMatrix; boundingVolume = BoundingSphere.clone( this._baseVolumeWC, this._boundingVolume ); } else { boundingVolume = BoundingSphere.clone( this._baseVolume2D, this._boundingVolume ); } updateBoundingVolume(this, frameState, boundingVolume); var blendOptionChanged = this._blendOption !== this.blendOption; this._blendOption = this.blendOption; if (blendOptionChanged) { if ( this._blendOption === BlendOption$1.OPAQUE || this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT ) { this._rsOpaque = RenderState.fromCache({ depthTest: { enabled: true, func: WebGLConstants$1.LEQUAL, }, depthMask: true, }); } else { this._rsOpaque = undefined; } if ( this._blendOption === BlendOption$1.TRANSLUCENT || this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT ) { this._rsTranslucent = RenderState.fromCache({ depthTest: { enabled: true, func: WebGLConstants$1.LEQUAL, }, depthMask: false, blending: BlendingState$1.ALPHA_BLEND, }); } else { this._rsTranslucent = undefined; } } this._shaderDisableDepthDistance = this._shaderDisableDepthDistance || frameState.minimumDisableDepthTestDistance !== 0.0; var vs; var fs; if ( blendOptionChanged || (this._shaderScaleByDistance && !this._compiledShaderScaleByDistance) || (this._shaderTranslucencyByDistance && !this._compiledShaderTranslucencyByDistance) || (this._shaderDistanceDisplayCondition && !this._compiledShaderDistanceDisplayCondition) || this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance ) { vs = new ShaderSource({ sources: [PointPrimitiveCollectionVS], }); if (this._shaderScaleByDistance) { vs.defines.push("EYE_DISTANCE_SCALING"); } if (this._shaderTranslucencyByDistance) { vs.defines.push("EYE_DISTANCE_TRANSLUCENCY"); } if (this._shaderDistanceDisplayCondition) { vs.defines.push("DISTANCE_DISPLAY_CONDITION"); } if (this._shaderDisableDepthDistance) { vs.defines.push("DISABLE_DEPTH_DISTANCE"); } if (this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT) { fs = new ShaderSource({ defines: ["OPAQUE"], sources: [PointPrimitiveCollectionFS], }); this._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: this._sp, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$1, }); fs = new ShaderSource({ defines: ["TRANSLUCENT"], sources: [PointPrimitiveCollectionFS], }); this._spTranslucent = ShaderProgram.replaceCache({ context: context, shaderProgram: this._spTranslucent, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$1, }); } if (this._blendOption === BlendOption$1.OPAQUE) { fs = new ShaderSource({ sources: [PointPrimitiveCollectionFS], }); this._sp = ShaderProgram.replaceCache({ context: context, shaderProgram: this._sp, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$1, }); } if (this._blendOption === BlendOption$1.TRANSLUCENT) { fs = new ShaderSource({ sources: [PointPrimitiveCollectionFS], }); this._spTranslucent = ShaderProgram.replaceCache({ context: context, shaderProgram: this._spTranslucent, vertexShaderSource: vs, fragmentShaderSource: fs, attributeLocations: attributeLocations$1, }); } this._compiledShaderScaleByDistance = this._shaderScaleByDistance; this._compiledShaderTranslucencyByDistance = this._shaderTranslucencyByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; } var va; var vaLength; var command; var j; var commandList = frameState.commandList; if (pass.render || picking) { var colorList = this._colorCommands; var opaque = this._blendOption === BlendOption$1.OPAQUE; var opaqueAndTranslucent = this._blendOption === BlendOption$1.OPAQUE_AND_TRANSLUCENT; va = this._vaf.va; vaLength = va.length; colorList.length = vaLength; var totalLength = opaqueAndTranslucent ? vaLength * 2 : vaLength; for (j = 0; j < totalLength; ++j) { var opaqueCommand = opaque || (opaqueAndTranslucent && j % 2 === 0); command = colorList[j]; if (!defined(command)) { command = colorList[j] = new DrawCommand(); } command.primitiveType = PrimitiveType$1.POINTS; command.pass = opaqueCommand || !opaqueAndTranslucent ? Pass$1.OPAQUE : Pass$1.TRANSLUCENT; command.owner = this; var index = opaqueAndTranslucent ? Math.floor(j / 2.0) : j; command.boundingVolume = boundingVolume; command.modelMatrix = modelMatrix; command.shaderProgram = opaqueCommand ? this._sp : this._spTranslucent; command.uniformMap = this._uniforms; command.vertexArray = va[index].va; command.renderState = opaqueCommand ? this._rsOpaque : this._rsTranslucent; command.debugShowBoundingVolume = this.debugShowBoundingVolume; command.pickId = "v_pickColor"; commandList.push(command); } } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see PointPrimitiveCollection#destroy */ PointPrimitiveCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * pointPrimitives = pointPrimitives && pointPrimitives.destroy(); * * @see PointPrimitiveCollection#isDestroyed */ PointPrimitiveCollection.prototype.destroy = function () { this._sp = this._sp && this._sp.destroy(); this._spTranslucent = this._spTranslucent && this._spTranslucent.destroy(); this._spPick = this._spPick && this._spPick.destroy(); this._vaf = this._vaf && this._vaf.destroy(); destroyPointPrimitives(this._pointPrimitives); return destroyObject(this); }; function kdbush(points, getX, getY, nodeSize, ArrayType) { return new KDBush(points, getX, getY, nodeSize, ArrayType); } function KDBush(points, getX, getY, nodeSize, ArrayType) { getX = getX || defaultGetX; getY = getY || defaultGetY; ArrayType = ArrayType || Array; this.nodeSize = nodeSize || 64; this.points = points; this.ids = new ArrayType(points.length); this.coords = new ArrayType(points.length * 2); for (var i = 0; i < points.length; i++) { this.ids[i] = i; this.coords[2 * i] = getX(points[i]); this.coords[2 * i + 1] = getY(points[i]); } sort(this.ids, this.coords, this.nodeSize, 0, this.ids.length - 1, 0); } KDBush.prototype = { range: function (minX, minY, maxX, maxY) { return range(this.ids, this.coords, minX, minY, maxX, maxY, this.nodeSize); }, within: function (x, y, r) { return within(this.ids, this.coords, x, y, r, this.nodeSize); } }; function defaultGetX(p) { return p[0]; } function defaultGetY(p) { return p[1]; } function range(ids, coords, minX, minY, maxX, maxY, nodeSize) { var stack = [0, ids.length - 1, 0]; var result = []; var x, y; while (stack.length) { var axis = stack.pop(); var right = stack.pop(); var left = stack.pop(); if (right - left <= nodeSize) { for (var i = left; i <= right; i++) { x = coords[2 * i]; y = coords[2 * i + 1]; if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]); } continue; } var m = Math.floor((left + right) / 2); x = coords[2 * m]; y = coords[2 * m + 1]; if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]); var nextAxis = (axis + 1) % 2; if (axis === 0 ? minX <= x : minY <= y) { stack.push(left); stack.push(m - 1); stack.push(nextAxis); } if (axis === 0 ? maxX >= x : maxY >= y) { stack.push(m + 1); stack.push(right); stack.push(nextAxis); } } return result; } function sort(ids, coords, nodeSize, left, right, depth) { if (right - left <= nodeSize) return; var m = Math.floor((left + right) / 2); select(ids, coords, m, left, right, depth % 2); sort(ids, coords, nodeSize, left, m - 1, depth + 1); sort(ids, coords, nodeSize, m + 1, right, depth + 1); } function select(ids, coords, k, left, right, inc) { while (right > left) { if (right - left > 600) { var n = right - left + 1; var m = k - left + 1; var z = Math.log(n); var s = 0.5 * Math.exp(2 * z / 3); var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); select(ids, coords, k, newLeft, newRight, inc); } var t = coords[2 * k + inc]; var i = left; var j = right; swapItem(ids, coords, left, k); if (coords[2 * right + inc] > t) swapItem(ids, coords, left, right); while (i < j) { swapItem(ids, coords, i, j); i++; j--; while (coords[2 * i + inc] < t) i++; while (coords[2 * j + inc] > t) j--; } if (coords[2 * left + inc] === t) swapItem(ids, coords, left, j); else { j++; swapItem(ids, coords, j, right); } if (j <= k) left = j + 1; if (k <= j) right = j - 1; } } function swapItem(ids, coords, i, j) { swap$1(ids, i, j); swap$1(coords, 2 * i, 2 * j); swap$1(coords, 2 * i + 1, 2 * j + 1); } function swap$1(arr, i, j) { var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } function within(ids, coords, qx, qy, r, nodeSize) { var stack = [0, ids.length - 1, 0]; var result = []; var r2 = r * r; while (stack.length) { var axis = stack.pop(); var right = stack.pop(); var left = stack.pop(); if (right - left <= nodeSize) { for (var i = left; i <= right; i++) { if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) result.push(ids[i]); } continue; } var m = Math.floor((left + right) / 2); var x = coords[2 * m]; var y = coords[2 * m + 1]; if (sqDist(x, y, qx, qy) <= r2) result.push(ids[m]); var nextAxis = (axis + 1) % 2; if (axis === 0 ? qx - r <= x : qy - r <= y) { stack.push(left); stack.push(m - 1); stack.push(nextAxis); } if (axis === 0 ? qx + r >= x : qy + r >= y) { stack.push(m + 1); stack.push(right); stack.push(nextAxis); } } return result; } function sqDist(ax, ay, bx, by) { var dx = ax - bx; var dy = ay - by; return dx * dx + dy * dy; } /** * Defines how screen space objects (billboards, points, labels) are clustered. * * @param {Object} [options] An object with the following properties: * @param {Boolean} [options.enabled=false] Whether or not to enable clustering. * @param {Number} [options.pixelRange=80] The pixel range to extend the screen space bounding box. * @param {Number} [options.minimumClusterSize=2] The minimum number of screen space objects that can be clustered. * @param {Boolean} [options.clusterBillboards=true] Whether or not to cluster the billboards of an entity. * @param {Boolean} [options.clusterLabels=true] Whether or not to cluster the labels of an entity. * @param {Boolean} [options.clusterPoints=true] Whether or not to cluster the points of an entity. * @param {Boolean} [options.show=true] Determines if the entities in the cluster will be shown. * * @alias EntityCluster * @constructor * * @demo {@link https://sandcastle.cesium.com/index.html?src=Clustering.html|Cesium Sandcastle Clustering Demo} */ function EntityCluster(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._enabled = defaultValue(options.enabled, false); this._pixelRange = defaultValue(options.pixelRange, 80); this._minimumClusterSize = defaultValue(options.minimumClusterSize, 2); this._clusterBillboards = defaultValue(options.clusterBillboards, true); this._clusterLabels = defaultValue(options.clusterLabels, true); this._clusterPoints = defaultValue(options.clusterPoints, true); this._labelCollection = undefined; this._billboardCollection = undefined; this._pointCollection = undefined; this._clusterBillboardCollection = undefined; this._clusterLabelCollection = undefined; this._clusterPointCollection = undefined; this._collectionIndicesByEntity = {}; this._unusedLabelIndices = []; this._unusedBillboardIndices = []; this._unusedPointIndices = []; this._previousClusters = []; this._previousHeight = undefined; this._enabledDirty = false; this._clusterDirty = false; this._cluster = undefined; this._removeEventListener = undefined; this._clusterEvent = new Event(); /** * Determines if entities in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); } function getX(point) { return point.coord.x; } function getY(point) { return point.coord.y; } function expandBoundingBox(bbox, pixelRange) { bbox.x -= pixelRange; bbox.y -= pixelRange; bbox.width += pixelRange * 2.0; bbox.height += pixelRange * 2.0; } var labelBoundingBoxScratch = new BoundingRectangle(); function getBoundingBox(item, coord, pixelRange, entityCluster, result) { if (defined(item._labelCollection) && entityCluster._clusterLabels) { result = Label.getScreenSpaceBoundingBox(item, coord, result); } else if ( defined(item._billboardCollection) && entityCluster._clusterBillboards ) { result = Billboard.getScreenSpaceBoundingBox(item, coord, result); } else if ( defined(item._pointPrimitiveCollection) && entityCluster._clusterPoints ) { result = PointPrimitive.getScreenSpaceBoundingBox(item, coord, result); } expandBoundingBox(result, pixelRange); if ( entityCluster._clusterLabels && !defined(item._labelCollection) && defined(item.id) && hasLabelIndex(entityCluster, item.id.id) && defined(item.id._label) ) { var labelIndex = entityCluster._collectionIndicesByEntity[item.id.id].labelIndex; var label = entityCluster._labelCollection.get(labelIndex); var labelBBox = Label.getScreenSpaceBoundingBox( label, coord, labelBoundingBoxScratch ); expandBoundingBox(labelBBox, pixelRange); result = BoundingRectangle.union(result, labelBBox, result); } return result; } function addNonClusteredItem(item, entityCluster) { item.clusterShow = true; if ( !defined(item._labelCollection) && defined(item.id) && hasLabelIndex(entityCluster, item.id.id) && defined(item.id._label) ) { var labelIndex = entityCluster._collectionIndicesByEntity[item.id.id].labelIndex; var label = entityCluster._labelCollection.get(labelIndex); label.clusterShow = true; } } function addCluster(position, numPoints, ids, entityCluster) { var cluster = { billboard: entityCluster._clusterBillboardCollection.add(), label: entityCluster._clusterLabelCollection.add(), point: entityCluster._clusterPointCollection.add(), }; cluster.billboard.show = false; cluster.point.show = false; cluster.label.show = true; cluster.label.text = numPoints.toLocaleString(); cluster.label.id = ids; cluster.billboard.position = cluster.label.position = cluster.point.position = position; entityCluster._clusterEvent.raiseEvent(ids, cluster); } function hasLabelIndex(entityCluster, entityId) { return ( defined(entityCluster) && defined(entityCluster._collectionIndicesByEntity[entityId]) && defined(entityCluster._collectionIndicesByEntity[entityId].labelIndex) ); } function getScreenSpacePositions( collection, points, scene, occluder, entityCluster ) { if (!defined(collection)) { return; } var length = collection.length; for (var i = 0; i < length; ++i) { var item = collection.get(i); item.clusterShow = false; if ( !item.show || (entityCluster._scene.mode === SceneMode$1.SCENE3D && !occluder.isPointVisible(item.position)) ) { continue; } var canClusterLabels = entityCluster._clusterLabels && defined(item._labelCollection); var canClusterBillboards = entityCluster._clusterBillboards && defined(item.id._billboard); var canClusterPoints = entityCluster._clusterPoints && defined(item.id._point); if (canClusterLabels && (canClusterPoints || canClusterBillboards)) { continue; } var coord = item.computeScreenSpacePosition(scene); if (!defined(coord)) { continue; } points.push({ index: i, collection: collection, clustered: false, coord: coord, }); } } var pointBoundinRectangleScratch = new BoundingRectangle(); var totalBoundingRectangleScratch = new BoundingRectangle(); var neighborBoundingRectangleScratch = new BoundingRectangle(); function createDeclutterCallback(entityCluster) { return function (amount) { if ((defined(amount) && amount < 0.05) || !entityCluster.enabled) { return; } var scene = entityCluster._scene; var labelCollection = entityCluster._labelCollection; var billboardCollection = entityCluster._billboardCollection; var pointCollection = entityCluster._pointCollection; if ( (!defined(labelCollection) && !defined(billboardCollection) && !defined(pointCollection)) || (!entityCluster._clusterBillboards && !entityCluster._clusterLabels && !entityCluster._clusterPoints) ) { return; } var clusteredLabelCollection = entityCluster._clusterLabelCollection; var clusteredBillboardCollection = entityCluster._clusterBillboardCollection; var clusteredPointCollection = entityCluster._clusterPointCollection; if (defined(clusteredLabelCollection)) { clusteredLabelCollection.removeAll(); } else { clusteredLabelCollection = entityCluster._clusterLabelCollection = new LabelCollection( { scene: scene, } ); } if (defined(clusteredBillboardCollection)) { clusteredBillboardCollection.removeAll(); } else { clusteredBillboardCollection = entityCluster._clusterBillboardCollection = new BillboardCollection( { scene: scene, } ); } if (defined(clusteredPointCollection)) { clusteredPointCollection.removeAll(); } else { clusteredPointCollection = entityCluster._clusterPointCollection = new PointPrimitiveCollection(); } var pixelRange = entityCluster._pixelRange; var minimumClusterSize = entityCluster._minimumClusterSize; var clusters = entityCluster._previousClusters; var newClusters = []; var previousHeight = entityCluster._previousHeight; var currentHeight = scene.camera.positionCartographic.height; var ellipsoid = scene.mapProjection.ellipsoid; var cameraPosition = scene.camera.positionWC; var occluder = new EllipsoidalOccluder(ellipsoid, cameraPosition); var points = []; if (entityCluster._clusterLabels) { getScreenSpacePositions( labelCollection, points, scene, occluder, entityCluster ); } if (entityCluster._clusterBillboards) { getScreenSpacePositions( billboardCollection, points, scene, occluder, entityCluster ); } if (entityCluster._clusterPoints) { getScreenSpacePositions( pointCollection, points, scene, occluder, entityCluster ); } var i; var j; var length; var bbox; var neighbors; var neighborLength; var neighborIndex; var neighborPoint; var ids; var numPoints; var collection; var collectionIndex; var index = kdbush(points, getX, getY, 64, Int32Array); if (currentHeight < previousHeight) { length = clusters.length; for (i = 0; i < length; ++i) { var cluster = clusters[i]; if (!occluder.isPointVisible(cluster.position)) { continue; } var coord = Billboard._computeScreenSpacePosition( Matrix4.IDENTITY, cluster.position, Cartesian3.ZERO, Cartesian2.ZERO, scene ); if (!defined(coord)) { continue; } var factor = 1.0 - currentHeight / previousHeight; var width = (cluster.width = cluster.width * factor); var height = (cluster.height = cluster.height * factor); width = Math.max(width, cluster.minimumWidth); height = Math.max(height, cluster.minimumHeight); var minX = coord.x - width * 0.5; var minY = coord.y - height * 0.5; var maxX = coord.x + width; var maxY = coord.y + height; neighbors = index.range(minX, minY, maxX, maxY); neighborLength = neighbors.length; numPoints = 0; ids = []; for (j = 0; j < neighborLength; ++j) { neighborIndex = neighbors[j]; neighborPoint = points[neighborIndex]; if (!neighborPoint.clustered) { ++numPoints; collection = neighborPoint.collection; collectionIndex = neighborPoint.index; ids.push(collection.get(collectionIndex).id); } } if (numPoints >= minimumClusterSize) { addCluster(cluster.position, numPoints, ids, entityCluster); newClusters.push(cluster); for (j = 0; j < neighborLength; ++j) { points[neighbors[j]].clustered = true; } } } } length = points.length; for (i = 0; i < length; ++i) { var point = points[i]; if (point.clustered) { continue; } point.clustered = true; collection = point.collection; collectionIndex = point.index; var item = collection.get(collectionIndex); bbox = getBoundingBox( item, point.coord, pixelRange, entityCluster, pointBoundinRectangleScratch ); var totalBBox = BoundingRectangle.clone( bbox, totalBoundingRectangleScratch ); neighbors = index.range( bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height ); neighborLength = neighbors.length; var clusterPosition = Cartesian3.clone(item.position); numPoints = 1; ids = [item.id]; for (j = 0; j < neighborLength; ++j) { neighborIndex = neighbors[j]; neighborPoint = points[neighborIndex]; if (!neighborPoint.clustered) { var neighborItem = neighborPoint.collection.get(neighborPoint.index); var neighborBBox = getBoundingBox( neighborItem, neighborPoint.coord, pixelRange, entityCluster, neighborBoundingRectangleScratch ); Cartesian3.add( neighborItem.position, clusterPosition, clusterPosition ); BoundingRectangle.union(totalBBox, neighborBBox, totalBBox); ++numPoints; ids.push(neighborItem.id); } } if (numPoints >= minimumClusterSize) { var position = Cartesian3.multiplyByScalar( clusterPosition, 1.0 / numPoints, clusterPosition ); addCluster(position, numPoints, ids, entityCluster); newClusters.push({ position: position, width: totalBBox.width, height: totalBBox.height, minimumWidth: bbox.width, minimumHeight: bbox.height, }); for (j = 0; j < neighborLength; ++j) { points[neighbors[j]].clustered = true; } } else { addNonClusteredItem(item, entityCluster); } } if (clusteredLabelCollection.length === 0) { clusteredLabelCollection.destroy(); entityCluster._clusterLabelCollection = undefined; } if (clusteredBillboardCollection.length === 0) { clusteredBillboardCollection.destroy(); entityCluster._clusterBillboardCollection = undefined; } if (clusteredPointCollection.length === 0) { clusteredPointCollection.destroy(); entityCluster._clusterPointCollection = undefined; } entityCluster._previousClusters = newClusters; entityCluster._previousHeight = currentHeight; }; } EntityCluster.prototype._initialize = function (scene) { this._scene = scene; var cluster = createDeclutterCallback(this); this._cluster = cluster; this._removeEventListener = scene.camera.changed.addEventListener(cluster); }; Object.defineProperties(EntityCluster.prototype, { /** * Gets or sets whether clustering is enabled. * @memberof EntityCluster.prototype * @type {Boolean} */ enabled: { get: function () { return this._enabled; }, set: function (value) { this._enabledDirty = value !== this._enabled; this._enabled = value; }, }, /** * Gets or sets the pixel range to extend the screen space bounding box. * @memberof EntityCluster.prototype * @type {Number} */ pixelRange: { get: function () { return this._pixelRange; }, set: function (value) { this._clusterDirty = this._clusterDirty || value !== this._pixelRange; this._pixelRange = value; }, }, /** * Gets or sets the minimum number of screen space objects that can be clustered. * @memberof EntityCluster.prototype * @type {Number} */ minimumClusterSize: { get: function () { return this._minimumClusterSize; }, set: function (value) { this._clusterDirty = this._clusterDirty || value !== this._minimumClusterSize; this._minimumClusterSize = value; }, }, /** * Gets the event that will be raised when a new cluster will be displayed. The signature of the event listener is {@link EntityCluster.newClusterCallback}. * @memberof EntityCluster.prototype * @type {Event} */ clusterEvent: { get: function () { return this._clusterEvent; }, }, /** * Gets or sets whether clustering billboard entities is enabled. * @memberof EntityCluster.prototype * @type {Boolean} */ clusterBillboards: { get: function () { return this._clusterBillboards; }, set: function (value) { this._clusterDirty = this._clusterDirty || value !== this._clusterBillboards; this._clusterBillboards = value; }, }, /** * Gets or sets whether clustering labels entities is enabled. * @memberof EntityCluster.prototype * @type {Boolean} */ clusterLabels: { get: function () { return this._clusterLabels; }, set: function (value) { this._clusterDirty = this._clusterDirty || value !== this._clusterLabels; this._clusterLabels = value; }, }, /** * Gets or sets whether clustering point entities is enabled. * @memberof EntityCluster.prototype * @type {Boolean} */ clusterPoints: { get: function () { return this._clusterPoints; }, set: function (value) { this._clusterDirty = this._clusterDirty || value !== this._clusterPoints; this._clusterPoints = value; }, }, }); function createGetEntity( collectionProperty, CollectionConstructor, unusedIndicesProperty, entityIndexProperty ) { return function (entity) { var collection = this[collectionProperty]; if (!defined(this._collectionIndicesByEntity)) { this._collectionIndicesByEntity = {}; } var entityIndices = this._collectionIndicesByEntity[entity.id]; if (!defined(entityIndices)) { entityIndices = this._collectionIndicesByEntity[entity.id] = { billboardIndex: undefined, labelIndex: undefined, pointIndex: undefined, }; } if (defined(collection) && defined(entityIndices[entityIndexProperty])) { return collection.get(entityIndices[entityIndexProperty]); } if (!defined(collection)) { collection = this[collectionProperty] = new CollectionConstructor({ scene: this._scene, }); } var index; var entityItem; var unusedIndices = this[unusedIndicesProperty]; if (unusedIndices.length > 0) { index = unusedIndices.pop(); entityItem = collection.get(index); } else { entityItem = collection.add(); index = collection.length - 1; } entityIndices[entityIndexProperty] = index; this._clusterDirty = true; return entityItem; }; } function removeEntityIndicesIfUnused(entityCluster, entityId) { var indices = entityCluster._collectionIndicesByEntity[entityId]; if ( !defined(indices.billboardIndex) && !defined(indices.labelIndex) && !defined(indices.pointIndex) ) { delete entityCluster._collectionIndicesByEntity[entityId]; } } /** * Returns a new {@link Label}. * @param {Entity} entity The entity that will use the returned {@link Label} for visualization. * @returns {Label} The label that will be used to visualize an entity. * * @private */ EntityCluster.prototype.getLabel = createGetEntity( "_labelCollection", LabelCollection, "_unusedLabelIndices", "labelIndex" ); /** * Removes the {@link Label} associated with an entity so it can be reused by another entity. * @param {Entity} entity The entity that will uses the returned {@link Label} for visualization. * * @private */ EntityCluster.prototype.removeLabel = function (entity) { var entityIndices = this._collectionIndicesByEntity && this._collectionIndicesByEntity[entity.id]; if ( !defined(this._labelCollection) || !defined(entityIndices) || !defined(entityIndices.labelIndex) ) { return; } var index = entityIndices.labelIndex; entityIndices.labelIndex = undefined; removeEntityIndicesIfUnused(this, entity.id); var label = this._labelCollection.get(index); label.show = false; label.text = ""; label.id = undefined; this._unusedLabelIndices.push(index); this._clusterDirty = true; }; /** * Returns a new {@link Billboard}. * @param {Entity} entity The entity that will use the returned {@link Billboard} for visualization. * @returns {Billboard} The label that will be used to visualize an entity. * * @private */ EntityCluster.prototype.getBillboard = createGetEntity( "_billboardCollection", BillboardCollection, "_unusedBillboardIndices", "billboardIndex" ); /** * Removes the {@link Billboard} associated with an entity so it can be reused by another entity. * @param {Entity} entity The entity that will uses the returned {@link Billboard} for visualization. * * @private */ EntityCluster.prototype.removeBillboard = function (entity) { var entityIndices = this._collectionIndicesByEntity && this._collectionIndicesByEntity[entity.id]; if ( !defined(this._billboardCollection) || !defined(entityIndices) || !defined(entityIndices.billboardIndex) ) { return; } var index = entityIndices.billboardIndex; entityIndices.billboardIndex = undefined; removeEntityIndicesIfUnused(this, entity.id); var billboard = this._billboardCollection.get(index); billboard.id = undefined; billboard.show = false; billboard.image = undefined; this._unusedBillboardIndices.push(index); this._clusterDirty = true; }; /** * Returns a new {@link Point}. * @param {Entity} entity The entity that will use the returned {@link Point} for visualization. * @returns {Point} The label that will be used to visualize an entity. * * @private */ EntityCluster.prototype.getPoint = createGetEntity( "_pointCollection", PointPrimitiveCollection, "_unusedPointIndices", "pointIndex" ); /** * Removes the {@link Point} associated with an entity so it can be reused by another entity. * @param {Entity} entity The entity that will uses the returned {@link Point} for visualization. * * @private */ EntityCluster.prototype.removePoint = function (entity) { var entityIndices = this._collectionIndicesByEntity && this._collectionIndicesByEntity[entity.id]; if ( !defined(this._pointCollection) || !defined(entityIndices) || !defined(entityIndices.pointIndex) ) { return; } var index = entityIndices.pointIndex; entityIndices.pointIndex = undefined; removeEntityIndicesIfUnused(this, entity.id); var point = this._pointCollection.get(index); point.show = false; point.id = undefined; this._unusedPointIndices.push(index); this._clusterDirty = true; }; function disableCollectionClustering(collection) { if (!defined(collection)) { return; } var length = collection.length; for (var i = 0; i < length; ++i) { collection.get(i).clusterShow = true; } } function updateEnable(entityCluster) { if (entityCluster.enabled) { return; } if (defined(entityCluster._clusterLabelCollection)) { entityCluster._clusterLabelCollection.destroy(); } if (defined(entityCluster._clusterBillboardCollection)) { entityCluster._clusterBillboardCollection.destroy(); } if (defined(entityCluster._clusterPointCollection)) { entityCluster._clusterPointCollection.destroy(); } entityCluster._clusterLabelCollection = undefined; entityCluster._clusterBillboardCollection = undefined; entityCluster._clusterPointCollection = undefined; disableCollectionClustering(entityCluster._labelCollection); disableCollectionClustering(entityCluster._billboardCollection); disableCollectionClustering(entityCluster._pointCollection); } /** * Gets the draw commands for the clustered billboards/points/labels if enabled, otherwise, * queues the draw commands for billboards/points/labels created for entities. * @private */ EntityCluster.prototype.update = function (frameState) { if (!this.show) { return; } // If clustering is enabled before the label collection is updated, // the glyphs haven't been created so the screen space bounding boxes // are incorrect. var commandList; if ( defined(this._labelCollection) && this._labelCollection.length > 0 && this._labelCollection.get(0)._glyphs.length === 0 ) { commandList = frameState.commandList; frameState.commandList = []; this._labelCollection.update(frameState); frameState.commandList = commandList; } // If clustering is enabled before the billboard collection is updated, // the images haven't been added to the image atlas so the screen space bounding boxes // are incorrect. if ( defined(this._billboardCollection) && this._billboardCollection.length > 0 && !defined(this._billboardCollection.get(0).width) ) { commandList = frameState.commandList; frameState.commandList = []; this._billboardCollection.update(frameState); frameState.commandList = commandList; } if (this._enabledDirty) { this._enabledDirty = false; updateEnable(this); this._clusterDirty = true; } if (this._clusterDirty) { this._clusterDirty = false; this._cluster(); } if (defined(this._clusterLabelCollection)) { this._clusterLabelCollection.update(frameState); } if (defined(this._clusterBillboardCollection)) { this._clusterBillboardCollection.update(frameState); } if (defined(this._clusterPointCollection)) { this._clusterPointCollection.update(frameState); } if (defined(this._labelCollection)) { this._labelCollection.update(frameState); } if (defined(this._billboardCollection)) { this._billboardCollection.update(frameState); } if (defined(this._pointCollection)) { this._pointCollection.update(frameState); } }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Unlike other objects that use WebGL resources, this object can be reused. For example, if a data source is removed * from a data source collection and added to another. *

*/ EntityCluster.prototype.destroy = function () { this._labelCollection = this._labelCollection && this._labelCollection.destroy(); this._billboardCollection = this._billboardCollection && this._billboardCollection.destroy(); this._pointCollection = this._pointCollection && this._pointCollection.destroy(); this._clusterLabelCollection = this._clusterLabelCollection && this._clusterLabelCollection.destroy(); this._clusterBillboardCollection = this._clusterBillboardCollection && this._clusterBillboardCollection.destroy(); this._clusterPointCollection = this._clusterPointCollection && this._clusterPointCollection.destroy(); if (defined(this._removeEventListener)) { this._removeEventListener(); this._removeEventListener = undefined; } this._labelCollection = undefined; this._billboardCollection = undefined; this._pointCollection = undefined; this._clusterBillboardCollection = undefined; this._clusterLabelCollection = undefined; this._clusterPointCollection = undefined; this._collectionIndicesByEntity = undefined; this._unusedLabelIndices = []; this._unusedBillboardIndices = []; this._unusedPointIndices = []; this._previousClusters = []; this._previousHeight = undefined; this._enabledDirty = false; this._pixelRangeDirty = false; this._minimumClusterSizeDirty = false; return undefined; }; /** * A {@link DataSource} implementation which can be used to manually manage a group of entities. * * @alias CustomDataSource * @constructor * * @param {String} [name] A human-readable name for this instance. * * @example * var dataSource = new Cesium.CustomDataSource('myData'); * * var entity = dataSource.entities.add({ * position : Cesium.Cartesian3.fromDegrees(1, 2, 0), * billboard : { * image : 'image.png' * } * }); * * viewer.dataSources.add(dataSource); */ function CustomDataSource(name) { this._name = name; this._clock = undefined; this._changed = new Event(); this._error = new Event(); this._isLoading = false; this._loading = new Event(); this._entityCollection = new EntityCollection(this); this._entityCluster = new EntityCluster(); } Object.defineProperties(CustomDataSource.prototype, { /** * Gets or sets a human-readable name for this instance. * @memberof CustomDataSource.prototype * @type {String} */ name: { get: function () { return this._name; }, set: function (value) { if (this._name !== value) { this._name = value; this._changed.raiseEvent(this); } }, }, /** * Gets or sets the clock for this instance. * @memberof CustomDataSource.prototype * @type {DataSourceClock} */ clock: { get: function () { return this._clock; }, set: function (value) { if (this._clock !== value) { this._clock = value; this._changed.raiseEvent(this); } }, }, /** * Gets the collection of {@link Entity} instances. * @memberof CustomDataSource.prototype * @type {EntityCollection} */ entities: { get: function () { return this._entityCollection; }, }, /** * Gets or sets whether the data source is currently loading data. * @memberof CustomDataSource.prototype * @type {Boolean} */ isLoading: { get: function () { return this._isLoading; }, set: function (value) { DataSource.setLoading(this, value); }, }, /** * Gets an event that will be raised when the underlying data changes. * @memberof CustomDataSource.prototype * @type {Event} */ changedEvent: { get: function () { return this._changed; }, }, /** * Gets an event that will be raised if an error is encountered during processing. * @memberof CustomDataSource.prototype * @type {Event} */ errorEvent: { get: function () { return this._error; }, }, /** * Gets an event that will be raised when the data source either starts or stops loading. * @memberof CustomDataSource.prototype * @type {Event} */ loadingEvent: { get: function () { return this._loading; }, }, /** * Gets whether or not this data source should be displayed. * @memberof CustomDataSource.prototype * @type {Boolean} */ show: { get: function () { return this._entityCollection.show; }, set: function (value) { this._entityCollection.show = value; }, }, /** * Gets or sets the clustering options for this data source. This object can be shared between multiple data sources. * * @memberof CustomDataSource.prototype * @type {EntityCluster} */ clustering: { get: function () { return this._entityCluster; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value must be defined."); } //>>includeEnd('debug'); this._entityCluster = value; }, }, }); /** * Updates the data source to the provided time. This function is optional and * is not required to be implemented. It is provided for data sources which * retrieve data based on the current animation time or scene state. * If implemented, update will be called by {@link DataSourceDisplay} once a frame. * * @param {JulianDate} time The simulation time. * @returns {Boolean} True if this data source is ready to be displayed at the provided time, false otherwise. */ CustomDataSource.prototype.update = function (time) { return true; }; var defaultOffset$8 = Cartesian3.ZERO; var offsetScratch$7 = new Cartesian3(); var positionScratch$4 = new Cartesian3(); var scratchColor$c = new Color(); function CylinderGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.length = undefined; this.topRadius = undefined; this.bottomRadius = undefined; this.slices = undefined; this.numberOfVerticalLines = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for cylinders. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias CylinderGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function CylinderGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new CylinderGeometryOptions(entity), geometryPropertyName: "cylinder", observedPropertyNames: [ "availability", "position", "orientation", "cylinder", ], }); this._onEntityPropertyChanged(entity, "cylinder", entity.cylinder, undefined); } if (defined(Object.create)) { CylinderGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); CylinderGeometryUpdater.prototype.constructor = CylinderGeometryUpdater; } Object.defineProperties(CylinderGeometryUpdater.prototype, { /** * Gets the terrain offset property * @type {TerrainOffsetProperty} * @memberof CylinderGeometryUpdater.prototype * @readonly * @private */ terrainOffsetProperty: { get: function () { return this._terrainOffsetProperty; }, }, }); /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ CylinderGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); var attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: undefined, offset: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$c); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$8, offsetScratch$7 ) ); } return new GeometryInstance({ id: entity, geometry: new CylinderGeometry(this._options), modelMatrix: entity.computeModelMatrixForHeightReference( time, entity.cylinder.heightReference, this._options.length * 0.5, this._scene.mapProjection.ellipsoid ), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ CylinderGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$c ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$8, offsetScratch$7 ) ); } return new GeometryInstance({ id: entity, geometry: new CylinderOutlineGeometry(this._options), modelMatrix: entity.computeModelMatrixForHeightReference( time, entity.cylinder.heightReference, this._options.length * 0.5, this._scene.mapProjection.ellipsoid ), attributes: attributes, }); }; CylinderGeometryUpdater.prototype._computeCenter = function (time, result) { return Property.getValueOrUndefined(this._entity.position, time, result); }; CylinderGeometryUpdater.prototype._isHidden = function (entity, cylinder) { return ( !defined(entity.position) || !defined(cylinder.length) || !defined(cylinder.topRadius) || !defined(cylinder.bottomRadius) || GeometryUpdater.prototype._isHidden.call(this, entity, cylinder) ); }; CylinderGeometryUpdater.prototype._isDynamic = function (entity, cylinder) { return ( !entity.position.isConstant || // !Property.isConstant(entity.orientation) || // !cylinder.length.isConstant || // !cylinder.topRadius.isConstant || // !cylinder.bottomRadius.isConstant || // !Property.isConstant(cylinder.slices) || // !Property.isConstant(cylinder.outlineWidth) || // !Property.isConstant(cylinder.numberOfVerticalLines) ); }; CylinderGeometryUpdater.prototype._setStaticOptions = function ( entity, cylinder ) { var heightReference = Property.getValueOrDefault( cylinder.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var options = this._options; options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.length = cylinder.length.getValue(Iso8601.MINIMUM_VALUE); options.topRadius = cylinder.topRadius.getValue(Iso8601.MINIMUM_VALUE); options.bottomRadius = cylinder.bottomRadius.getValue(Iso8601.MINIMUM_VALUE); options.slices = Property.getValueOrUndefined( cylinder.slices, Iso8601.MINIMUM_VALUE ); options.numberOfVerticalLines = Property.getValueOrUndefined( cylinder.numberOfVerticalLines, Iso8601.MINIMUM_VALUE ); options.offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; }; CylinderGeometryUpdater.prototype._onEntityPropertyChanged = heightReferenceOnEntityPropertyChanged; CylinderGeometryUpdater.DynamicGeometryUpdater = DynamicCylinderGeometryUpdater; /** * @private */ function DynamicCylinderGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicCylinderGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicCylinderGeometryUpdater.prototype.constructor = DynamicCylinderGeometryUpdater; } DynamicCylinderGeometryUpdater.prototype._isHidden = function ( entity, cylinder, time ) { var options = this._options; var position = Property.getValueOrUndefined( entity.position, time, positionScratch$4 ); return ( !defined(position) || !defined(options.length) || !defined(options.topRadius) || // !defined(options.bottomRadius) || DynamicGeometryUpdater$1.prototype._isHidden.call( this, entity, cylinder, time ) ); }; DynamicCylinderGeometryUpdater.prototype._setOptions = function ( entity, cylinder, time ) { var heightReference = Property.getValueOrDefault( cylinder.heightReference, time, HeightReference$1.NONE ); var options = this._options; options.length = Property.getValueOrUndefined(cylinder.length, time); options.topRadius = Property.getValueOrUndefined(cylinder.topRadius, time); options.bottomRadius = Property.getValueOrUndefined( cylinder.bottomRadius, time ); options.slices = Property.getValueOrUndefined(cylinder.slices, time); options.numberOfVerticalLines = Property.getValueOrUndefined( cylinder.numberOfVerticalLines, time ); options.offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; }; /** * Represents desired clock settings for a particular {@link DataSource}. These settings may be applied * to the {@link Clock} when the DataSource is loaded. * * @alias DataSourceClock * @constructor */ function DataSourceClock() { this._definitionChanged = new Event(); this._startTime = undefined; this._stopTime = undefined; this._currentTime = undefined; this._clockRange = undefined; this._clockStep = undefined; this._multiplier = undefined; } Object.defineProperties(DataSourceClock.prototype, { /** * Gets the event that is raised whenever a new property is assigned. * @memberof DataSourceClock.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the desired start time of the clock. * See {@link Clock#startTime}. * @memberof DataSourceClock.prototype * @type {JulianDate} */ startTime: createRawPropertyDescriptor("startTime"), /** * Gets or sets the desired stop time of the clock. * See {@link Clock#stopTime}. * @memberof DataSourceClock.prototype * @type {JulianDate} */ stopTime: createRawPropertyDescriptor("stopTime"), /** * Gets or sets the desired current time when this data source is loaded. * See {@link Clock#currentTime}. * @memberof DataSourceClock.prototype * @type {JulianDate} */ currentTime: createRawPropertyDescriptor("currentTime"), /** * Gets or sets the desired clock range setting. * See {@link Clock#clockRange}. * @memberof DataSourceClock.prototype * @type {ClockRange} */ clockRange: createRawPropertyDescriptor("clockRange"), /** * Gets or sets the desired clock step setting. * See {@link Clock#clockStep}. * @memberof DataSourceClock.prototype * @type {ClockStep} */ clockStep: createRawPropertyDescriptor("clockStep"), /** * Gets or sets the desired clock multiplier. * See {@link Clock#multiplier}. * @memberof DataSourceClock.prototype * @type {Number} */ multiplier: createRawPropertyDescriptor("multiplier"), }); /** * Duplicates a DataSourceClock instance. * * @param {DataSourceClock} [result] The object onto which to store the result. * @returns {DataSourceClock} The modified result parameter or a new instance if one was not provided. */ DataSourceClock.prototype.clone = function (result) { if (!defined(result)) { result = new DataSourceClock(); } result.startTime = this.startTime; result.stopTime = this.stopTime; result.currentTime = this.currentTime; result.clockRange = this.clockRange; result.clockStep = this.clockStep; result.multiplier = this.multiplier; return result; }; /** * Returns true if this DataSourceClock is equivalent to the other * * @param {DataSourceClock} other The other DataSourceClock to compare to. * @returns {Boolean} true if the DataSourceClocks are equal; otherwise, false. */ DataSourceClock.prototype.equals = function (other) { return ( this === other || (defined(other) && JulianDate.equals(this.startTime, other.startTime) && JulianDate.equals(this.stopTime, other.stopTime) && JulianDate.equals(this.currentTime, other.currentTime) && this.clockRange === other.clockRange && this.clockStep === other.clockStep && this.multiplier === other.multiplier) ); }; /** * Assigns each unassigned property on this object to the value * of the same property on the provided source object. * * @param {DataSourceClock} source The object to be merged into this object. */ DataSourceClock.prototype.merge = function (source) { //>>includeStart('debug', pragmas.debug); if (!defined(source)) { throw new DeveloperError("source is required."); } //>>includeEnd('debug'); this.startTime = defaultValue(this.startTime, source.startTime); this.stopTime = defaultValue(this.stopTime, source.stopTime); this.currentTime = defaultValue(this.currentTime, source.currentTime); this.clockRange = defaultValue(this.clockRange, source.clockRange); this.clockStep = defaultValue(this.clockStep, source.clockStep); this.multiplier = defaultValue(this.multiplier, source.multiplier); }; /** * Gets the value of this clock instance as a {@link Clock} object. * * @returns {Clock} The modified result parameter or a new instance if one was not provided. */ DataSourceClock.prototype.getValue = function (result) { if (!defined(result)) { result = new Clock(); } result.startTime = defaultValue(this.startTime, result.startTime); result.stopTime = defaultValue(this.stopTime, result.stopTime); result.currentTime = defaultValue(this.currentTime, result.currentTime); result.clockRange = defaultValue(this.clockRange, result.clockRange); result.multiplier = defaultValue(this.multiplier, result.multiplier); result.clockStep = defaultValue(this.clockStep, result.clockStep); return result; }; var defaultColor$6 = Color.WHITE; var defaultCellAlpha = 0.1; var defaultLineCount = new Cartesian2(8, 8); var defaultLineOffset = new Cartesian2(0, 0); var defaultLineThickness = new Cartesian2(1, 1); /** * A {@link MaterialProperty} that maps to grid {@link Material} uniforms. * @alias GridMaterialProperty * * @param {Object} [options] Object with the following properties: * @param {Property|Color} [options.color=Color.WHITE] A Property specifying the grid {@link Color}. * @param {Property|Number} [options.cellAlpha=0.1] A numeric Property specifying cell alpha values. * @param {Property|Cartesian2} [options.lineCount=new Cartesian2(8, 8)] A {@link Cartesian2} Property specifying the number of grid lines along each axis. * @param {Property|Cartesian2} [options.lineThickness=new Cartesian2(1.0, 1.0)] A {@link Cartesian2} Property specifying the thickness of grid lines along each axis. * @param {Property|Cartesian2} [options.lineOffset=new Cartesian2(0.0, 0.0)] A {@link Cartesian2} Property specifying starting offset of grid lines along each axis. * * @constructor */ function GridMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this._cellAlpha = undefined; this._cellAlphaSubscription = undefined; this._lineCount = undefined; this._lineCountSubscription = undefined; this._lineThickness = undefined; this._lineThicknessSubscription = undefined; this._lineOffset = undefined; this._lineOffsetSubscription = undefined; this.color = options.color; this.cellAlpha = options.cellAlpha; this.lineCount = options.lineCount; this.lineThickness = options.lineThickness; this.lineOffset = options.lineOffset; } Object.defineProperties(GridMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof GridMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._color) && Property.isConstant(this._cellAlpha) && Property.isConstant(this._lineCount) && Property.isConstant(this._lineThickness) && Property.isConstant(this._lineOffset) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof GridMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the grid {@link Color}. * @memberof GridMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), /** * Gets or sets the numeric Property specifying cell alpha values. * @memberof GridMaterialProperty.prototype * @type {Property|undefined} * @default 0.1 */ cellAlpha: createPropertyDescriptor("cellAlpha"), /** * Gets or sets the {@link Cartesian2} Property specifying the number of grid lines along each axis. * @memberof GridMaterialProperty.prototype * @type {Property|undefined} * @default new Cartesian2(8.0, 8.0) */ lineCount: createPropertyDescriptor("lineCount"), /** * Gets or sets the {@link Cartesian2} Property specifying the thickness of grid lines along each axis. * @memberof GridMaterialProperty.prototype * @type {Property|undefined} * @default new Cartesian2(1.0, 1.0) */ lineThickness: createPropertyDescriptor("lineThickness"), /** * Gets or sets the {@link Cartesian2} Property specifying the starting offset of grid lines along each axis. * @memberof GridMaterialProperty.prototype * @type {Property|undefined} * @default new Cartesian2(0.0, 0.0) */ lineOffset: createPropertyDescriptor("lineOffset"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ GridMaterialProperty.prototype.getType = function (time) { return "Grid"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ GridMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, defaultColor$6, result.color ); result.cellAlpha = Property.getValueOrDefault( this._cellAlpha, time, defaultCellAlpha ); result.lineCount = Property.getValueOrClonedDefault( this._lineCount, time, defaultLineCount, result.lineCount ); result.lineThickness = Property.getValueOrClonedDefault( this._lineThickness, time, defaultLineThickness, result.lineThickness ); result.lineOffset = Property.getValueOrClonedDefault( this._lineOffset, time, defaultLineOffset, result.lineOffset ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ GridMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof GridMaterialProperty && // Property.equals(this._color, other._color) && // Property.equals(this._cellAlpha, other._cellAlpha) && // Property.equals(this._lineCount, other._lineCount) && // Property.equals(this._lineThickness, other._lineThickness) && // Property.equals(this._lineOffset, other._lineOffset)) ); }; /** * A {@link MaterialProperty} that maps to PolylineArrow {@link Material} uniforms. * * @param {Property|Color} [color=Color.WHITE] The {@link Color} Property to be used. * * @alias PolylineArrowMaterialProperty * @constructor */ function PolylineArrowMaterialProperty(color) { this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this.color = color; } Object.defineProperties(PolylineArrowMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof PolylineArrowMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return Property.isConstant(this._color); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof PolylineArrowMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the {@link Color} {@link Property}. * @memberof PolylineArrowMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ PolylineArrowMaterialProperty.prototype.getType = function (time) { return "PolylineArrow"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ PolylineArrowMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, Color.WHITE, result.color ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PolylineArrowMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof PolylineArrowMaterialProperty && // Property.equals(this._color, other._color)) ); }; var defaultColor$5 = Color.WHITE; var defaultGapColor = Color.TRANSPARENT; var defaultDashLength = 16.0; var defaultDashPattern = 255.0; /** * A {@link MaterialProperty} that maps to polyline dash {@link Material} uniforms. * @alias PolylineDashMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|Color} [options.color=Color.WHITE] A Property specifying the {@link Color} of the line. * @param {Property|Color} [options.gapColor=Color.TRANSPARENT] A Property specifying the {@link Color} of the gaps in the line. * @param {Property|Number} [options.dashLength=16.0] A numeric Property specifying the length of the dash pattern in pixels. * @param {Property|Number} [options.dashPattern=255.0] A numeric Property specifying a 16 bit pattern for the dash */ function PolylineDashMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this._gapColor = undefined; this._gapColorSubscription = undefined; this._dashLength = undefined; this._dashLengthSubscription = undefined; this._dashPattern = undefined; this._dashPatternSubscription = undefined; this.color = options.color; this.gapColor = options.gapColor; this.dashLength = options.dashLength; this.dashPattern = options.dashPattern; } Object.defineProperties(PolylineDashMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof PolylineDashMaterialProperty.prototype * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._color) && Property.isConstant(this._gapColor) && Property.isConstant(this._dashLength) && Property.isConstant(this._dashPattern) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof PolylineDashMaterialProperty.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the {@link Color} of the line. * @memberof PolylineDashMaterialProperty.prototype * @type {Property|undefined} */ color: createPropertyDescriptor("color"), /** * Gets or sets the Property specifying the {@link Color} of the gaps in the line. * @memberof PolylineDashMaterialProperty.prototype * @type {Property|undefined} */ gapColor: createPropertyDescriptor("gapColor"), /** * Gets or sets the numeric Property specifying the length of a dash cycle * @memberof PolylineDashMaterialProperty.prototype * @type {Property|undefined} */ dashLength: createPropertyDescriptor("dashLength"), /** * Gets or sets the numeric Property specifying a dash pattern * @memberof PolylineDashMaterialProperty.prototype * @type {Property|undefined} */ dashPattern: createPropertyDescriptor("dashPattern"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ PolylineDashMaterialProperty.prototype.getType = function (time) { return "PolylineDash"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ PolylineDashMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, defaultColor$5, result.color ); result.gapColor = Property.getValueOrClonedDefault( this._gapColor, time, defaultGapColor, result.gapColor ); result.dashLength = Property.getValueOrDefault( this._dashLength, time, defaultDashLength, result.dashLength ); result.dashPattern = Property.getValueOrDefault( this._dashPattern, time, defaultDashPattern, result.dashPattern ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PolylineDashMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof PolylineDashMaterialProperty && Property.equals(this._color, other._color) && Property.equals(this._gapColor, other._gapColor) && Property.equals(this._dashLength, other._dashLength) && Property.equals(this._dashPattern, other._dashPattern)) ); }; var defaultColor$4 = Color.WHITE; var defaultGlowPower = 0.25; var defaultTaperPower = 1.0; /** * A {@link MaterialProperty} that maps to polyline glow {@link Material} uniforms. * @alias PolylineGlowMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|Color} [options.color=Color.WHITE] A Property specifying the {@link Color} of the line. * @param {Property|Number} [options.glowPower=0.25] A numeric Property specifying the strength of the glow, as a percentage of the total line width. * @param {Property|Number} [options.taperPower=1.0] A numeric Property specifying the strength of the tapering effect, as a percentage of the total line length. If 1.0 or higher, no taper effect is used. */ function PolylineGlowMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this._glowPower = undefined; this._glowPowerSubscription = undefined; this._taperPower = undefined; this._taperPowerSubscription = undefined; this.color = options.color; this.glowPower = options.glowPower; this.taperPower = options.taperPower; } Object.defineProperties(PolylineGlowMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof PolylineGlowMaterialProperty.prototype * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._color) && Property.isConstant(this._glow) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof PolylineGlowMaterialProperty.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the {@link Color} of the line. * @memberof PolylineGlowMaterialProperty.prototype * @type {Property|undefined} */ color: createPropertyDescriptor("color"), /** * Gets or sets the numeric Property specifying the strength of the glow, as a percentage of the total line width (less than 1.0). * @memberof PolylineGlowMaterialProperty.prototype * @type {Property|undefined} */ glowPower: createPropertyDescriptor("glowPower"), /** * Gets or sets the numeric Property specifying the strength of the tapering effect, as a percentage of the total line length. If 1.0 or higher, no taper effect is used. * @memberof PolylineGlowMaterialProperty.prototype * @type {Property|undefined} */ taperPower: createPropertyDescriptor("taperPower"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ PolylineGlowMaterialProperty.prototype.getType = function (time) { return "PolylineGlow"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ PolylineGlowMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, defaultColor$4, result.color ); result.glowPower = Property.getValueOrDefault( this._glowPower, time, defaultGlowPower, result.glowPower ); result.taperPower = Property.getValueOrDefault( this._taperPower, time, defaultTaperPower, result.taperPower ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PolylineGlowMaterialProperty.prototype.equals = function (other) { return ( this === other || (other instanceof PolylineGlowMaterialProperty && Property.equals(this._color, other._color) && Property.equals(this._glowPower, other._glowPower) && Property.equals(this._taperPower, other._taperPower)) ); }; var defaultColor$3 = Color.WHITE; var defaultOutlineColor$2 = Color.BLACK; var defaultOutlineWidth$2 = 1.0; /** * A {@link MaterialProperty} that maps to polyline outline {@link Material} uniforms. * @alias PolylineOutlineMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|Color} [options.color=Color.WHITE] A Property specifying the {@link Color} of the line. * @param {Property|Color} [options.outlineColor=Color.BLACK] A Property specifying the {@link Color} of the outline. * @param {Property|Number} [options.outlineWidth=1.0] A numeric Property specifying the width of the outline, in pixels. */ function PolylineOutlineMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._color = undefined; this._colorSubscription = undefined; this._outlineColor = undefined; this._outlineColorSubscription = undefined; this._outlineWidth = undefined; this._outlineWidthSubscription = undefined; this.color = options.color; this.outlineColor = options.outlineColor; this.outlineWidth = options.outlineWidth; } Object.defineProperties(PolylineOutlineMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof PolylineOutlineMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._color) && Property.isConstant(this._outlineColor) && Property.isConstant(this._outlineWidth) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof PolylineOutlineMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the {@link Color} of the line. * @memberof PolylineOutlineMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ color: createPropertyDescriptor("color"), /** * Gets or sets the Property specifying the {@link Color} of the outline. * @memberof PolylineOutlineMaterialProperty.prototype * @type {Property|undefined} * @default Color.BLACK */ outlineColor: createPropertyDescriptor("outlineColor"), /** * Gets or sets the numeric Property specifying the width of the outline. * @memberof PolylineOutlineMaterialProperty.prototype * @type {Property|undefined} * @default 1.0 */ outlineWidth: createPropertyDescriptor("outlineWidth"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ PolylineOutlineMaterialProperty.prototype.getType = function (time) { return "PolylineOutline"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ PolylineOutlineMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.color = Property.getValueOrClonedDefault( this._color, time, defaultColor$3, result.color ); result.outlineColor = Property.getValueOrClonedDefault( this._outlineColor, time, defaultOutlineColor$2, result.outlineColor ); result.outlineWidth = Property.getValueOrDefault( this._outlineWidth, time, defaultOutlineWidth$2 ); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PolylineOutlineMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof PolylineOutlineMaterialProperty && // Property.equals(this._color, other._color) && // Property.equals(this._outlineColor, other._outlineColor) && // Property.equals(this._outlineWidth, other._outlineWidth)) ); }; /** * A {@link Property} whose value is an array whose items are the computed value * of other PositionProperty instances. * * @alias PositionPropertyArray * @constructor * * @param {Property[]} [value] An array of Property instances. * @param {ReferenceFrame} [referenceFrame=ReferenceFrame.FIXED] The reference frame in which the position is defined. */ function PositionPropertyArray(value, referenceFrame) { this._value = undefined; this._definitionChanged = new Event(); this._eventHelper = new EventHelper(); this._referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); this.setValue(value); } Object.defineProperties(PositionPropertyArray.prototype, { /** * Gets a value indicating if this property is constant. This property * is considered constant if all property items in the array are constant. * @memberof PositionPropertyArray.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { var value = this._value; if (!defined(value)) { return true; } var length = value.length; for (var i = 0; i < length; i++) { if (!Property.isConstant(value[i])) { return false; } } return true; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value or one of the properties in the array also changes. * @memberof PositionPropertyArray.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the reference frame in which the position is defined. * @memberof PositionPropertyArray.prototype * @type {ReferenceFrame} * @default ReferenceFrame.FIXED; */ referenceFrame: { get: function () { return this._referenceFrame; }, }, }); /** * Gets the value of the property. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Cartesian3[]} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3[]} The modified result parameter or a new instance if the result parameter was not supplied. */ PositionPropertyArray.prototype.getValue = function (time, result) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; /** * Gets the value of the property at the provided time and in the provided reference frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3[]} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3[]} The modified result parameter or a new instance if the result parameter was not supplied. */ PositionPropertyArray.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!defined(referenceFrame)) { throw new DeveloperError("referenceFrame is required."); } //>>includeEnd('debug'); var value = this._value; if (!defined(value)) { return undefined; } var length = value.length; if (!defined(result)) { result = new Array(length); } var i = 0; var x = 0; while (i < length) { var property = value[i]; var itemValue = property.getValueInReferenceFrame( time, referenceFrame, result[i] ); if (defined(itemValue)) { result[x] = itemValue; x++; } i++; } result.length = x; return result; }; /** * Sets the value of the property. * * @param {Property[]} value An array of Property instances. */ PositionPropertyArray.prototype.setValue = function (value) { var eventHelper = this._eventHelper; eventHelper.removeAll(); if (defined(value)) { this._value = value.slice(); var length = value.length; for (var i = 0; i < length; i++) { var property = value[i]; if (defined(property)) { eventHelper.add( property.definitionChanged, PositionPropertyArray.prototype._raiseDefinitionChanged, this ); } } } else { this._value = undefined; } this._definitionChanged.raiseEvent(this); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PositionPropertyArray.prototype.equals = function (other) { return ( this === other || // (other instanceof PositionPropertyArray && // this._referenceFrame === other._referenceFrame && // Property.arrayEquals(this._value, other._value)) ); }; PositionPropertyArray.prototype._raiseDefinitionChanged = function () { this._definitionChanged.raiseEvent(this); }; /** * A {@link Property} whose value is an array whose items are the computed value * of other property instances. * * @alias PropertyArray * @constructor * * @param {Property[]} [value] An array of Property instances. */ function PropertyArray(value) { this._value = undefined; this._definitionChanged = new Event(); this._eventHelper = new EventHelper(); this.setValue(value); } Object.defineProperties(PropertyArray.prototype, { /** * Gets a value indicating if this property is constant. This property * is considered constant if all property items in the array are constant. * @memberof PropertyArray.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { var value = this._value; if (!defined(value)) { return true; } var length = value.length; for (var i = 0; i < length; i++) { if (!Property.isConstant(value[i])) { return false; } } return true; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value or one of the properties in the array also changes. * @memberof PropertyArray.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, }); /** * Gets the value of the property. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object[]} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object[]} The modified result parameter, which is an array of values produced by evaluating each of the contained properties at the given time or a new instance if the result parameter was not supplied. */ PropertyArray.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var value = this._value; if (!defined(value)) { return undefined; } var length = value.length; if (!defined(result)) { result = new Array(length); } var i = 0; var x = 0; while (i < length) { var property = this._value[i]; var itemValue = property.getValue(time, result[i]); if (defined(itemValue)) { result[x] = itemValue; x++; } i++; } result.length = x; return result; }; /** * Sets the value of the property. * * @param {Property[]} value An array of Property instances. */ PropertyArray.prototype.setValue = function (value) { var eventHelper = this._eventHelper; eventHelper.removeAll(); if (defined(value)) { this._value = value.slice(); var length = value.length; for (var i = 0; i < length; i++) { var property = value[i]; if (defined(property)) { eventHelper.add( property.definitionChanged, PropertyArray.prototype._raiseDefinitionChanged, this ); } } } else { this._value = undefined; } this._definitionChanged.raiseEvent(this); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ PropertyArray.prototype.equals = function (other) { return ( this === other || // (other instanceof PropertyArray && // Property.arrayEquals(this._value, other._value)) ); }; PropertyArray.prototype._raiseDefinitionChanged = function () { this._definitionChanged.raiseEvent(this); }; function resolve(that) { var targetProperty = that._targetProperty; if (!defined(targetProperty)) { var targetEntity = that._targetEntity; if (!defined(targetEntity)) { targetEntity = that._targetCollection.getById(that._targetId); if (!defined(targetEntity)) { // target entity not found that._targetEntity = that._targetProperty = undefined; return; } // target entity was found. listen for changes to entity definition targetEntity.definitionChanged.addEventListener( ReferenceProperty.prototype._onTargetEntityDefinitionChanged, that ); that._targetEntity = targetEntity; } // walk the list of property names and resolve properties var targetPropertyNames = that._targetPropertyNames; targetProperty = that._targetEntity; for ( var i = 0, len = targetPropertyNames.length; i < len && defined(targetProperty); ++i ) { targetProperty = targetProperty[targetPropertyNames[i]]; } // target property may or may not be defined, depending on if it was found that._targetProperty = targetProperty; } return targetProperty; } /** * A {@link Property} which transparently links to another property on a provided object. * * @alias ReferenceProperty * @constructor * * @param {EntityCollection} targetCollection The entity collection which will be used to resolve the reference. * @param {String} targetId The id of the entity which is being referenced. * @param {String[]} targetPropertyNames The names of the property on the target entity which we will use. * * @example * var collection = new Cesium.EntityCollection(); * * //Create a new entity and assign a billboard scale. * var object1 = new Cesium.Entity({id:'object1'}); * object1.billboard = new Cesium.BillboardGraphics(); * object1.billboard.scale = new Cesium.ConstantProperty(2.0); * collection.add(object1); * * //Create a second entity and reference the scale from the first one. * var object2 = new Cesium.Entity({id:'object2'}); * object2.model = new Cesium.ModelGraphics(); * object2.model.scale = new Cesium.ReferenceProperty(collection, 'object1', ['billboard', 'scale']); * collection.add(object2); * * //Create a third object, but use the fromString helper function. * var object3 = new Cesium.Entity({id:'object3'}); * object3.billboard = new Cesium.BillboardGraphics(); * object3.billboard.scale = Cesium.ReferenceProperty.fromString(collection, 'object1#billboard.scale'); * collection.add(object3); * * //You can refer to an entity with a # or . in id and property names by escaping them. * var object4 = new Cesium.Entity({id:'#object.4'}); * object4.billboard = new Cesium.BillboardGraphics(); * object4.billboard.scale = new Cesium.ConstantProperty(2.0); * collection.add(object4); * * var object5 = new Cesium.Entity({id:'object5'}); * object5.billboard = new Cesium.BillboardGraphics(); * object5.billboard.scale = Cesium.ReferenceProperty.fromString(collection, '\\#object\\.4#billboard.scale'); * collection.add(object5); */ function ReferenceProperty(targetCollection, targetId, targetPropertyNames) { //>>includeStart('debug', pragmas.debug); if (!defined(targetCollection)) { throw new DeveloperError("targetCollection is required."); } if (!defined(targetId) || targetId === "") { throw new DeveloperError("targetId is required."); } if (!defined(targetPropertyNames) || targetPropertyNames.length === 0) { throw new DeveloperError("targetPropertyNames is required."); } for (var i = 0; i < targetPropertyNames.length; i++) { var item = targetPropertyNames[i]; if (!defined(item) || item === "") { throw new DeveloperError("reference contains invalid properties."); } } //>>includeEnd('debug'); this._targetCollection = targetCollection; this._targetId = targetId; this._targetPropertyNames = targetPropertyNames; this._targetProperty = undefined; this._targetEntity = undefined; this._definitionChanged = new Event(); targetCollection.collectionChanged.addEventListener( ReferenceProperty.prototype._onCollectionChanged, this ); } Object.defineProperties(ReferenceProperty.prototype, { /** * Gets a value indicating if this property is constant. * @memberof ReferenceProperty.prototype * @type {Boolean} * @readonly */ isConstant: { get: function () { return Property.isConstant(resolve(this)); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever the referenced property's definition is changed. * @memberof ReferenceProperty.prototype * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the reference frame that the position is defined in. * This property is only valid if the referenced property is a {@link PositionProperty}. * @memberof ReferenceProperty.prototype * @type {ReferenceFrame} * @readonly */ referenceFrame: { get: function () { var target = resolve(this); return defined(target) ? target.referenceFrame : undefined; }, }, /** * Gets the id of the entity being referenced. * @memberof ReferenceProperty.prototype * @type {String} * @readonly */ targetId: { get: function () { return this._targetId; }, }, /** * Gets the collection containing the entity being referenced. * @memberof ReferenceProperty.prototype * @type {EntityCollection} * @readonly */ targetCollection: { get: function () { return this._targetCollection; }, }, /** * Gets the array of property names used to retrieve the referenced property. * @memberof ReferenceProperty.prototype * @type {String[]} * @readonly */ targetPropertyNames: { get: function () { return this._targetPropertyNames; }, }, /** * Gets the resolved instance of the underlying referenced property. * @memberof ReferenceProperty.prototype * @type {Property|undefined} * @readonly */ resolvedProperty: { get: function () { return resolve(this); }, }, }); /** * Creates a new instance given the entity collection that will * be used to resolve it and a string indicating the target entity id and property. * The format of the string is "objectId#foo.bar", where # separates the id from * property path and . separates sub-properties. If the reference identifier or * or any sub-properties contains a # . or \ they must be escaped. * * @param {EntityCollection} targetCollection * @param {String} referenceString * @returns {ReferenceProperty} A new instance of ReferenceProperty. * * @exception {DeveloperError} invalid referenceString. */ ReferenceProperty.fromString = function (targetCollection, referenceString) { //>>includeStart('debug', pragmas.debug); if (!defined(targetCollection)) { throw new DeveloperError("targetCollection is required."); } if (!defined(referenceString)) { throw new DeveloperError("referenceString is required."); } //>>includeEnd('debug'); var identifier; var values = []; var inIdentifier = true; var isEscaped = false; var token = ""; for (var i = 0; i < referenceString.length; ++i) { var c = referenceString.charAt(i); if (isEscaped) { token += c; isEscaped = false; } else if (c === "\\") { isEscaped = true; } else if (inIdentifier && c === "#") { identifier = token; inIdentifier = false; token = ""; } else if (!inIdentifier && c === ".") { values.push(token); token = ""; } else { token += c; } } values.push(token); return new ReferenceProperty(targetCollection, identifier, values); }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ ReferenceProperty.prototype.getValue = function (time, result) { var target = resolve(this); return defined(target) ? target.getValue(time, result) : undefined; }; /** * Gets the value of the property at the provided time and in the provided reference frame. * This method is only valid if the property being referenced is a {@link PositionProperty}. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ ReferenceProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { var target = resolve(this); return defined(target) ? target.getValueInReferenceFrame(time, referenceFrame, result) : undefined; }; /** * Gets the {@link Material} type at the provided time. * This method is only valid if the property being referenced is a {@link MaterialProperty}. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ ReferenceProperty.prototype.getType = function (time) { var target = resolve(this); return defined(target) ? target.getType(time) : undefined; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ ReferenceProperty.prototype.equals = function (other) { if (this === other) { return true; } var names = this._targetPropertyNames; var otherNames = other._targetPropertyNames; if ( this._targetCollection !== other._targetCollection || // this._targetId !== other._targetId || // names.length !== otherNames.length ) { return false; } var length = this._targetPropertyNames.length; for (var i = 0; i < length; i++) { if (names[i] !== otherNames[i]) { return false; } } return true; }; ReferenceProperty.prototype._onTargetEntityDefinitionChanged = function ( targetEntity, name, value, oldValue ) { if (defined(this._targetProperty) && this._targetPropertyNames[0] === name) { this._targetProperty = undefined; this._definitionChanged.raiseEvent(this); } }; ReferenceProperty.prototype._onCollectionChanged = function ( collection, added, removed ) { var targetEntity = this._targetEntity; if (defined(targetEntity) && removed.indexOf(targetEntity) !== -1) { targetEntity.definitionChanged.removeEventListener( ReferenceProperty.prototype._onTargetEntityDefinitionChanged, this ); this._targetEntity = this._targetProperty = undefined; } else if (!defined(targetEntity)) { targetEntity = resolve(this); if (defined(targetEntity)) { this._definitionChanged.raiseEvent(this); } } }; /** * Represents a {@link Packable} number that always interpolates values * towards the shortest angle of rotation. This object is never used directly * but is instead passed to the constructor of {@link SampledProperty} * in order to represent a two-dimensional angle of rotation. * * @interface Rotation * * * @example * var time1 = Cesium.JulianDate.fromIso8601('2010-05-07T00:00:00'); * var time2 = Cesium.JulianDate.fromIso8601('2010-05-07T00:01:00'); * var time3 = Cesium.JulianDate.fromIso8601('2010-05-07T00:02:00'); * * var property = new Cesium.SampledProperty(Cesium.Rotation); * property.addSample(time1, 0); * property.addSample(time3, Cesium.Math.toRadians(350)); * * //Getting the value at time2 will equal 355 degrees instead * //of 175 degrees (which is what you get if you construct * //a SampledProperty(Number) instead. Note, the actual * //return value is in radians, not degrees. * property.getValue(time2); * * @see PackableForInterpolation */ var Rotation = { /** * The number of elements used to pack the object into an array. * @type {Number} */ packedLength: 1, /** * Stores the provided instance into the provided array. * * @param {Rotation} value The value to pack. * @param {Number[]} array The array to pack into. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. * * @returns {Number[]} The array that was packed into */ pack: function (value, array, startingIndex) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required"); } if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); array[startingIndex] = value; return array; }, /** * Retrieves an instance from a packed array. * * @param {Number[]} array The packed array. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. * @param {Rotation} [result] The object into which to store the result. * @returns {Rotation} The modified result parameter or a new Rotation instance if one was not provided. */ unpack: function (array, startingIndex, result) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } //>>includeEnd('debug'); startingIndex = defaultValue(startingIndex, 0); return array[startingIndex]; }, /** * Converts a packed array into a form suitable for interpolation. * * @param {Number[]} packedArray The packed array. * @param {Number} [startingIndex=0] The index of the first element to be converted. * @param {Number} [lastIndex=packedArray.length] The index of the last element to be converted. * @param {Number[]} [result] The object into which to store the result. */ convertPackedArrayForInterpolation: function ( packedArray, startingIndex, lastIndex, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(packedArray)) { throw new DeveloperError("packedArray is required"); } //>>includeEnd('debug'); if (!defined(result)) { result = []; } startingIndex = defaultValue(startingIndex, 0); lastIndex = defaultValue(lastIndex, packedArray.length); var previousValue; for (var i = 0, len = lastIndex - startingIndex + 1; i < len; i++) { var value = packedArray[startingIndex + i]; if (i === 0 || Math.abs(previousValue - value) < Math.PI) { result[i] = value; } else { result[i] = value - CesiumMath.TWO_PI; } previousValue = value; } }, /** * Retrieves an instance from a packed array converted with {@link Rotation.convertPackedArrayForInterpolation}. * * @param {Number[]} array The array previously packed for interpolation. * @param {Number[]} sourceArray The original packed array. * @param {Number} [firstIndex=0] The firstIndex used to convert the array. * @param {Number} [lastIndex=packedArray.length] The lastIndex used to convert the array. * @param {Rotation} [result] The object into which to store the result. * @returns {Rotation} The modified result parameter or a new Rotation instance if one was not provided. */ unpackInterpolationResult: function ( array, sourceArray, firstIndex, lastIndex, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(array)) { throw new DeveloperError("array is required"); } if (!defined(sourceArray)) { throw new DeveloperError("sourceArray is required"); } //>>includeEnd('debug'); result = array[0]; if (result < 0) { return result + CesiumMath.TWO_PI; } return result; }, }; var PackableNumber = { packedLength: 1, pack: function (value, array, startingIndex) { startingIndex = defaultValue(startingIndex, 0); array[startingIndex] = value; }, unpack: function (array, startingIndex, result) { startingIndex = defaultValue(startingIndex, 0); return array[startingIndex]; }, }; //We can't use splice for inserting new elements because function apply can't handle //a huge number of arguments. See https://code.google.com/p/chromium/issues/detail?id=56588 function arrayInsert(array, startIndex, items) { var i; var arrayLength = array.length; var itemsLength = items.length; var newLength = arrayLength + itemsLength; array.length = newLength; if (arrayLength !== startIndex) { var q = arrayLength - 1; for (i = newLength - 1; i >= startIndex; i--) { array[i] = array[q--]; } } for (i = 0; i < itemsLength; i++) { array[startIndex++] = items[i]; } } function convertDate(date, epoch) { if (date instanceof JulianDate) { return date; } if (typeof date === "string") { return JulianDate.fromIso8601(date); } return JulianDate.addSeconds(epoch, date, new JulianDate()); } var timesSpliceArgs = []; var valuesSpliceArgs = []; function mergeNewSamples(epoch, times, values, newData, packedLength) { var newDataIndex = 0; var i; var prevItem; var timesInsertionPoint; var valuesInsertionPoint; var currentTime; var nextTime; while (newDataIndex < newData.length) { currentTime = convertDate(newData[newDataIndex], epoch); timesInsertionPoint = binarySearch(times, currentTime, JulianDate.compare); var timesSpliceArgsCount = 0; var valuesSpliceArgsCount = 0; if (timesInsertionPoint < 0) { //Doesn't exist, insert as many additional values as we can. timesInsertionPoint = ~timesInsertionPoint; valuesInsertionPoint = timesInsertionPoint * packedLength; prevItem = undefined; nextTime = times[timesInsertionPoint]; while (newDataIndex < newData.length) { currentTime = convertDate(newData[newDataIndex], epoch); if ( (defined(prevItem) && JulianDate.compare(prevItem, currentTime) >= 0) || (defined(nextTime) && JulianDate.compare(currentTime, nextTime) >= 0) ) { break; } timesSpliceArgs[timesSpliceArgsCount++] = currentTime; newDataIndex = newDataIndex + 1; for (i = 0; i < packedLength; i++) { valuesSpliceArgs[valuesSpliceArgsCount++] = newData[newDataIndex]; newDataIndex = newDataIndex + 1; } prevItem = currentTime; } if (timesSpliceArgsCount > 0) { valuesSpliceArgs.length = valuesSpliceArgsCount; arrayInsert(values, valuesInsertionPoint, valuesSpliceArgs); timesSpliceArgs.length = timesSpliceArgsCount; arrayInsert(times, timesInsertionPoint, timesSpliceArgs); } } else { //Found an exact match for (i = 0; i < packedLength; i++) { newDataIndex++; values[timesInsertionPoint * packedLength + i] = newData[newDataIndex]; } newDataIndex++; } } } /** * A {@link Property} whose value is interpolated for a given time from the * provided set of samples and specified interpolation algorithm and degree. * @alias SampledProperty * @constructor * * @param {Number|Packable} type The type of property. * @param {Packable[]} [derivativeTypes] When supplied, indicates that samples will contain derivative information of the specified types. * * * @example * //Create a linearly interpolated Cartesian2 * var property = new Cesium.SampledProperty(Cesium.Cartesian2); * * //Populate it with data * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:00.00Z'), new Cesium.Cartesian2(0, 0)); * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-02T00:00:00.00Z'), new Cesium.Cartesian2(4, 7)); * * //Retrieve an interpolated value * var result = property.getValue(Cesium.JulianDate.fromIso8601('2012-08-01T12:00:00.00Z')); * * @example * //Create a simple numeric SampledProperty that uses third degree Hermite Polynomial Approximation * var property = new Cesium.SampledProperty(Number); * property.setInterpolationOptions({ * interpolationDegree : 3, * interpolationAlgorithm : Cesium.HermitePolynomialApproximation * }); * * //Populate it with data * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:00.00Z'), 1.0); * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:01:00.00Z'), 6.0); * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:02:00.00Z'), 12.0); * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:03:30.00Z'), 5.0); * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:06:30.00Z'), 2.0); * * //Samples can be added in any order. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:30.00Z'), 6.2); * * //Retrieve an interpolated value * var result = property.getValue(Cesium.JulianDate.fromIso8601('2012-08-01T00:02:34.00Z')); * * @see SampledPositionProperty */ function SampledProperty(type, derivativeTypes) { //>>includeStart('debug', pragmas.debug); Check.defined("type", type); //>>includeEnd('debug'); var innerType = type; if (innerType === Number) { innerType = PackableNumber; } var packedLength = innerType.packedLength; var packedInterpolationLength = defaultValue( innerType.packedInterpolationLength, packedLength ); var inputOrder = 0; var innerDerivativeTypes; if (defined(derivativeTypes)) { var length = derivativeTypes.length; innerDerivativeTypes = new Array(length); for (var i = 0; i < length; i++) { var derivativeType = derivativeTypes[i]; if (derivativeType === Number) { derivativeType = PackableNumber; } var derivativePackedLength = derivativeType.packedLength; packedLength += derivativePackedLength; packedInterpolationLength += defaultValue( derivativeType.packedInterpolationLength, derivativePackedLength ); innerDerivativeTypes[i] = derivativeType; } inputOrder = length; } this._type = type; this._innerType = innerType; this._interpolationDegree = 1; this._interpolationAlgorithm = LinearApproximation; this._numberOfPoints = 0; this._times = []; this._values = []; this._xTable = []; this._yTable = []; this._packedLength = packedLength; this._packedInterpolationLength = packedInterpolationLength; this._updateTableLength = true; this._interpolationResult = new Array(packedInterpolationLength); this._definitionChanged = new Event(); this._derivativeTypes = derivativeTypes; this._innerDerivativeTypes = innerDerivativeTypes; this._inputOrder = inputOrder; this._forwardExtrapolationType = ExtrapolationType$1.NONE; this._forwardExtrapolationDuration = 0; this._backwardExtrapolationType = ExtrapolationType$1.NONE; this._backwardExtrapolationDuration = 0; } Object.defineProperties(SampledProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof SampledProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._values.length === 0; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof SampledProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the type of property. * @memberof SampledProperty.prototype * @type {*} */ type: { get: function () { return this._type; }, }, /** * Gets the derivative types used by this property. * @memberof SampledProperty.prototype * @type {Packable[]} */ derivativeTypes: { get: function () { return this._derivativeTypes; }, }, /** * Gets the degree of interpolation to perform when retrieving a value. * @memberof SampledProperty.prototype * @type {Number} * @default 1 */ interpolationDegree: { get: function () { return this._interpolationDegree; }, }, /** * Gets the interpolation algorithm to use when retrieving a value. * @memberof SampledProperty.prototype * @type {InterpolationAlgorithm} * @default LinearApproximation */ interpolationAlgorithm: { get: function () { return this._interpolationAlgorithm; }, }, /** * Gets or sets the type of extrapolation to perform when a value * is requested at a time after any available samples. * @memberof SampledProperty.prototype * @type {ExtrapolationType} * @default ExtrapolationType.NONE */ forwardExtrapolationType: { get: function () { return this._forwardExtrapolationType; }, set: function (value) { if (this._forwardExtrapolationType !== value) { this._forwardExtrapolationType = value; this._definitionChanged.raiseEvent(this); } }, }, /** * Gets or sets the amount of time to extrapolate forward before * the property becomes undefined. A value of 0 will extrapolate forever. * @memberof SampledProperty.prototype * @type {Number} * @default 0 */ forwardExtrapolationDuration: { get: function () { return this._forwardExtrapolationDuration; }, set: function (value) { if (this._forwardExtrapolationDuration !== value) { this._forwardExtrapolationDuration = value; this._definitionChanged.raiseEvent(this); } }, }, /** * Gets or sets the type of extrapolation to perform when a value * is requested at a time before any available samples. * @memberof SampledProperty.prototype * @type {ExtrapolationType} * @default ExtrapolationType.NONE */ backwardExtrapolationType: { get: function () { return this._backwardExtrapolationType; }, set: function (value) { if (this._backwardExtrapolationType !== value) { this._backwardExtrapolationType = value; this._definitionChanged.raiseEvent(this); } }, }, /** * Gets or sets the amount of time to extrapolate backward * before the property becomes undefined. A value of 0 will extrapolate forever. * @memberof SampledProperty.prototype * @type {Number} * @default 0 */ backwardExtrapolationDuration: { get: function () { return this._backwardExtrapolationDuration; }, set: function (value) { if (this._backwardExtrapolationDuration !== value) { this._backwardExtrapolationDuration = value; this._definitionChanged.raiseEvent(this); } }, }, }); /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ SampledProperty.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var times = this._times; var timesLength = times.length; if (timesLength === 0) { return undefined; } var timeout; var innerType = this._innerType; var values = this._values; var index = binarySearch(times, time, JulianDate.compare); if (index < 0) { index = ~index; if (index === 0) { var startTime = times[index]; timeout = this._backwardExtrapolationDuration; if ( this._backwardExtrapolationType === ExtrapolationType$1.NONE || (timeout !== 0 && JulianDate.secondsDifference(startTime, time) > timeout) ) { return undefined; } if (this._backwardExtrapolationType === ExtrapolationType$1.HOLD) { return innerType.unpack(values, 0, result); } } if (index >= timesLength) { index = timesLength - 1; var endTime = times[index]; timeout = this._forwardExtrapolationDuration; if ( this._forwardExtrapolationType === ExtrapolationType$1.NONE || (timeout !== 0 && JulianDate.secondsDifference(time, endTime) > timeout) ) { return undefined; } if (this._forwardExtrapolationType === ExtrapolationType$1.HOLD) { index = timesLength - 1; return innerType.unpack(values, index * innerType.packedLength, result); } } var xTable = this._xTable; var yTable = this._yTable; var interpolationAlgorithm = this._interpolationAlgorithm; var packedInterpolationLength = this._packedInterpolationLength; var inputOrder = this._inputOrder; if (this._updateTableLength) { this._updateTableLength = false; var numberOfPoints = Math.min( interpolationAlgorithm.getRequiredDataPoints( this._interpolationDegree, inputOrder ), timesLength ); if (numberOfPoints !== this._numberOfPoints) { this._numberOfPoints = numberOfPoints; xTable.length = numberOfPoints; yTable.length = numberOfPoints * packedInterpolationLength; } } var degree = this._numberOfPoints - 1; if (degree < 1) { return undefined; } var firstIndex = 0; var lastIndex = timesLength - 1; var pointsInCollection = lastIndex - firstIndex + 1; if (pointsInCollection >= degree + 1) { var computedFirstIndex = index - ((degree / 2) | 0) - 1; if (computedFirstIndex < firstIndex) { computedFirstIndex = firstIndex; } var computedLastIndex = computedFirstIndex + degree; if (computedLastIndex > lastIndex) { computedLastIndex = lastIndex; computedFirstIndex = computedLastIndex - degree; if (computedFirstIndex < firstIndex) { computedFirstIndex = firstIndex; } } firstIndex = computedFirstIndex; lastIndex = computedLastIndex; } var length = lastIndex - firstIndex + 1; // Build the tables for (var i = 0; i < length; ++i) { xTable[i] = JulianDate.secondsDifference( times[firstIndex + i], times[lastIndex] ); } if (!defined(innerType.convertPackedArrayForInterpolation)) { var destinationIndex = 0; var packedLength = this._packedLength; var sourceIndex = firstIndex * packedLength; var stop = (lastIndex + 1) * packedLength; while (sourceIndex < stop) { yTable[destinationIndex] = values[sourceIndex]; sourceIndex++; destinationIndex++; } } else { innerType.convertPackedArrayForInterpolation( values, firstIndex, lastIndex, yTable ); } // Interpolate! var x = JulianDate.secondsDifference(time, times[lastIndex]); var interpolationResult; if (inputOrder === 0 || !defined(interpolationAlgorithm.interpolate)) { interpolationResult = interpolationAlgorithm.interpolateOrderZero( x, xTable, yTable, packedInterpolationLength, this._interpolationResult ); } else { var yStride = Math.floor(packedInterpolationLength / (inputOrder + 1)); interpolationResult = interpolationAlgorithm.interpolate( x, xTable, yTable, yStride, inputOrder, inputOrder, this._interpolationResult ); } if (!defined(innerType.unpackInterpolationResult)) { return innerType.unpack(interpolationResult, 0, result); } return innerType.unpackInterpolationResult( interpolationResult, values, firstIndex, lastIndex, result ); } return innerType.unpack(values, index * this._packedLength, result); }; /** * Sets the algorithm and degree to use when interpolating a value. * * @param {Object} [options] Object with the following properties: * @param {InterpolationAlgorithm} [options.interpolationAlgorithm] The new interpolation algorithm. If undefined, the existing property will be unchanged. * @param {Number} [options.interpolationDegree] The new interpolation degree. If undefined, the existing property will be unchanged. */ SampledProperty.prototype.setInterpolationOptions = function (options) { if (!defined(options)) { return; } var valuesChanged = false; var interpolationAlgorithm = options.interpolationAlgorithm; var interpolationDegree = options.interpolationDegree; if ( defined(interpolationAlgorithm) && this._interpolationAlgorithm !== interpolationAlgorithm ) { this._interpolationAlgorithm = interpolationAlgorithm; valuesChanged = true; } if ( defined(interpolationDegree) && this._interpolationDegree !== interpolationDegree ) { this._interpolationDegree = interpolationDegree; valuesChanged = true; } if (valuesChanged) { this._updateTableLength = true; this._definitionChanged.raiseEvent(this); } }; /** * Adds a new sample. * * @param {JulianDate} time The sample time. * @param {Packable} value The value at the provided time. * @param {Packable[]} [derivatives] The array of derivatives at the provided time. */ SampledProperty.prototype.addSample = function (time, value, derivatives) { var innerDerivativeTypes = this._innerDerivativeTypes; var hasDerivatives = defined(innerDerivativeTypes); //>>includeStart('debug', pragmas.debug); Check.defined("time", time); Check.defined("value", value); if (hasDerivatives) { Check.defined("derivatives", derivatives); } //>>includeEnd('debug'); var innerType = this._innerType; var data = []; data.push(time); innerType.pack(value, data, data.length); if (hasDerivatives) { var derivativesLength = innerDerivativeTypes.length; for (var x = 0; x < derivativesLength; x++) { innerDerivativeTypes[x].pack(derivatives[x], data, data.length); } } mergeNewSamples( undefined, this._times, this._values, data, this._packedLength ); this._updateTableLength = true; this._definitionChanged.raiseEvent(this); }; /** * Adds an array of samples. * * @param {JulianDate[]} times An array of JulianDate instances where each index is a sample time. * @param {Packable[]} values The array of values, where each value corresponds to the provided times index. * @param {Array[]} [derivativeValues] An array where each item is the array of derivatives at the equivalent time index. * * @exception {DeveloperError} times and values must be the same length. * @exception {DeveloperError} times and derivativeValues must be the same length. */ SampledProperty.prototype.addSamples = function ( times, values, derivativeValues ) { var innerDerivativeTypes = this._innerDerivativeTypes; var hasDerivatives = defined(innerDerivativeTypes); //>>includeStart('debug', pragmas.debug); Check.defined("times", times); Check.defined("values", values); if (times.length !== values.length) { throw new DeveloperError("times and values must be the same length."); } if ( hasDerivatives && (!defined(derivativeValues) || derivativeValues.length !== times.length) ) { throw new DeveloperError( "times and derivativeValues must be the same length." ); } //>>includeEnd('debug'); var innerType = this._innerType; var length = times.length; var data = []; for (var i = 0; i < length; i++) { data.push(times[i]); innerType.pack(values[i], data, data.length); if (hasDerivatives) { var derivatives = derivativeValues[i]; var derivativesLength = innerDerivativeTypes.length; for (var x = 0; x < derivativesLength; x++) { innerDerivativeTypes[x].pack(derivatives[x], data, data.length); } } } mergeNewSamples( undefined, this._times, this._values, data, this._packedLength ); this._updateTableLength = true; this._definitionChanged.raiseEvent(this); }; /** * Adds samples as a single packed array where each new sample is represented as a date, * followed by the packed representation of the corresponding value and derivatives. * * @param {Number[]} packedSamples The array of packed samples. * @param {JulianDate} [epoch] If any of the dates in packedSamples are numbers, they are considered an offset from this epoch, in seconds. */ SampledProperty.prototype.addSamplesPackedArray = function ( packedSamples, epoch ) { //>>includeStart('debug', pragmas.debug); Check.defined("packedSamples", packedSamples); //>>includeEnd('debug'); mergeNewSamples( epoch, this._times, this._values, packedSamples, this._packedLength ); this._updateTableLength = true; this._definitionChanged.raiseEvent(this); }; /** * Removes a sample at the given time, if present. * * @param {JulianDate} time The sample time. * @returns {Boolean} true if a sample at time was removed, false otherwise. */ SampledProperty.prototype.removeSample = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var index = binarySearch(this._times, time, JulianDate.compare); if (index < 0) { return false; } removeSamples(this, index, 1); return true; }; function removeSamples(property, startIndex, numberToRemove) { var packedLength = property._packedLength; property._times.splice(startIndex, numberToRemove); property._values.splice( startIndex * packedLength, numberToRemove * packedLength ); property._updateTableLength = true; property._definitionChanged.raiseEvent(property); } /** * Removes all samples for the given time interval. * * @param {TimeInterval} time The time interval for which to remove all samples. */ SampledProperty.prototype.removeSamples = function (timeInterval) { //>>includeStart('debug', pragmas.debug); Check.defined("timeInterval", timeInterval); //>>includeEnd('debug'); var times = this._times; var startIndex = binarySearch(times, timeInterval.start, JulianDate.compare); if (startIndex < 0) { startIndex = ~startIndex; } else if (!timeInterval.isStartIncluded) { ++startIndex; } var stopIndex = binarySearch(times, timeInterval.stop, JulianDate.compare); if (stopIndex < 0) { stopIndex = ~stopIndex; } else if (timeInterval.isStopIncluded) { ++stopIndex; } removeSamples(this, startIndex, stopIndex - startIndex); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ SampledProperty.prototype.equals = function (other) { if (this === other) { return true; } if (!defined(other)) { return false; } if ( this._type !== other._type || // this._interpolationDegree !== other._interpolationDegree || // this._interpolationAlgorithm !== other._interpolationAlgorithm ) { return false; } var derivativeTypes = this._derivativeTypes; var hasDerivatives = defined(derivativeTypes); var otherDerivativeTypes = other._derivativeTypes; var otherHasDerivatives = defined(otherDerivativeTypes); if (hasDerivatives !== otherHasDerivatives) { return false; } var i; var length; if (hasDerivatives) { length = derivativeTypes.length; if (length !== otherDerivativeTypes.length) { return false; } for (i = 0; i < length; i++) { if (derivativeTypes[i] !== otherDerivativeTypes[i]) { return false; } } } var times = this._times; var otherTimes = other._times; length = times.length; if (length !== otherTimes.length) { return false; } for (i = 0; i < length; i++) { if (!JulianDate.equals(times[i], otherTimes[i])) { return false; } } var values = this._values; var otherValues = other._values; length = values.length; //Since time lengths are equal, values length and other length are guaranteed to be equal. for (i = 0; i < length; i++) { if (values[i] !== otherValues[i]) { return false; } } return true; }; //Exposed for testing. SampledProperty._mergeNewSamples = mergeNewSamples; /** * A {@link SampledProperty} which is also a {@link PositionProperty}. * * @alias SampledPositionProperty * @constructor * * @param {ReferenceFrame} [referenceFrame=ReferenceFrame.FIXED] The reference frame in which the position is defined. * @param {Number} [numberOfDerivatives=0] The number of derivatives that accompany each position; i.e. velocity, acceleration, etc... */ function SampledPositionProperty(referenceFrame, numberOfDerivatives) { numberOfDerivatives = defaultValue(numberOfDerivatives, 0); var derivativeTypes; if (numberOfDerivatives > 0) { derivativeTypes = new Array(numberOfDerivatives); for (var i = 0; i < numberOfDerivatives; i++) { derivativeTypes[i] = Cartesian3; } } this._numberOfDerivatives = numberOfDerivatives; this._property = new SampledProperty(Cartesian3, derivativeTypes); this._definitionChanged = new Event(); this._referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); this._property._definitionChanged.addEventListener(function () { this._definitionChanged.raiseEvent(this); }, this); } Object.defineProperties(SampledPositionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof SampledPositionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._property.isConstant; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof SampledPositionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the reference frame in which the position is defined. * @memberof SampledPositionProperty.prototype * @type {ReferenceFrame} * @default ReferenceFrame.FIXED; */ referenceFrame: { get: function () { return this._referenceFrame; }, }, /** * Gets the degree of interpolation to perform when retrieving a value. Call setInterpolationOptions to set this. * @memberof SampledPositionProperty.prototype * * @type {Number} * @default 1 * @readonly */ interpolationDegree: { get: function () { return this._property.interpolationDegree; }, }, /** * Gets the interpolation algorithm to use when retrieving a value. Call setInterpolationOptions to set this. * @memberof SampledPositionProperty.prototype * * @type {InterpolationAlgorithm} * @default LinearApproximation * @readonly */ interpolationAlgorithm: { get: function () { return this._property.interpolationAlgorithm; }, }, /** * The number of derivatives contained by this property; i.e. 0 for just position, 1 for velocity, etc. * @memberof SampledPositionProperty.prototype * * @type {Number} * @default 0 */ numberOfDerivatives: { get: function () { return this._numberOfDerivatives; }, }, /** * Gets or sets the type of extrapolation to perform when a value * is requested at a time after any available samples. * @memberof SampledPositionProperty.prototype * @type {ExtrapolationType} * @default ExtrapolationType.NONE */ forwardExtrapolationType: { get: function () { return this._property.forwardExtrapolationType; }, set: function (value) { this._property.forwardExtrapolationType = value; }, }, /** * Gets or sets the amount of time to extrapolate forward before * the property becomes undefined. A value of 0 will extrapolate forever. * @memberof SampledPositionProperty.prototype * @type {Number} * @default 0 */ forwardExtrapolationDuration: { get: function () { return this._property.forwardExtrapolationDuration; }, set: function (value) { this._property.forwardExtrapolationDuration = value; }, }, /** * Gets or sets the type of extrapolation to perform when a value * is requested at a time before any available samples. * @memberof SampledPositionProperty.prototype * @type {ExtrapolationType} * @default ExtrapolationType.NONE */ backwardExtrapolationType: { get: function () { return this._property.backwardExtrapolationType; }, set: function (value) { this._property.backwardExtrapolationType = value; }, }, /** * Gets or sets the amount of time to extrapolate backward * before the property becomes undefined. A value of 0 will extrapolate forever. * @memberof SampledPositionProperty.prototype * @type {Number} * @default 0 */ backwardExtrapolationDuration: { get: function () { return this._property.backwardExtrapolationDuration; }, set: function (value) { this._property.backwardExtrapolationDuration = value; }, }, }); /** * Gets the position at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ SampledPositionProperty.prototype.getValue = function (time, result) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; /** * Gets the position at the provided time and in the provided reference frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ SampledPositionProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); Check.defined("referenceFrame", referenceFrame); //>>includeEnd('debug'); result = this._property.getValue(time, result); if (defined(result)) { return PositionProperty.convertToReferenceFrame( time, result, this._referenceFrame, referenceFrame, result ); } return undefined; }; /** * Sets the algorithm and degree to use when interpolating a position. * * @param {Object} [options] Object with the following properties: * @param {InterpolationAlgorithm} [options.interpolationAlgorithm] The new interpolation algorithm. If undefined, the existing property will be unchanged. * @param {Number} [options.interpolationDegree] The new interpolation degree. If undefined, the existing property will be unchanged. */ SampledPositionProperty.prototype.setInterpolationOptions = function (options) { this._property.setInterpolationOptions(options); }; /** * Adds a new sample. * * @param {JulianDate} time The sample time. * @param {Cartesian3} position The position at the provided time. * @param {Cartesian3[]} [derivatives] The array of derivative values at the provided time. */ SampledPositionProperty.prototype.addSample = function ( time, position, derivatives ) { var numberOfDerivatives = this._numberOfDerivatives; //>>includeStart('debug', pragmas.debug); if ( numberOfDerivatives > 0 && (!defined(derivatives) || derivatives.length !== numberOfDerivatives) ) { throw new DeveloperError( "derivatives length must be equal to the number of derivatives." ); } //>>includeEnd('debug'); this._property.addSample(time, position, derivatives); }; /** * Adds multiple samples via parallel arrays. * * @param {JulianDate[]} times An array of JulianDate instances where each index is a sample time. * @param {Cartesian3[]} positions An array of Cartesian3 position instances, where each value corresponds to the provided time index. * @param {Array[]} [derivatives] An array where each value is another array containing derivatives for the corresponding time index. * * @exception {DeveloperError} All arrays must be the same length. */ SampledPositionProperty.prototype.addSamples = function ( times, positions, derivatives ) { this._property.addSamples(times, positions, derivatives); }; /** * Adds samples as a single packed array where each new sample is represented as a date, * followed by the packed representation of the corresponding value and derivatives. * * @param {Number[]} packedSamples The array of packed samples. * @param {JulianDate} [epoch] If any of the dates in packedSamples are numbers, they are considered an offset from this epoch, in seconds. */ SampledPositionProperty.prototype.addSamplesPackedArray = function ( packedSamples, epoch ) { this._property.addSamplesPackedArray(packedSamples, epoch); }; /** * Removes a sample at the given time, if present. * * @param {JulianDate} time The sample time. * @returns {Boolean} true if a sample at time was removed, false otherwise. */ SampledPositionProperty.prototype.removeSample = function (time) { return this._property.removeSample(time); }; /** * Removes all samples for the given time interval. * * @param {TimeInterval} time The time interval for which to remove all samples. */ SampledPositionProperty.prototype.removeSamples = function (timeInterval) { this._property.removeSamples(timeInterval); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ SampledPositionProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof SampledPositionProperty && Property.equals(this._property, other._property) && // this._referenceFrame === other._referenceFrame) ); }; /** * Defined the orientation of stripes in {@link StripeMaterialProperty}. * * @enum {Number} */ var StripeOrientation = { /** * Horizontal orientation. * @type {Number} */ HORIZONTAL: 0, /** * Vertical orientation. * @type {Number} */ VERTICAL: 1, }; var StripeOrientation$1 = Object.freeze(StripeOrientation); var defaultOrientation = StripeOrientation$1.HORIZONTAL; var defaultEvenColor = Color.WHITE; var defaultOddColor = Color.BLACK; var defaultOffset$7 = 0; var defaultRepeat = 1; /** * A {@link MaterialProperty} that maps to stripe {@link Material} uniforms. * @alias StripeMaterialProperty * @constructor * * @param {Object} [options] Object with the following properties: * @param {Property|StripeOrientation} [options.orientation=StripeOrientation.HORIZONTAL] A Property specifying the {@link StripeOrientation}. * @param {Property|Color} [options.evenColor=Color.WHITE] A Property specifying the first {@link Color}. * @param {Property|Color} [options.oddColor=Color.BLACK] A Property specifying the second {@link Color}. * @param {Property|Number} [options.offset=0] A numeric Property specifying how far into the pattern to start the material. * @param {Property|Number} [options.repeat=1] A numeric Property specifying how many times the stripes repeat. */ function StripeMaterialProperty(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._definitionChanged = new Event(); this._orientation = undefined; this._orientationSubscription = undefined; this._evenColor = undefined; this._evenColorSubscription = undefined; this._oddColor = undefined; this._oddColorSubscription = undefined; this._offset = undefined; this._offsetSubscription = undefined; this._repeat = undefined; this._repeatSubscription = undefined; this.orientation = options.orientation; this.evenColor = options.evenColor; this.oddColor = options.oddColor; this.offset = options.offset; this.repeat = options.repeat; } Object.defineProperties(StripeMaterialProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof StripeMaterialProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return ( Property.isConstant(this._orientation) && // Property.isConstant(this._evenColor) && // Property.isConstant(this._oddColor) && // Property.isConstant(this._offset) && // Property.isConstant(this._repeat) ); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof StripeMaterialProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the Property specifying the {@link StripeOrientation}/ * @memberof StripeMaterialProperty.prototype * @type {Property|undefined} * @default StripeOrientation.HORIZONTAL */ orientation: createPropertyDescriptor("orientation"), /** * Gets or sets the Property specifying the first {@link Color}. * @memberof StripeMaterialProperty.prototype * @type {Property|undefined} * @default Color.WHITE */ evenColor: createPropertyDescriptor("evenColor"), /** * Gets or sets the Property specifying the second {@link Color}. * @memberof StripeMaterialProperty.prototype * @type {Property|undefined} * @default Color.BLACK */ oddColor: createPropertyDescriptor("oddColor"), /** * Gets or sets the numeric Property specifying the point into the pattern * to begin drawing; with 0.0 being the beginning of the even color, 1.0 the beginning * of the odd color, 2.0 being the even color again, and any multiple or fractional values * being in between. * @memberof StripeMaterialProperty.prototype * @type {Property|undefined} * @default 0.0 */ offset: createPropertyDescriptor("offset"), /** * Gets or sets the numeric Property specifying how many times the stripes repeat. * @memberof StripeMaterialProperty.prototype * @type {Property|undefined} * @default 1.0 */ repeat: createPropertyDescriptor("repeat"), }); /** * Gets the {@link Material} type at the provided time. * * @param {JulianDate} time The time for which to retrieve the type. * @returns {String} The type of material. */ StripeMaterialProperty.prototype.getType = function (time) { return "Stripe"; }; /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ StripeMaterialProperty.prototype.getValue = function (time, result) { if (!defined(result)) { result = {}; } result.horizontal = Property.getValueOrDefault(this._orientation, time, defaultOrientation) === StripeOrientation$1.HORIZONTAL; result.evenColor = Property.getValueOrClonedDefault( this._evenColor, time, defaultEvenColor, result.evenColor ); result.oddColor = Property.getValueOrClonedDefault( this._oddColor, time, defaultOddColor, result.oddColor ); result.offset = Property.getValueOrDefault(this._offset, time, defaultOffset$7); result.repeat = Property.getValueOrDefault(this._repeat, time, defaultRepeat); return result; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ StripeMaterialProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof StripeMaterialProperty && // Property.equals(this._orientation, other._orientation) && // Property.equals(this._evenColor, other._evenColor) && // Property.equals(this._oddColor, other._oddColor) && // Property.equals(this._offset, other._offset) && // Property.equals(this._repeat, other._repeat)) ); }; /** * A {@link TimeIntervalCollectionProperty} which is also a {@link PositionProperty}. * * @alias TimeIntervalCollectionPositionProperty * @constructor * * @param {ReferenceFrame} [referenceFrame=ReferenceFrame.FIXED] The reference frame in which the position is defined. */ function TimeIntervalCollectionPositionProperty(referenceFrame) { this._definitionChanged = new Event(); this._intervals = new TimeIntervalCollection(); this._intervals.changedEvent.addEventListener( TimeIntervalCollectionPositionProperty.prototype._intervalsChanged, this ); this._referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); } Object.defineProperties(TimeIntervalCollectionPositionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof TimeIntervalCollectionPositionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._intervals.isEmpty; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is considered to have changed if a call to getValue would return * a different result for the same time. * @memberof TimeIntervalCollectionPositionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the interval collection. * @memberof TimeIntervalCollectionPositionProperty.prototype * @type {TimeIntervalCollection} */ intervals: { get: function () { return this._intervals; }, }, /** * Gets the reference frame in which the position is defined. * @memberof TimeIntervalCollectionPositionProperty.prototype * @type {ReferenceFrame} * @default ReferenceFrame.FIXED; */ referenceFrame: { get: function () { return this._referenceFrame; }, }, }); /** * Gets the value of the property at the provided time in the fixed frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ TimeIntervalCollectionPositionProperty.prototype.getValue = function ( time, result ) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; /** * Gets the value of the property at the provided time and in the provided reference frame. * * @param {JulianDate} time The time for which to retrieve the value. * @param {ReferenceFrame} referenceFrame The desired referenceFrame of the result. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ TimeIntervalCollectionPositionProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!defined(referenceFrame)) { throw new DeveloperError("referenceFrame is required."); } //>>includeEnd('debug'); var position = this._intervals.findDataForIntervalContainingDate(time); if (defined(position)) { return PositionProperty.convertToReferenceFrame( time, position, this._referenceFrame, referenceFrame, result ); } return undefined; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ TimeIntervalCollectionPositionProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof TimeIntervalCollectionPositionProperty && // this._intervals.equals(other._intervals, Property.equals) && // this._referenceFrame === other._referenceFrame) ); }; /** * @private */ TimeIntervalCollectionPositionProperty.prototype._intervalsChanged = function () { this._definitionChanged.raiseEvent(this); }; /** * A {@link Property} which is defined by a {@link TimeIntervalCollection}, where the * data property of each {@link TimeInterval} represents the value at time. * * @alias TimeIntervalCollectionProperty * @constructor * * @example * //Create a Cartesian2 interval property which contains data on August 1st, 2012 * //and uses a different value every 6 hours. * var composite = new Cesium.TimeIntervalCollectionProperty(); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T00:00:00.00Z/2012-08-01T06:00:00.00Z', * isStartIncluded : true, * isStopIncluded : false, * data : new Cesium.Cartesian2(2.0, 3.4) * })); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T06:00:00.00Z/2012-08-01T12:00:00.00Z', * isStartIncluded : true, * isStopIncluded : false, * data : new Cesium.Cartesian2(12.0, 2.7) * })); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T12:00:00.00Z/2012-08-01T18:00:00.00Z', * isStartIncluded : true, * isStopIncluded : false, * data : new Cesium.Cartesian2(5.0, 12.4) * })); * composite.intervals.addInterval(Cesium.TimeInterval.fromIso8601({ * iso8601 : '2012-08-01T18:00:00.00Z/2012-08-02T00:00:00.00Z', * isStartIncluded : true, * isStopIncluded : true, * data : new Cesium.Cartesian2(85.0, 4.1) * })); */ function TimeIntervalCollectionProperty() { this._definitionChanged = new Event(); this._intervals = new TimeIntervalCollection(); this._intervals.changedEvent.addEventListener( TimeIntervalCollectionProperty.prototype._intervalsChanged, this ); } Object.defineProperties(TimeIntervalCollectionProperty.prototype, { /** * Gets a value indicating if this property is constant. A property is considered * constant if getValue always returns the same result for the current definition. * @memberof TimeIntervalCollectionProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return this._intervals.isEmpty; }, }, /** * Gets the event that is raised whenever the definition of this property changes. * The definition is changed whenever setValue is called with data different * than the current value. * @memberof TimeIntervalCollectionProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets the interval collection. * @memberof TimeIntervalCollectionProperty.prototype * * @type {TimeIntervalCollection} */ intervals: { get: function () { return this._intervals; }, }, }); /** * Gets the value of the property at the provided time. * * @param {JulianDate} time The time for which to retrieve the value. * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied. */ TimeIntervalCollectionProperty.prototype.getValue = function (time, result) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required"); } //>>includeEnd('debug'); var value = this._intervals.findDataForIntervalContainingDate(time); if (defined(value) && typeof value.clone === "function") { return value.clone(result); } return value; }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ TimeIntervalCollectionProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof TimeIntervalCollectionProperty && // this._intervals.equals(other._intervals, Property.equals)) ); }; /** * @private */ TimeIntervalCollectionProperty.prototype._intervalsChanged = function () { this._definitionChanged.raiseEvent(this); }; /** * A {@link Property} which evaluates to a {@link Cartesian3} vector * based on the velocity of the provided {@link PositionProperty}. * * @alias VelocityVectorProperty * @constructor * * @param {PositionProperty} [position] The position property used to compute the velocity. * @param {Boolean} [normalize=true] Whether to normalize the computed velocity vector. * * @example * //Create an entity with a billboard rotated to match its velocity. * var position = new Cesium.SampledProperty(); * position.addSamples(...); * var entity = viewer.entities.add({ * position : position, * billboard : { * image : 'image.png', * alignedAxis : new Cesium.VelocityVectorProperty(position, true) // alignedAxis must be a unit vector * } * })); */ function VelocityVectorProperty(position, normalize) { this._position = undefined; this._subscription = undefined; this._definitionChanged = new Event(); this._normalize = defaultValue(normalize, true); this.position = position; } Object.defineProperties(VelocityVectorProperty.prototype, { /** * Gets a value indicating if this property is constant. * @memberof VelocityVectorProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return Property.isConstant(this._position); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * @memberof VelocityVectorProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the position property used to compute the velocity vector. * @memberof VelocityVectorProperty.prototype * * @type {Property|undefined} */ position: { get: function () { return this._position; }, set: function (value) { var oldValue = this._position; if (oldValue !== value) { if (defined(oldValue)) { this._subscription(); } this._position = value; if (defined(value)) { this._subscription = value._definitionChanged.addEventListener( function () { this._definitionChanged.raiseEvent(this); }, this ); } this._definitionChanged.raiseEvent(this); } }, }, /** * Gets or sets whether the vector produced by this property * will be normalized or not. * @memberof VelocityVectorProperty.prototype * * @type {Boolean} */ normalize: { get: function () { return this._normalize; }, set: function (value) { if (this._normalize === value) { return; } this._normalize = value; this._definitionChanged.raiseEvent(this); }, }, }); var position1Scratch = new Cartesian3(); var position2Scratch = new Cartesian3(); var timeScratch = new JulianDate(); var step = 1.0 / 60.0; /** * Gets the value of the property at the provided time. * * @param {JulianDate} [time] The time for which to retrieve the value. * @param {Cartesian3} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Cartesian3} The modified result parameter or a new instance if the result parameter was not supplied. */ VelocityVectorProperty.prototype.getValue = function (time, result) { return this._getValue(time, result); }; /** * @private */ VelocityVectorProperty.prototype._getValue = function ( time, velocityResult, positionResult ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required"); } //>>includeEnd('debug'); if (!defined(velocityResult)) { velocityResult = new Cartesian3(); } var property = this._position; if (Property.isConstant(property)) { return this._normalize ? undefined : Cartesian3.clone(Cartesian3.ZERO, velocityResult); } var position1 = property.getValue(time, position1Scratch); var position2 = property.getValue( JulianDate.addSeconds(time, step, timeScratch), position2Scratch ); //If we don't have a position for now, return undefined. if (!defined(position1)) { return undefined; } //If we don't have a position for now + step, see if we have a position for now - step. if (!defined(position2)) { position2 = position1; position1 = property.getValue( JulianDate.addSeconds(time, -step, timeScratch), position2Scratch ); if (!defined(position1)) { return undefined; } } if (Cartesian3.equals(position1, position2)) { return this._normalize ? undefined : Cartesian3.clone(Cartesian3.ZERO, velocityResult); } if (defined(positionResult)) { position1.clone(positionResult); } var velocity = Cartesian3.subtract(position2, position1, velocityResult); if (this._normalize) { return Cartesian3.normalize(velocity, velocityResult); } return Cartesian3.divideByScalar(velocity, step, velocityResult); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ VelocityVectorProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof VelocityVectorProperty && Property.equals(this._position, other._position)) ); }; /** * A {@link Property} which evaluates to a {@link Quaternion} rotation * based on the velocity of the provided {@link PositionProperty}. * * @alias VelocityOrientationProperty * @constructor * * @param {PositionProperty} [position] The position property used to compute the orientation. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine which way is up. * * @example * //Create an entity with position and orientation. * var position = new Cesium.SampledProperty(); * position.addSamples(...); * var entity = viewer.entities.add({ * position : position, * orientation : new Cesium.VelocityOrientationProperty(position) * })); */ function VelocityOrientationProperty(position, ellipsoid) { this._velocityVectorProperty = new VelocityVectorProperty(position, true); this._subscription = undefined; this._ellipsoid = undefined; this._definitionChanged = new Event(); this.ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); var that = this; this._velocityVectorProperty.definitionChanged.addEventListener(function () { that._definitionChanged.raiseEvent(that); }); } Object.defineProperties(VelocityOrientationProperty.prototype, { /** * Gets a value indicating if this property is constant. * @memberof VelocityOrientationProperty.prototype * * @type {Boolean} * @readonly */ isConstant: { get: function () { return Property.isConstant(this._velocityVectorProperty); }, }, /** * Gets the event that is raised whenever the definition of this property changes. * @memberof VelocityOrientationProperty.prototype * * @type {Event} * @readonly */ definitionChanged: { get: function () { return this._definitionChanged; }, }, /** * Gets or sets the position property used to compute orientation. * @memberof VelocityOrientationProperty.prototype * * @type {Property|undefined} */ position: { get: function () { return this._velocityVectorProperty.position; }, set: function (value) { this._velocityVectorProperty.position = value; }, }, /** * Gets or sets the ellipsoid used to determine which way is up. * @memberof VelocityOrientationProperty.prototype * * @type {Property|undefined} */ ellipsoid: { get: function () { return this._ellipsoid; }, set: function (value) { var oldValue = this._ellipsoid; if (oldValue !== value) { this._ellipsoid = value; this._definitionChanged.raiseEvent(this); } }, }, }); var positionScratch$3 = new Cartesian3(); var velocityScratch = new Cartesian3(); var rotationScratch$1 = new Matrix3(); /** * Gets the value of the property at the provided time. * * @param {JulianDate} [time] The time for which to retrieve the value. * @param {Quaternion} [result] The object to store the value into, if omitted, a new instance is created and returned. * @returns {Quaternion} The modified result parameter or a new instance if the result parameter was not supplied. */ VelocityOrientationProperty.prototype.getValue = function (time, result) { var velocity = this._velocityVectorProperty._getValue( time, velocityScratch, positionScratch$3 ); if (!defined(velocity)) { return undefined; } Transforms.rotationMatrixFromPositionVelocity( positionScratch$3, velocity, this._ellipsoid, rotationScratch$1 ); return Quaternion.fromRotationMatrix(rotationScratch$1, result); }; /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. * * @param {Property} [other] The other property. * @returns {Boolean} true if left and right are equal, false otherwise. */ VelocityOrientationProperty.prototype.equals = function (other) { return ( this === other || // (other instanceof VelocityOrientationProperty && Property.equals( this._velocityVectorProperty, other._velocityVectorProperty ) && (this._ellipsoid === other._ellipsoid || this._ellipsoid.equals(other._ellipsoid))) ); }; // A marker type to distinguish CZML properties where we need to end up with a unit vector. // The data is still loaded into Cartesian3 objects but they are normalized. function UnitCartesian3() {} UnitCartesian3.packedLength = Cartesian3.packedLength; UnitCartesian3.unpack = Cartesian3.unpack; UnitCartesian3.pack = Cartesian3.pack; // As a side note, for the purposes of CZML, Quaternion always indicates a unit quaternion. var currentId; function createReferenceProperty(entityCollection, referenceString) { if (referenceString[0] === "#") { referenceString = currentId + referenceString; } return ReferenceProperty.fromString(entityCollection, referenceString); } function createSpecializedProperty(type, entityCollection, packetData) { if (defined(packetData.reference)) { return createReferenceProperty(entityCollection, packetData.reference); } if (defined(packetData.velocityReference)) { var referenceProperty = createReferenceProperty( entityCollection, packetData.velocityReference ); switch (type) { case Cartesian3: case UnitCartesian3: return new VelocityVectorProperty( referenceProperty, type === UnitCartesian3 ); case Quaternion: return new VelocityOrientationProperty(referenceProperty); } } throw new RuntimeError(JSON.stringify(packetData) + " is not valid CZML."); } function createAdapterProperty(property, adapterFunction) { return new CallbackProperty(function (time, result) { return adapterFunction(property.getValue(time, result)); }, property.isConstant); } var scratchCartesian$3 = new Cartesian3(); var scratchSpherical = new Spherical(); var scratchCartographic$7 = new Cartographic(); var scratchTimeInterval$1 = new TimeInterval(); var scratchQuaternion = new Quaternion(); function unwrapColorInterval(czmlInterval) { var rgbaf = czmlInterval.rgbaf; if (defined(rgbaf)) { return rgbaf; } var rgba = czmlInterval.rgba; if (!defined(rgba)) { return undefined; } var length = rgba.length; if (length === Color.packedLength) { return [ Color.byteToFloat(rgba[0]), Color.byteToFloat(rgba[1]), Color.byteToFloat(rgba[2]), Color.byteToFloat(rgba[3]), ]; } rgbaf = new Array(length); for (var i = 0; i < length; i += 5) { rgbaf[i] = rgba[i]; rgbaf[i + 1] = Color.byteToFloat(rgba[i + 1]); rgbaf[i + 2] = Color.byteToFloat(rgba[i + 2]); rgbaf[i + 3] = Color.byteToFloat(rgba[i + 3]); rgbaf[i + 4] = Color.byteToFloat(rgba[i + 4]); } return rgbaf; } function unwrapUriInterval(czmlInterval, sourceUri) { var uri = defaultValue(czmlInterval.uri, czmlInterval); if (defined(sourceUri)) { return sourceUri.getDerivedResource({ url: uri, }); } return Resource.createIfNeeded(uri); } function unwrapRectangleInterval(czmlInterval) { var wsen = czmlInterval.wsen; if (defined(wsen)) { return wsen; } var wsenDegrees = czmlInterval.wsenDegrees; if (!defined(wsenDegrees)) { return undefined; } var length = wsenDegrees.length; if (length === Rectangle.packedLength) { return [ CesiumMath.toRadians(wsenDegrees[0]), CesiumMath.toRadians(wsenDegrees[1]), CesiumMath.toRadians(wsenDegrees[2]), CesiumMath.toRadians(wsenDegrees[3]), ]; } wsen = new Array(length); for (var i = 0; i < length; i += 5) { wsen[i] = wsenDegrees[i]; wsen[i + 1] = CesiumMath.toRadians(wsenDegrees[i + 1]); wsen[i + 2] = CesiumMath.toRadians(wsenDegrees[i + 2]); wsen[i + 3] = CesiumMath.toRadians(wsenDegrees[i + 3]); wsen[i + 4] = CesiumMath.toRadians(wsenDegrees[i + 4]); } return wsen; } function convertUnitSphericalToCartesian(unitSpherical) { var length = unitSpherical.length; scratchSpherical.magnitude = 1.0; if (length === 2) { scratchSpherical.clock = unitSpherical[0]; scratchSpherical.cone = unitSpherical[1]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian$3); return [scratchCartesian$3.x, scratchCartesian$3.y, scratchCartesian$3.z]; } var result = new Array((length / 3) * 4); for (var i = 0, j = 0; i < length; i += 3, j += 4) { result[j] = unitSpherical[i]; scratchSpherical.clock = unitSpherical[i + 1]; scratchSpherical.cone = unitSpherical[i + 2]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian$3); result[j + 1] = scratchCartesian$3.x; result[j + 2] = scratchCartesian$3.y; result[j + 3] = scratchCartesian$3.z; } return result; } function convertSphericalToCartesian(spherical) { var length = spherical.length; if (length === 3) { scratchSpherical.clock = spherical[0]; scratchSpherical.cone = spherical[1]; scratchSpherical.magnitude = spherical[2]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian$3); return [scratchCartesian$3.x, scratchCartesian$3.y, scratchCartesian$3.z]; } var result = new Array(length); for (var i = 0; i < length; i += 4) { result[i] = spherical[i]; scratchSpherical.clock = spherical[i + 1]; scratchSpherical.cone = spherical[i + 2]; scratchSpherical.magnitude = spherical[i + 3]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian$3); result[i + 1] = scratchCartesian$3.x; result[i + 2] = scratchCartesian$3.y; result[i + 3] = scratchCartesian$3.z; } return result; } function convertCartographicRadiansToCartesian(cartographicRadians) { var length = cartographicRadians.length; if (length === 3) { scratchCartographic$7.longitude = cartographicRadians[0]; scratchCartographic$7.latitude = cartographicRadians[1]; scratchCartographic$7.height = cartographicRadians[2]; Ellipsoid.WGS84.cartographicToCartesian( scratchCartographic$7, scratchCartesian$3 ); return [scratchCartesian$3.x, scratchCartesian$3.y, scratchCartesian$3.z]; } var result = new Array(length); for (var i = 0; i < length; i += 4) { result[i] = cartographicRadians[i]; scratchCartographic$7.longitude = cartographicRadians[i + 1]; scratchCartographic$7.latitude = cartographicRadians[i + 2]; scratchCartographic$7.height = cartographicRadians[i + 3]; Ellipsoid.WGS84.cartographicToCartesian( scratchCartographic$7, scratchCartesian$3 ); result[i + 1] = scratchCartesian$3.x; result[i + 2] = scratchCartesian$3.y; result[i + 3] = scratchCartesian$3.z; } return result; } function convertCartographicDegreesToCartesian(cartographicDegrees) { var length = cartographicDegrees.length; if (length === 3) { scratchCartographic$7.longitude = CesiumMath.toRadians( cartographicDegrees[0] ); scratchCartographic$7.latitude = CesiumMath.toRadians(cartographicDegrees[1]); scratchCartographic$7.height = cartographicDegrees[2]; Ellipsoid.WGS84.cartographicToCartesian( scratchCartographic$7, scratchCartesian$3 ); return [scratchCartesian$3.x, scratchCartesian$3.y, scratchCartesian$3.z]; } var result = new Array(length); for (var i = 0; i < length; i += 4) { result[i] = cartographicDegrees[i]; scratchCartographic$7.longitude = CesiumMath.toRadians( cartographicDegrees[i + 1] ); scratchCartographic$7.latitude = CesiumMath.toRadians( cartographicDegrees[i + 2] ); scratchCartographic$7.height = cartographicDegrees[i + 3]; Ellipsoid.WGS84.cartographicToCartesian( scratchCartographic$7, scratchCartesian$3 ); result[i + 1] = scratchCartesian$3.x; result[i + 2] = scratchCartesian$3.y; result[i + 3] = scratchCartesian$3.z; } return result; } function unwrapCartesianInterval(czmlInterval) { var cartesian = czmlInterval.cartesian; if (defined(cartesian)) { return cartesian; } var cartesianVelocity = czmlInterval.cartesianVelocity; if (defined(cartesianVelocity)) { return cartesianVelocity; } var unitCartesian = czmlInterval.unitCartesian; if (defined(unitCartesian)) { return unitCartesian; } var unitSpherical = czmlInterval.unitSpherical; if (defined(unitSpherical)) { return convertUnitSphericalToCartesian(unitSpherical); } var spherical = czmlInterval.spherical; if (defined(spherical)) { return convertSphericalToCartesian(spherical); } var cartographicRadians = czmlInterval.cartographicRadians; if (defined(cartographicRadians)) { return convertCartographicRadiansToCartesian(cartographicRadians); } var cartographicDegrees = czmlInterval.cartographicDegrees; if (defined(cartographicDegrees)) { return convertCartographicDegreesToCartesian(cartographicDegrees); } throw new RuntimeError( JSON.stringify(czmlInterval) + " is not a valid CZML interval." ); } function normalizePackedCartesianArray(array, startingIndex) { Cartesian3.unpack(array, startingIndex, scratchCartesian$3); Cartesian3.normalize(scratchCartesian$3, scratchCartesian$3); Cartesian3.pack(scratchCartesian$3, array, startingIndex); } function unwrapUnitCartesianInterval(czmlInterval) { var cartesian = unwrapCartesianInterval(czmlInterval); if (cartesian.length === 3) { normalizePackedCartesianArray(cartesian, 0); return cartesian; } for (var i = 1; i < cartesian.length; i += 4) { normalizePackedCartesianArray(cartesian, i); } return cartesian; } function normalizePackedQuaternionArray(array, startingIndex) { Quaternion.unpack(array, startingIndex, scratchQuaternion); Quaternion.normalize(scratchQuaternion, scratchQuaternion); Quaternion.pack(scratchQuaternion, array, startingIndex); } function unwrapQuaternionInterval(czmlInterval) { var unitQuaternion = czmlInterval.unitQuaternion; if (defined(unitQuaternion)) { if (unitQuaternion.length === 4) { normalizePackedQuaternionArray(unitQuaternion, 0); return unitQuaternion; } for (var i = 1; i < unitQuaternion.length; i += 5) { normalizePackedQuaternionArray(unitQuaternion, i); } } return unitQuaternion; } function getPropertyType(czmlInterval) { // The associations in this function need to be kept in sync with the // associations in unwrapInterval. // Intentionally omitted due to conficts in CZML property names: // * Image (conflicts with Uri) // * Rotation (conflicts with Number) // // cartesianVelocity is also omitted due to incomplete support for // derivative information in CZML properties. // (Currently cartesianVelocity is hacked directly into the position processing code) if (typeof czmlInterval === "boolean") { return Boolean; } else if (typeof czmlInterval === "number") { return Number; } else if (typeof czmlInterval === "string") { return String; } else if (czmlInterval.hasOwnProperty("array")) { return Array; } else if (czmlInterval.hasOwnProperty("boolean")) { return Boolean; } else if (czmlInterval.hasOwnProperty("boundingRectangle")) { return BoundingRectangle; } else if (czmlInterval.hasOwnProperty("cartesian2")) { return Cartesian2; } else if ( czmlInterval.hasOwnProperty("cartesian") || czmlInterval.hasOwnProperty("spherical") || czmlInterval.hasOwnProperty("cartographicRadians") || czmlInterval.hasOwnProperty("cartographicDegrees") ) { return Cartesian3; } else if ( czmlInterval.hasOwnProperty("unitCartesian") || czmlInterval.hasOwnProperty("unitSpherical") ) { return UnitCartesian3; } else if ( czmlInterval.hasOwnProperty("rgba") || czmlInterval.hasOwnProperty("rgbaf") ) { return Color; } else if (czmlInterval.hasOwnProperty("arcType")) { return ArcType$1; } else if (czmlInterval.hasOwnProperty("classificationType")) { return ClassificationType$1; } else if (czmlInterval.hasOwnProperty("colorBlendMode")) { return ColorBlendMode$1; } else if (czmlInterval.hasOwnProperty("cornerType")) { return CornerType$1; } else if (czmlInterval.hasOwnProperty("heightReference")) { return HeightReference$1; } else if (czmlInterval.hasOwnProperty("horizontalOrigin")) { return HorizontalOrigin$1; } else if (czmlInterval.hasOwnProperty("date")) { return JulianDate; } else if (czmlInterval.hasOwnProperty("labelStyle")) { return LabelStyle$1; } else if (czmlInterval.hasOwnProperty("number")) { return Number; } else if (czmlInterval.hasOwnProperty("nearFarScalar")) { return NearFarScalar; } else if (czmlInterval.hasOwnProperty("distanceDisplayCondition")) { return DistanceDisplayCondition; } else if ( czmlInterval.hasOwnProperty("object") || czmlInterval.hasOwnProperty("value") ) { return Object; } else if (czmlInterval.hasOwnProperty("unitQuaternion")) { return Quaternion; } else if (czmlInterval.hasOwnProperty("shadowMode")) { return ShadowMode$1; } else if (czmlInterval.hasOwnProperty("string")) { return String; } else if (czmlInterval.hasOwnProperty("stripeOrientation")) { return StripeOrientation$1; } else if ( czmlInterval.hasOwnProperty("wsen") || czmlInterval.hasOwnProperty("wsenDegrees") ) { return Rectangle; } else if (czmlInterval.hasOwnProperty("uri")) { return URI; } else if (czmlInterval.hasOwnProperty("verticalOrigin")) { return VerticalOrigin$1; } // fallback case return Object; } function unwrapInterval(type, czmlInterval, sourceUri) { // The associations in this function need to be kept in sync with the // associations in getPropertyType switch (type) { case ArcType$1: return ArcType$1[defaultValue(czmlInterval.arcType, czmlInterval)]; case Array: return czmlInterval.array; case Boolean: return defaultValue(czmlInterval["boolean"], czmlInterval); case BoundingRectangle: return czmlInterval.boundingRectangle; case Cartesian2: return czmlInterval.cartesian2; case Cartesian3: return unwrapCartesianInterval(czmlInterval); case UnitCartesian3: return unwrapUnitCartesianInterval(czmlInterval); case Color: return unwrapColorInterval(czmlInterval); case ClassificationType$1: return ClassificationType$1[ defaultValue(czmlInterval.classificationType, czmlInterval) ]; case ColorBlendMode$1: return ColorBlendMode$1[ defaultValue(czmlInterval.colorBlendMode, czmlInterval) ]; case CornerType$1: return CornerType$1[defaultValue(czmlInterval.cornerType, czmlInterval)]; case HeightReference$1: return HeightReference$1[ defaultValue(czmlInterval.heightReference, czmlInterval) ]; case HorizontalOrigin$1: return HorizontalOrigin$1[ defaultValue(czmlInterval.horizontalOrigin, czmlInterval) ]; case Image: return unwrapUriInterval(czmlInterval, sourceUri); case JulianDate: return JulianDate.fromIso8601( defaultValue(czmlInterval.date, czmlInterval) ); case LabelStyle$1: return LabelStyle$1[defaultValue(czmlInterval.labelStyle, czmlInterval)]; case Number: return defaultValue(czmlInterval.number, czmlInterval); case NearFarScalar: return czmlInterval.nearFarScalar; case DistanceDisplayCondition: return czmlInterval.distanceDisplayCondition; case Object: return defaultValue( defaultValue(czmlInterval.object, czmlInterval.value), czmlInterval ); case Quaternion: return unwrapQuaternionInterval(czmlInterval); case Rotation: return defaultValue(czmlInterval.number, czmlInterval); case ShadowMode$1: return ShadowMode$1[ defaultValue( defaultValue(czmlInterval.shadowMode, czmlInterval.shadows), czmlInterval ) ]; case String: return defaultValue(czmlInterval.string, czmlInterval); case StripeOrientation$1: return StripeOrientation$1[ defaultValue(czmlInterval.stripeOrientation, czmlInterval) ]; case Rectangle: return unwrapRectangleInterval(czmlInterval); case URI: return unwrapUriInterval(czmlInterval, sourceUri); case VerticalOrigin$1: return VerticalOrigin$1[ defaultValue(czmlInterval.verticalOrigin, czmlInterval) ]; default: throw new RuntimeError(type); } } var interpolators = { HERMITE: HermitePolynomialApproximation, LAGRANGE: LagrangePolynomialApproximation, LINEAR: LinearApproximation, }; function updateInterpolationSettings(packetData, property) { var interpolationAlgorithm = packetData.interpolationAlgorithm; var interpolationDegree = packetData.interpolationDegree; if (defined(interpolationAlgorithm) || defined(interpolationDegree)) { property.setInterpolationOptions({ interpolationAlgorithm: interpolators[interpolationAlgorithm], interpolationDegree: interpolationDegree, }); } var forwardExtrapolationType = packetData.forwardExtrapolationType; if (defined(forwardExtrapolationType)) { property.forwardExtrapolationType = ExtrapolationType$1[forwardExtrapolationType]; } var forwardExtrapolationDuration = packetData.forwardExtrapolationDuration; if (defined(forwardExtrapolationDuration)) { property.forwardExtrapolationDuration = forwardExtrapolationDuration; } var backwardExtrapolationType = packetData.backwardExtrapolationType; if (defined(backwardExtrapolationType)) { property.backwardExtrapolationType = ExtrapolationType$1[backwardExtrapolationType]; } var backwardExtrapolationDuration = packetData.backwardExtrapolationDuration; if (defined(backwardExtrapolationDuration)) { property.backwardExtrapolationDuration = backwardExtrapolationDuration; } } var iso8601Scratch = { iso8601: undefined, }; function intervalFromString(intervalString) { if (!defined(intervalString)) { return undefined; } iso8601Scratch.iso8601 = intervalString; return TimeInterval.fromIso8601(iso8601Scratch); } function wrapPropertyInInfiniteInterval(property) { var interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; return interval; } function convertPropertyToComposite(property) { // Create the composite and add the old property, wrapped in an infinite interval. var composite = new CompositeProperty(); composite.intervals.addInterval(wrapPropertyInInfiniteInterval(property)); return composite; } function convertPositionPropertyToComposite(property) { // Create the composite and add the old property, wrapped in an infinite interval. var composite = new CompositePositionProperty(property.referenceFrame); composite.intervals.addInterval(wrapPropertyInInfiniteInterval(property)); return composite; } function processProperty( type, object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection ) { var combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval$1 ); } else { combinedInterval = constrainedInterval; } } var packedLength; var unwrappedInterval; var unwrappedIntervalLength; // CZML properties can be defined in many ways. Most ways represent a structure for // encoding a single value (number, string, cartesian, etc.) Regardless of the value type, // if it encodes a single value it will get loaded into a ConstantProperty eventually. // Alternatively, there are ways of defining a property that require specialized // client-side representation. Currently, these are ReferenceProperty, // and client-side velocity computation properties such as VelocityVectorProperty. var isValue = !defined(packetData.reference) && !defined(packetData.velocityReference); var hasInterval = defined(combinedInterval) && !combinedInterval.equals(Iso8601.MAXIMUM_INTERVAL); if (packetData.delete === true) { // If deleting this property for all time, we can simply set to undefined and return. if (!hasInterval) { object[propertyName] = undefined; return; } // Deleting depends on the type of property we have. return removePropertyData(object[propertyName], combinedInterval); } var isSampled = false; if (isValue) { unwrappedInterval = unwrapInterval(type, packetData, sourceUri); if (!defined(unwrappedInterval)) { // not a known value type, bail return; } packedLength = defaultValue(type.packedLength, 1); unwrappedIntervalLength = defaultValue(unwrappedInterval.length, 1); isSampled = !defined(packetData.array) && typeof unwrappedInterval !== "string" && unwrappedIntervalLength > packedLength && type !== Object; } // Rotation is a special case because it represents a native type (Number) // and therefore does not need to be unpacked when loaded as a constant value. var needsUnpacking = typeof type.unpack === "function" && type !== Rotation; // Any time a constant value is assigned, it completely blows away anything else. if (!isSampled && !hasInterval) { if (isValue) { object[propertyName] = new ConstantProperty( needsUnpacking ? type.unpack(unwrappedInterval, 0) : unwrappedInterval ); } else { object[propertyName] = createSpecializedProperty( type, entityCollection, packetData ); } return; } var property = object[propertyName]; var epoch; var packetEpoch = packetData.epoch; if (defined(packetEpoch)) { epoch = JulianDate.fromIso8601(packetEpoch); } // Without an interval, any sampled value is infinite, meaning it completely // replaces any non-sampled property that may exist. if (isSampled && !hasInterval) { if (!(property instanceof SampledProperty)) { object[propertyName] = property = new SampledProperty(type); } property.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, property); return; } var interval; // A constant value with an interval is normally part of a TimeIntervalCollection, // However, if the current property is not a time-interval collection, we need // to turn it into a Composite, preserving the old data with the new interval. if (!isSampled && hasInterval) { // Create a new interval for the constant value. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = needsUnpacking ? type.unpack(unwrappedInterval, 0) : unwrappedInterval; } else { combinedInterval.data = createSpecializedProperty( type, entityCollection, packetData ); } // If no property exists, simply use a new interval collection if (!defined(property)) { object[propertyName] = property = isValue ? new TimeIntervalCollectionProperty() : new CompositeProperty(); } if (isValue && property instanceof TimeIntervalCollectionProperty) { // If we created a collection, or it already was one, use it. property.intervals.addInterval(combinedInterval); } else if (property instanceof CompositeProperty) { // If the collection was already a CompositeProperty, use it. if (isValue) { combinedInterval.data = new ConstantProperty(combinedInterval.data); } property.intervals.addInterval(combinedInterval); } else { // Otherwise, create a CompositeProperty but preserve the existing data. object[propertyName] = property = convertPropertyToComposite(property); // Change the new data to a ConstantProperty and add it. if (isValue) { combinedInterval.data = new ConstantProperty(combinedInterval.data); } property.intervals.addInterval(combinedInterval); } return; } // isSampled && hasInterval if (!defined(property)) { object[propertyName] = property = new CompositeProperty(); } // Create a CompositeProperty but preserve the existing data. if (!(property instanceof CompositeProperty)) { object[propertyName] = property = convertPropertyToComposite(property); } // Check if the interval already exists in the composite. var intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if (!defined(interval) || !(interval.data instanceof SampledProperty)) { // If not, create a SampledProperty for it. interval = combinedInterval.clone(); interval.data = new SampledProperty(type); intervals.addInterval(interval); } interval.data.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, interval.data); } function removePropertyData(property, interval) { if (property instanceof SampledProperty) { property.removeSamples(interval); return; } else if (property instanceof TimeIntervalCollectionProperty) { property.intervals.removeInterval(interval); return; } else if (property instanceof CompositeProperty) { var intervals = property.intervals; for (var i = 0; i < intervals.length; ++i) { var intersection = TimeInterval.intersect( intervals.get(i), interval, scratchTimeInterval$1 ); if (!intersection.isEmpty) { // remove data from the contained properties removePropertyData(intersection.data, interval); } } // remove the intervals from the composite intervals.removeInterval(interval); return; } } function processPacketData( type, object, propertyName, packetData, interval, sourceUri, entityCollection ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; ++i) { processProperty( type, object, propertyName, packetData[i], interval, sourceUri, entityCollection ); } } else { processProperty( type, object, propertyName, packetData, interval, sourceUri, entityCollection ); } } function processPositionProperty( object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection ) { var combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval$1 ); } else { combinedInterval = constrainedInterval; } } var numberOfDerivatives = defined(packetData.cartesianVelocity) ? 1 : 0; var packedLength = Cartesian3.packedLength * (numberOfDerivatives + 1); var unwrappedInterval; var unwrappedIntervalLength; var isValue = !defined(packetData.reference); var hasInterval = defined(combinedInterval) && !combinedInterval.equals(Iso8601.MAXIMUM_INTERVAL); if (packetData.delete === true) { // If deleting this property for all time, we can simply set to undefined and return. if (!hasInterval) { object[propertyName] = undefined; return; } // Deleting depends on the type of property we have. return removePositionPropertyData(object[propertyName], combinedInterval); } var referenceFrame; var isSampled = false; if (isValue) { if (defined(packetData.referenceFrame)) { referenceFrame = ReferenceFrame$1[packetData.referenceFrame]; } referenceFrame = defaultValue(referenceFrame, ReferenceFrame$1.FIXED); unwrappedInterval = unwrapCartesianInterval(packetData); unwrappedIntervalLength = defaultValue(unwrappedInterval.length, 1); isSampled = unwrappedIntervalLength > packedLength; } // Any time a constant value is assigned, it completely blows away anything else. if (!isSampled && !hasInterval) { if (isValue) { object[propertyName] = new ConstantPositionProperty( Cartesian3.unpack(unwrappedInterval), referenceFrame ); } else { object[propertyName] = createReferenceProperty( entityCollection, packetData.reference ); } return; } var property = object[propertyName]; var epoch; var packetEpoch = packetData.epoch; if (defined(packetEpoch)) { epoch = JulianDate.fromIso8601(packetEpoch); } // Without an interval, any sampled value is infinite, meaning it completely // replaces any non-sampled property that may exist. if (isSampled && !hasInterval) { if ( !(property instanceof SampledPositionProperty) || (defined(referenceFrame) && property.referenceFrame !== referenceFrame) ) { object[propertyName] = property = new SampledPositionProperty( referenceFrame, numberOfDerivatives ); } property.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, property); return; } var interval; // A constant value with an interval is normally part of a TimeIntervalCollection, // However, if the current property is not a time-interval collection, we need // to turn it into a Composite, preserving the old data with the new interval. if (!isSampled && hasInterval) { // Create a new interval for the constant value. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = Cartesian3.unpack(unwrappedInterval); } else { combinedInterval.data = createReferenceProperty( entityCollection, packetData.reference ); } // If no property exists, simply use a new interval collection if (!defined(property)) { if (isValue) { property = new TimeIntervalCollectionPositionProperty(referenceFrame); } else { property = new CompositePositionProperty(referenceFrame); } object[propertyName] = property; } if ( isValue && property instanceof TimeIntervalCollectionPositionProperty && defined(referenceFrame) && property.referenceFrame === referenceFrame ) { // If we create a collection, or it already existed, use it. property.intervals.addInterval(combinedInterval); } else if (property instanceof CompositePositionProperty) { // If the collection was already a CompositePositionProperty, use it. if (isValue) { combinedInterval.data = new ConstantPositionProperty( combinedInterval.data, referenceFrame ); } property.intervals.addInterval(combinedInterval); } else { // Otherwise, create a CompositePositionProperty but preserve the existing data. object[propertyName] = property = convertPositionPropertyToComposite( property ); // Change the new data to a ConstantPositionProperty and add it. if (isValue) { combinedInterval.data = new ConstantPositionProperty( combinedInterval.data, referenceFrame ); } property.intervals.addInterval(combinedInterval); } return; } // isSampled && hasInterval if (!defined(property)) { object[propertyName] = property = new CompositePositionProperty( referenceFrame ); } else if (!(property instanceof CompositePositionProperty)) { // Create a CompositeProperty but preserve the existing data. object[propertyName] = property = convertPositionPropertyToComposite( property ); } // Check if the interval already exists in the composite. var intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if ( !defined(interval) || !(interval.data instanceof SampledPositionProperty) || (defined(referenceFrame) && interval.data.referenceFrame !== referenceFrame) ) { // If not, create a SampledPositionProperty for it. interval = combinedInterval.clone(); interval.data = new SampledPositionProperty( referenceFrame, numberOfDerivatives ); intervals.addInterval(interval); } interval.data.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, interval.data); } function removePositionPropertyData(property, interval) { if (property instanceof SampledPositionProperty) { property.removeSamples(interval); return; } else if (property instanceof TimeIntervalCollectionPositionProperty) { property.intervals.removeInterval(interval); return; } else if (property instanceof CompositePositionProperty) { var intervals = property.intervals; for (var i = 0; i < intervals.length; ++i) { var intersection = TimeInterval.intersect( intervals.get(i), interval, scratchTimeInterval$1 ); if (!intersection.isEmpty) { // remove data from the contained properties removePositionPropertyData(intersection.data, interval); } } // remove the intervals from the composite intervals.removeInterval(interval); return; } } function processPositionPacketData( object, propertyName, packetData, interval, sourceUri, entityCollection ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; ++i) { processPositionProperty( object, propertyName, packetData[i], interval, sourceUri, entityCollection ); } } else { processPositionProperty( object, propertyName, packetData, interval, sourceUri, entityCollection ); } } function processShapePacketData( object, propertyName, packetData, entityCollection ) { if (defined(packetData.references)) { processReferencesArrayPacketData( object, propertyName, packetData.references, packetData.interval, entityCollection, PropertyArray, CompositeProperty ); } else { if (defined(packetData.cartesian2)) { packetData.array = Cartesian2.unpackArray(packetData.cartesian2); } else if (defined(packetData.cartesian)) { // for backwards compatibility, also accept `cartesian` packetData.array = Cartesian2.unpackArray(packetData.cartesian); } if (defined(packetData.array)) { processPacketData( Array, object, propertyName, packetData, undefined, undefined, entityCollection ); } } } function processMaterialProperty( object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection ) { var combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval$1 ); } else { combinedInterval = constrainedInterval; } } var property = object[propertyName]; var existingMaterial; var existingInterval; if (defined(combinedInterval)) { if (!(property instanceof CompositeMaterialProperty)) { property = new CompositeMaterialProperty(); object[propertyName] = property; } //See if we already have data at that interval. var thisIntervals = property.intervals; existingInterval = thisIntervals.findInterval({ start: combinedInterval.start, stop: combinedInterval.stop, }); if (defined(existingInterval)) { //We have an interval, but we need to make sure the //new data is the same type of material as the old data. existingMaterial = existingInterval.data; } else { //If not, create it. existingInterval = combinedInterval.clone(); thisIntervals.addInterval(existingInterval); } } else { existingMaterial = property; } var materialData; if (defined(packetData.solidColor)) { if (!(existingMaterial instanceof ColorMaterialProperty)) { existingMaterial = new ColorMaterialProperty(); } materialData = packetData.solidColor; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection ); } else if (defined(packetData.grid)) { if (!(existingMaterial instanceof GridMaterialProperty)) { existingMaterial = new GridMaterialProperty(); } materialData = packetData.grid; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "cellAlpha", materialData.cellAlpha, undefined, sourceUri, entityCollection ); processPacketData( Cartesian2, existingMaterial, "lineCount", materialData.lineCount, undefined, sourceUri, entityCollection ); processPacketData( Cartesian2, existingMaterial, "lineThickness", materialData.lineThickness, undefined, sourceUri, entityCollection ); processPacketData( Cartesian2, existingMaterial, "lineOffset", materialData.lineOffset, undefined, sourceUri, entityCollection ); } else if (defined(packetData.image)) { if (!(existingMaterial instanceof ImageMaterialProperty)) { existingMaterial = new ImageMaterialProperty(); } materialData = packetData.image; processPacketData( Image, existingMaterial, "image", materialData.image, undefined, sourceUri, entityCollection ); processPacketData( Cartesian2, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection ); processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection ); processPacketData( Boolean, existingMaterial, "transparent", materialData.transparent, undefined, sourceUri, entityCollection ); } else if (defined(packetData.stripe)) { if (!(existingMaterial instanceof StripeMaterialProperty)) { existingMaterial = new StripeMaterialProperty(); } materialData = packetData.stripe; processPacketData( StripeOrientation$1, existingMaterial, "orientation", materialData.orientation, undefined, sourceUri, entityCollection ); processPacketData( Color, existingMaterial, "evenColor", materialData.evenColor, undefined, sourceUri, entityCollection ); processPacketData( Color, existingMaterial, "oddColor", materialData.oddColor, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "offset", materialData.offset, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection ); } else if (defined(packetData.polylineOutline)) { if (!(existingMaterial instanceof PolylineOutlineMaterialProperty)) { existingMaterial = new PolylineOutlineMaterialProperty(); } materialData = packetData.polylineOutline; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection ); processPacketData( Color, existingMaterial, "outlineColor", materialData.outlineColor, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "outlineWidth", materialData.outlineWidth, undefined, sourceUri, entityCollection ); } else if (defined(packetData.polylineGlow)) { if (!(existingMaterial instanceof PolylineGlowMaterialProperty)) { existingMaterial = new PolylineGlowMaterialProperty(); } materialData = packetData.polylineGlow; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "glowPower", materialData.glowPower, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "taperPower", materialData.taperPower, undefined, sourceUri, entityCollection ); } else if (defined(packetData.polylineArrow)) { if (!(existingMaterial instanceof PolylineArrowMaterialProperty)) { existingMaterial = new PolylineArrowMaterialProperty(); } materialData = packetData.polylineArrow; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection ); } else if (defined(packetData.polylineDash)) { if (!(existingMaterial instanceof PolylineDashMaterialProperty)) { existingMaterial = new PolylineDashMaterialProperty(); } materialData = packetData.polylineDash; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection ); processPacketData( Color, existingMaterial, "gapColor", materialData.gapColor, undefined, undefined, entityCollection ); processPacketData( Number, existingMaterial, "dashLength", materialData.dashLength, undefined, sourceUri, entityCollection ); processPacketData( Number, existingMaterial, "dashPattern", materialData.dashPattern, undefined, sourceUri, entityCollection ); } else if (defined(packetData.checkerboard)) { if (!(existingMaterial instanceof CheckerboardMaterialProperty)) { existingMaterial = new CheckerboardMaterialProperty(); } materialData = packetData.checkerboard; processPacketData( Color, existingMaterial, "evenColor", materialData.evenColor, undefined, sourceUri, entityCollection ); processPacketData( Color, existingMaterial, "oddColor", materialData.oddColor, undefined, sourceUri, entityCollection ); processPacketData( Cartesian2, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection ); } if (defined(existingInterval)) { existingInterval.data = existingMaterial; } else { object[propertyName] = existingMaterial; } } function processMaterialPacketData( object, propertyName, packetData, interval, sourceUri, entityCollection ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; ++i) { processMaterialProperty( object, propertyName, packetData[i], interval, sourceUri, entityCollection ); } } else { processMaterialProperty( object, propertyName, packetData, interval, sourceUri, entityCollection ); } } function processName(entity, packet, entityCollection, sourceUri) { var nameData = packet.name; if (defined(nameData)) { entity.name = packet.name; } } function processDescription$1(entity, packet, entityCollection, sourceUri) { var descriptionData = packet.description; if (defined(descriptionData)) { processPacketData( String, entity, "description", descriptionData, undefined, sourceUri, entityCollection ); } } function processPosition(entity, packet, entityCollection, sourceUri) { var positionData = packet.position; if (defined(positionData)) { processPositionPacketData( entity, "position", positionData, undefined, sourceUri, entityCollection ); } } function processViewFrom(entity, packet, entityCollection, sourceUri) { var viewFromData = packet.viewFrom; if (defined(viewFromData)) { processPacketData( Cartesian3, entity, "viewFrom", viewFromData, undefined, sourceUri, entityCollection ); } } function processOrientation(entity, packet, entityCollection, sourceUri) { var orientationData = packet.orientation; if (defined(orientationData)) { processPacketData( Quaternion, entity, "orientation", orientationData, undefined, sourceUri, entityCollection ); } } function processProperties(entity, packet, entityCollection, sourceUri) { var propertiesData = packet.properties; if (defined(propertiesData)) { if (!defined(entity.properties)) { entity.properties = new PropertyBag(); } // We cannot simply call processPacketData(entity, 'properties', propertyData, undefined, sourceUri, entityCollection) // because each property of "properties" may vary separately. // The properties will be accessible as entity.properties.myprop.getValue(time). for (var key in propertiesData) { if (propertiesData.hasOwnProperty(key)) { if (!entity.properties.hasProperty(key)) { entity.properties.addProperty(key); } var propertyData = propertiesData[key]; if (Array.isArray(propertyData)) { for (var i = 0, len = propertyData.length; i < len; ++i) { processProperty( getPropertyType(propertyData[i]), entity.properties, key, propertyData[i], undefined, sourceUri, entityCollection ); } } else { processProperty( getPropertyType(propertyData), entity.properties, key, propertyData, undefined, sourceUri, entityCollection ); } } } } } function processReferencesArrayPacketData( object, propertyName, references, interval, entityCollection, PropertyArrayType, CompositePropertyArrayType ) { var properties = references.map(function (reference) { return createReferenceProperty(entityCollection, reference); }); if (defined(interval)) { interval = intervalFromString(interval); var property = object[propertyName]; if (!(property instanceof CompositePropertyArrayType)) { // If the property was not already a CompositeProperty, // create a CompositeProperty but preserve the existing data. // Create the composite and add the old property, wrapped in an infinite interval. var composite = new CompositePropertyArrayType(); composite.intervals.addInterval(wrapPropertyInInfiniteInterval(property)); object[propertyName] = property = composite; } interval.data = new PropertyArrayType(properties); property.intervals.addInterval(interval); } else { object[propertyName] = new PropertyArrayType(properties); } } function processArrayPacketData( object, propertyName, packetData, entityCollection ) { var references = packetData.references; if (defined(references)) { processReferencesArrayPacketData( object, propertyName, references, packetData.interval, entityCollection, PropertyArray, CompositeProperty ); } else { processPacketData( Array, object, propertyName, packetData, undefined, undefined, entityCollection ); } } function processArray(object, propertyName, packetData, entityCollection) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, length = packetData.length; i < length; ++i) { processArrayPacketData( object, propertyName, packetData[i], entityCollection ); } } else { processArrayPacketData(object, propertyName, packetData, entityCollection); } } function processPositionArrayPacketData( object, propertyName, packetData, entityCollection ) { var references = packetData.references; if (defined(references)) { processReferencesArrayPacketData( object, propertyName, references, packetData.interval, entityCollection, PositionPropertyArray, CompositePositionProperty ); } else { if (defined(packetData.cartesian)) { packetData.array = Cartesian3.unpackArray(packetData.cartesian); } else if (defined(packetData.cartographicRadians)) { packetData.array = Cartesian3.fromRadiansArrayHeights( packetData.cartographicRadians ); } else if (defined(packetData.cartographicDegrees)) { packetData.array = Cartesian3.fromDegreesArrayHeights( packetData.cartographicDegrees ); } if (defined(packetData.array)) { processPacketData( Array, object, propertyName, packetData, undefined, undefined, entityCollection ); } } } function processPositionArray( object, propertyName, packetData, entityCollection ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, length = packetData.length; i < length; ++i) { processPositionArrayPacketData( object, propertyName, packetData[i], entityCollection ); } } else { processPositionArrayPacketData( object, propertyName, packetData, entityCollection ); } } function unpackCartesianArray(array) { return Cartesian3.unpackArray(array); } function unpackCartographicRadiansArray(array) { return Cartesian3.fromRadiansArrayHeights(array); } function unpackCartographicDegreesArray(array) { return Cartesian3.fromDegreesArrayHeights(array); } function processPositionArrayOfArraysPacketData( object, propertyName, packetData, entityCollection ) { var references = packetData.references; if (defined(references)) { var properties = references.map(function (referenceArray) { var tempObj = {}; processReferencesArrayPacketData( tempObj, "positions", referenceArray, packetData.interval, entityCollection, PositionPropertyArray, CompositePositionProperty ); return tempObj.positions; }); object[propertyName] = new PositionPropertyArray(properties); } else { if (defined(packetData.cartesian)) { packetData.array = packetData.cartesian.map(unpackCartesianArray); } else if (defined(packetData.cartographicRadians)) { packetData.array = packetData.cartographicRadians.map( unpackCartographicRadiansArray ); } else if (defined(packetData.cartographicDegrees)) { packetData.array = packetData.cartographicDegrees.map( unpackCartographicDegreesArray ); } if (defined(packetData.array)) { processPacketData( Array, object, propertyName, packetData, undefined, undefined, entityCollection ); } } } function processPositionArrayOfArrays( object, propertyName, packetData, entityCollection ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, length = packetData.length; i < length; ++i) { processPositionArrayOfArraysPacketData( object, propertyName, packetData[i], entityCollection ); } } else { processPositionArrayOfArraysPacketData( object, propertyName, packetData, entityCollection ); } } function processShape(object, propertyName, packetData, entityCollection) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (var i = 0, length = packetData.length; i < length; i++) { processShapePacketData( object, propertyName, packetData[i], entityCollection ); } } else { processShapePacketData(object, propertyName, packetData, entityCollection); } } function processAvailability(entity, packet, entityCollection, sourceUri) { var packetData = packet.availability; if (!defined(packetData)) { return; } var intervals; if (Array.isArray(packetData)) { for (var i = 0, len = packetData.length; i < len; ++i) { if (!defined(intervals)) { intervals = new TimeIntervalCollection(); } intervals.addInterval(intervalFromString(packetData[i])); } } else { intervals = new TimeIntervalCollection(); intervals.addInterval(intervalFromString(packetData)); } entity.availability = intervals; } function processAlignedAxis( billboard, packetData, interval, sourceUri, entityCollection ) { if (!defined(packetData)) { return; } processPacketData( UnitCartesian3, billboard, "alignedAxis", packetData, interval, sourceUri, entityCollection ); } function processBillboard(entity, packet, entityCollection, sourceUri) { var billboardData = packet.billboard; if (!defined(billboardData)) { return; } var interval = intervalFromString(billboardData.interval); var billboard = entity.billboard; if (!defined(billboard)) { entity.billboard = billboard = new BillboardGraphics(); } processPacketData( Boolean, billboard, "show", billboardData.show, interval, sourceUri, entityCollection ); processPacketData( Image, billboard, "image", billboardData.image, interval, sourceUri, entityCollection ); processPacketData( Number, billboard, "scale", billboardData.scale, interval, sourceUri, entityCollection ); processPacketData( Cartesian2, billboard, "pixelOffset", billboardData.pixelOffset, interval, sourceUri, entityCollection ); processPacketData( Cartesian3, billboard, "eyeOffset", billboardData.eyeOffset, interval, sourceUri, entityCollection ); processPacketData( HorizontalOrigin$1, billboard, "horizontalOrigin", billboardData.horizontalOrigin, interval, sourceUri, entityCollection ); processPacketData( VerticalOrigin$1, billboard, "verticalOrigin", billboardData.verticalOrigin, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, billboard, "heightReference", billboardData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Color, billboard, "color", billboardData.color, interval, sourceUri, entityCollection ); processPacketData( Rotation, billboard, "rotation", billboardData.rotation, interval, sourceUri, entityCollection ); processAlignedAxis( billboard, billboardData.alignedAxis, interval, sourceUri, entityCollection ); processPacketData( Boolean, billboard, "sizeInMeters", billboardData.sizeInMeters, interval, sourceUri, entityCollection ); processPacketData( Number, billboard, "width", billboardData.width, interval, sourceUri, entityCollection ); processPacketData( Number, billboard, "height", billboardData.height, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, billboard, "scaleByDistance", billboardData.scaleByDistance, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, billboard, "translucencyByDistance", billboardData.translucencyByDistance, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, billboard, "pixelOffsetScaleByDistance", billboardData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection ); processPacketData( BoundingRectangle, billboard, "imageSubRegion", billboardData.imageSubRegion, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, billboard, "distanceDisplayCondition", billboardData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( Number, billboard, "disableDepthTestDistance", billboardData.disableDepthTestDistance, interval, sourceUri, entityCollection ); } function processBox(entity, packet, entityCollection, sourceUri) { var boxData = packet.box; if (!defined(boxData)) { return; } var interval = intervalFromString(boxData.interval); var box = entity.box; if (!defined(box)) { entity.box = box = new BoxGraphics(); } processPacketData( Boolean, box, "show", boxData.show, interval, sourceUri, entityCollection ); processPacketData( Cartesian3, box, "dimensions", boxData.dimensions, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, box, "heightReference", boxData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Boolean, box, "fill", boxData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( box, "material", boxData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, box, "outline", boxData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, box, "outlineColor", boxData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, box, "outlineWidth", boxData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, box, "shadows", boxData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, box, "distanceDisplayCondition", boxData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processCorridor(entity, packet, entityCollection, sourceUri) { var corridorData = packet.corridor; if (!defined(corridorData)) { return; } var interval = intervalFromString(corridorData.interval); var corridor = entity.corridor; if (!defined(corridor)) { entity.corridor = corridor = new CorridorGraphics(); } processPacketData( Boolean, corridor, "show", corridorData.show, interval, sourceUri, entityCollection ); processPositionArray( corridor, "positions", corridorData.positions, entityCollection ); processPacketData( Number, corridor, "width", corridorData.width, interval, sourceUri, entityCollection ); processPacketData( Number, corridor, "height", corridorData.height, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, corridor, "heightReference", corridorData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Number, corridor, "extrudedHeight", corridorData.extrudedHeight, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, corridor, "extrudedHeightReference", corridorData.extrudedHeightReference, interval, sourceUri, entityCollection ); processPacketData( CornerType$1, corridor, "cornerType", corridorData.cornerType, interval, sourceUri, entityCollection ); processPacketData( Number, corridor, "granularity", corridorData.granularity, interval, sourceUri, entityCollection ); processPacketData( Boolean, corridor, "fill", corridorData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( corridor, "material", corridorData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, corridor, "outline", corridorData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, corridor, "outlineColor", corridorData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, corridor, "outlineWidth", corridorData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, corridor, "shadows", corridorData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, corridor, "distanceDisplayCondition", corridorData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( ClassificationType$1, corridor, "classificationType", corridorData.classificationType, interval, sourceUri, entityCollection ); processPacketData( Number, corridor, "zIndex", corridorData.zIndex, interval, sourceUri, entityCollection ); } function processCylinder(entity, packet, entityCollection, sourceUri) { var cylinderData = packet.cylinder; if (!defined(cylinderData)) { return; } var interval = intervalFromString(cylinderData.interval); var cylinder = entity.cylinder; if (!defined(cylinder)) { entity.cylinder = cylinder = new CylinderGraphics(); } processPacketData( Boolean, cylinder, "show", cylinderData.show, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "length", cylinderData.length, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "topRadius", cylinderData.topRadius, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "bottomRadius", cylinderData.bottomRadius, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, cylinder, "heightReference", cylinderData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Boolean, cylinder, "fill", cylinderData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( cylinder, "material", cylinderData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, cylinder, "outline", cylinderData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, cylinder, "outlineColor", cylinderData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "outlineWidth", cylinderData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "numberOfVerticalLines", cylinderData.numberOfVerticalLines, interval, sourceUri, entityCollection ); processPacketData( Number, cylinder, "slices", cylinderData.slices, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, cylinder, "shadows", cylinderData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, cylinder, "distanceDisplayCondition", cylinderData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processDocument$1(packet, dataSource) { var version = packet.version; if (defined(version)) { if (typeof version === "string") { var tokens = version.split("."); if (tokens.length === 2) { if (tokens[0] !== "1") { throw new RuntimeError("Cesium only supports CZML version 1."); } dataSource._version = version; } } } if (!defined(dataSource._version)) { throw new RuntimeError( "CZML version information invalid. It is expected to be a property on the document object in the . version format." ); } var documentPacket = dataSource._documentPacket; if (defined(packet.name)) { documentPacket.name = packet.name; } var clockPacket = packet.clock; if (defined(clockPacket)) { var clock = documentPacket.clock; if (!defined(clock)) { documentPacket.clock = { interval: clockPacket.interval, currentTime: clockPacket.currentTime, range: clockPacket.range, step: clockPacket.step, multiplier: clockPacket.multiplier, }; } else { clock.interval = defaultValue(clockPacket.interval, clock.interval); clock.currentTime = defaultValue( clockPacket.currentTime, clock.currentTime ); clock.range = defaultValue(clockPacket.range, clock.range); clock.step = defaultValue(clockPacket.step, clock.step); clock.multiplier = defaultValue(clockPacket.multiplier, clock.multiplier); } } } function processEllipse(entity, packet, entityCollection, sourceUri) { var ellipseData = packet.ellipse; if (!defined(ellipseData)) { return; } var interval = intervalFromString(ellipseData.interval); var ellipse = entity.ellipse; if (!defined(ellipse)) { entity.ellipse = ellipse = new EllipseGraphics(); } processPacketData( Boolean, ellipse, "show", ellipseData.show, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "semiMajorAxis", ellipseData.semiMajorAxis, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "semiMinorAxis", ellipseData.semiMinorAxis, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "height", ellipseData.height, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, ellipse, "heightReference", ellipseData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "extrudedHeight", ellipseData.extrudedHeight, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, ellipse, "extrudedHeightReference", ellipseData.extrudedHeightReference, interval, sourceUri, entityCollection ); processPacketData( Rotation, ellipse, "rotation", ellipseData.rotation, interval, sourceUri, entityCollection ); processPacketData( Rotation, ellipse, "stRotation", ellipseData.stRotation, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "granularity", ellipseData.granularity, interval, sourceUri, entityCollection ); processPacketData( Boolean, ellipse, "fill", ellipseData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( ellipse, "material", ellipseData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, ellipse, "outline", ellipseData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, ellipse, "outlineColor", ellipseData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "outlineWidth", ellipseData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "numberOfVerticalLines", ellipseData.numberOfVerticalLines, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, ellipse, "shadows", ellipseData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, ellipse, "distanceDisplayCondition", ellipseData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( ClassificationType$1, ellipse, "classificationType", ellipseData.classificationType, interval, sourceUri, entityCollection ); processPacketData( Number, ellipse, "zIndex", ellipseData.zIndex, interval, sourceUri, entityCollection ); } function processEllipsoid(entity, packet, entityCollection, sourceUri) { var ellipsoidData = packet.ellipsoid; if (!defined(ellipsoidData)) { return; } var interval = intervalFromString(ellipsoidData.interval); var ellipsoid = entity.ellipsoid; if (!defined(ellipsoid)) { entity.ellipsoid = ellipsoid = new EllipsoidGraphics(); } processPacketData( Boolean, ellipsoid, "show", ellipsoidData.show, interval, sourceUri, entityCollection ); processPacketData( Cartesian3, ellipsoid, "radii", ellipsoidData.radii, interval, sourceUri, entityCollection ); processPacketData( Cartesian3, ellipsoid, "innerRadii", ellipsoidData.innerRadii, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "minimumClock", ellipsoidData.minimumClock, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "maximumClock", ellipsoidData.maximumClock, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "minimumCone", ellipsoidData.minimumCone, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "maximumCone", ellipsoidData.maximumCone, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, ellipsoid, "heightReference", ellipsoidData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Boolean, ellipsoid, "fill", ellipsoidData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( ellipsoid, "material", ellipsoidData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, ellipsoid, "outline", ellipsoidData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, ellipsoid, "outlineColor", ellipsoidData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "outlineWidth", ellipsoidData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "stackPartitions", ellipsoidData.stackPartitions, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "slicePartitions", ellipsoidData.slicePartitions, interval, sourceUri, entityCollection ); processPacketData( Number, ellipsoid, "subdivisions", ellipsoidData.subdivisions, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, ellipsoid, "shadows", ellipsoidData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, ellipsoid, "distanceDisplayCondition", ellipsoidData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processLabel(entity, packet, entityCollection, sourceUri) { var labelData = packet.label; if (!defined(labelData)) { return; } var interval = intervalFromString(labelData.interval); var label = entity.label; if (!defined(label)) { entity.label = label = new LabelGraphics(); } processPacketData( Boolean, label, "show", labelData.show, interval, sourceUri, entityCollection ); processPacketData( String, label, "text", labelData.text, interval, sourceUri, entityCollection ); processPacketData( String, label, "font", labelData.font, interval, sourceUri, entityCollection ); processPacketData( LabelStyle$1, label, "style", labelData.style, interval, sourceUri, entityCollection ); processPacketData( Number, label, "scale", labelData.scale, interval, sourceUri, entityCollection ); processPacketData( Boolean, label, "showBackground", labelData.showBackground, interval, sourceUri, entityCollection ); processPacketData( Color, label, "backgroundColor", labelData.backgroundColor, interval, sourceUri, entityCollection ); processPacketData( Cartesian2, label, "backgroundPadding", labelData.backgroundPadding, interval, sourceUri, entityCollection ); processPacketData( Cartesian2, label, "pixelOffset", labelData.pixelOffset, interval, sourceUri, entityCollection ); processPacketData( Cartesian3, label, "eyeOffset", labelData.eyeOffset, interval, sourceUri, entityCollection ); processPacketData( HorizontalOrigin$1, label, "horizontalOrigin", labelData.horizontalOrigin, interval, sourceUri, entityCollection ); processPacketData( VerticalOrigin$1, label, "verticalOrigin", labelData.verticalOrigin, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, label, "heightReference", labelData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Color, label, "fillColor", labelData.fillColor, interval, sourceUri, entityCollection ); processPacketData( Color, label, "outlineColor", labelData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, label, "outlineWidth", labelData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, label, "translucencyByDistance", labelData.translucencyByDistance, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, label, "pixelOffsetScaleByDistance", labelData.pixelOffsetScaleByDistance, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, label, "scaleByDistance", labelData.scaleByDistance, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, label, "distanceDisplayCondition", labelData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( Number, label, "disableDepthTestDistance", labelData.disableDepthTestDistance, interval, sourceUri, entityCollection ); } function processModel(entity, packet, entityCollection, sourceUri) { var modelData = packet.model; if (!defined(modelData)) { return; } var interval = intervalFromString(modelData.interval); var model = entity.model; if (!defined(model)) { entity.model = model = new ModelGraphics(); } processPacketData( Boolean, model, "show", modelData.show, interval, sourceUri, entityCollection ); processPacketData( URI, model, "uri", modelData.gltf, interval, sourceUri, entityCollection ); processPacketData( Number, model, "scale", modelData.scale, interval, sourceUri, entityCollection ); processPacketData( Number, model, "minimumPixelSize", modelData.minimumPixelSize, interval, sourceUri, entityCollection ); processPacketData( Number, model, "maximumScale", modelData.maximumScale, interval, sourceUri, entityCollection ); processPacketData( Boolean, model, "incrementallyLoadTextures", modelData.incrementallyLoadTextures, interval, sourceUri, entityCollection ); processPacketData( Boolean, model, "runAnimations", modelData.runAnimations, interval, sourceUri, entityCollection ); processPacketData( Boolean, model, "clampAnimations", modelData.clampAnimations, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, model, "shadows", modelData.shadows, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, model, "heightReference", modelData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Color, model, "silhouetteColor", modelData.silhouetteColor, interval, sourceUri, entityCollection ); processPacketData( Number, model, "silhouetteSize", modelData.silhouetteSize, interval, sourceUri, entityCollection ); processPacketData( Color, model, "color", modelData.color, interval, sourceUri, entityCollection ); processPacketData( ColorBlendMode$1, model, "colorBlendMode", modelData.colorBlendMode, interval, sourceUri, entityCollection ); processPacketData( Number, model, "colorBlendAmount", modelData.colorBlendAmount, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, model, "distanceDisplayCondition", modelData.distanceDisplayCondition, interval, sourceUri, entityCollection ); var i, len; var nodeTransformationsData = modelData.nodeTransformations; if (defined(nodeTransformationsData)) { if (Array.isArray(nodeTransformationsData)) { for (i = 0, len = nodeTransformationsData.length; i < len; ++i) { processNodeTransformations( model, nodeTransformationsData[i], interval, sourceUri, entityCollection ); } } else { processNodeTransformations( model, nodeTransformationsData, interval, sourceUri, entityCollection ); } } var articulationsData = modelData.articulations; if (defined(articulationsData)) { if (Array.isArray(articulationsData)) { for (i = 0, len = articulationsData.length; i < len; ++i) { processArticulations( model, articulationsData[i], interval, sourceUri, entityCollection ); } } else { processArticulations( model, articulationsData, interval, sourceUri, entityCollection ); } } } function processNodeTransformations( model, nodeTransformationsData, constrainedInterval, sourceUri, entityCollection ) { var combinedInterval = intervalFromString(nodeTransformationsData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval$1 ); } else { combinedInterval = constrainedInterval; } } var nodeTransformations = model.nodeTransformations; var nodeNames = Object.keys(nodeTransformationsData); for (var i = 0, len = nodeNames.length; i < len; ++i) { var nodeName = nodeNames[i]; if (nodeName === "interval") { continue; } var nodeTransformationData = nodeTransformationsData[nodeName]; if (!defined(nodeTransformationData)) { continue; } if (!defined(nodeTransformations)) { model.nodeTransformations = nodeTransformations = new PropertyBag(); } if (!nodeTransformations.hasProperty(nodeName)) { nodeTransformations.addProperty(nodeName); } var nodeTransformation = nodeTransformations[nodeName]; if (!defined(nodeTransformation)) { nodeTransformations[ nodeName ] = nodeTransformation = new NodeTransformationProperty(); } processPacketData( Cartesian3, nodeTransformation, "translation", nodeTransformationData.translation, combinedInterval, sourceUri, entityCollection ); processPacketData( Quaternion, nodeTransformation, "rotation", nodeTransformationData.rotation, combinedInterval, sourceUri, entityCollection ); processPacketData( Cartesian3, nodeTransformation, "scale", nodeTransformationData.scale, combinedInterval, sourceUri, entityCollection ); } } function processArticulations( model, articulationsData, constrainedInterval, sourceUri, entityCollection ) { var combinedInterval = intervalFromString(articulationsData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval$1 ); } else { combinedInterval = constrainedInterval; } } var articulations = model.articulations; var keys = Object.keys(articulationsData); for (var i = 0, len = keys.length; i < len; ++i) { var key = keys[i]; if (key === "interval") { continue; } var articulationStageData = articulationsData[key]; if (!defined(articulationStageData)) { continue; } if (!defined(articulations)) { model.articulations = articulations = new PropertyBag(); } if (!articulations.hasProperty(key)) { articulations.addProperty(key); } processPacketData( Number, articulations, key, articulationStageData, combinedInterval, sourceUri, entityCollection ); } } function processPath(entity, packet, entityCollection, sourceUri) { var pathData = packet.path; if (!defined(pathData)) { return; } var interval = intervalFromString(pathData.interval); var path = entity.path; if (!defined(path)) { entity.path = path = new PathGraphics(); } processPacketData( Boolean, path, "show", pathData.show, interval, sourceUri, entityCollection ); processPacketData( Number, path, "leadTime", pathData.leadTime, interval, sourceUri, entityCollection ); processPacketData( Number, path, "trailTime", pathData.trailTime, interval, sourceUri, entityCollection ); processPacketData( Number, path, "width", pathData.width, interval, sourceUri, entityCollection ); processPacketData( Number, path, "resolution", pathData.resolution, interval, sourceUri, entityCollection ); processMaterialPacketData( path, "material", pathData.material, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, path, "distanceDisplayCondition", pathData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processPoint$2(entity, packet, entityCollection, sourceUri) { var pointData = packet.point; if (!defined(pointData)) { return; } var interval = intervalFromString(pointData.interval); var point = entity.point; if (!defined(point)) { entity.point = point = new PointGraphics(); } processPacketData( Boolean, point, "show", pointData.show, interval, sourceUri, entityCollection ); processPacketData( Number, point, "pixelSize", pointData.pixelSize, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, point, "heightReference", pointData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Color, point, "color", pointData.color, interval, sourceUri, entityCollection ); processPacketData( Color, point, "outlineColor", pointData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, point, "outlineWidth", pointData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, point, "scaleByDistance", pointData.scaleByDistance, interval, sourceUri, entityCollection ); processPacketData( NearFarScalar, point, "translucencyByDistance", pointData.translucencyByDistance, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, point, "distanceDisplayCondition", pointData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( Number, point, "disableDepthTestDistance", pointData.disableDepthTestDistance, interval, sourceUri, entityCollection ); } function PolygonHierarchyProperty(polygon) { this.polygon = polygon; this._definitionChanged = new Event(); } Object.defineProperties(PolygonHierarchyProperty.prototype, { isConstant: { get: function () { var positions = this.polygon._positions; var holes = this.polygon._holes; return ( (!defined(positions) || positions.isConstant) && (!defined(holes) || holes.isConstant) ); }, }, definitionChanged: { get: function () { return this._definitionChanged; }, }, }); PolygonHierarchyProperty.prototype.getValue = function (time, result) { var positions; if (defined(this.polygon._positions)) { positions = this.polygon._positions.getValue(time); } var holes; if (defined(this.polygon._holes)) { holes = this.polygon._holes.getValue(time); if (defined(holes)) { holes = holes.map(function (holePositions) { return new PolygonHierarchy(holePositions); }); } } if (!defined(result)) { return new PolygonHierarchy(positions, holes); } result.positions = positions; result.holes = holes; return result; }; PolygonHierarchyProperty.prototype.equals = function (other) { return ( this === other || (other instanceof PolygonHierarchyProperty && Property.equals(this.polygon._positions, other.polygon._positions) && Property.equals(this.polygon._holes, other.polygon._holes)) ); }; function processPolygon$2(entity, packet, entityCollection, sourceUri) { var polygonData = packet.polygon; if (!defined(polygonData)) { return; } var interval = intervalFromString(polygonData.interval); var polygon = entity.polygon; if (!defined(polygon)) { entity.polygon = polygon = new PolygonGraphics(); } processPacketData( Boolean, polygon, "show", polygonData.show, interval, sourceUri, entityCollection ); // adapt 'position' property producing Cartesian[] // and 'holes' property producing Cartesian[][] // to a single property producing PolygonHierarchy processPositionArray( polygon, "_positions", polygonData.positions, entityCollection ); processPositionArrayOfArrays( polygon, "_holes", polygonData.holes, entityCollection ); if (defined(polygon._positions) || defined(polygon._holes)) { polygon.hierarchy = new PolygonHierarchyProperty(polygon); } processPacketData( Number, polygon, "height", polygonData.height, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, polygon, "heightReference", polygonData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Number, polygon, "extrudedHeight", polygonData.extrudedHeight, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, polygon, "extrudedHeightReference", polygonData.extrudedHeightReference, interval, sourceUri, entityCollection ); processPacketData( Rotation, polygon, "stRotation", polygonData.stRotation, interval, sourceUri, entityCollection ); processPacketData( Number, polygon, "granularity", polygonData.granularity, interval, sourceUri, entityCollection ); processPacketData( Boolean, polygon, "fill", polygonData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( polygon, "material", polygonData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, polygon, "outline", polygonData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, polygon, "outlineColor", polygonData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, polygon, "outlineWidth", polygonData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( Boolean, polygon, "perPositionHeight", polygonData.perPositionHeight, interval, sourceUri, entityCollection ); processPacketData( Boolean, polygon, "closeTop", polygonData.closeTop, interval, sourceUri, entityCollection ); processPacketData( Boolean, polygon, "closeBottom", polygonData.closeBottom, interval, sourceUri, entityCollection ); processPacketData( ArcType$1, polygon, "arcType", polygonData.arcType, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, polygon, "shadows", polygonData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, polygon, "distanceDisplayCondition", polygonData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( ClassificationType$1, polygon, "classificationType", polygonData.classificationType, interval, sourceUri, entityCollection ); processPacketData( Number, polygon, "zIndex", polygonData.zIndex, interval, sourceUri, entityCollection ); } function adaptFollowSurfaceToArcType(followSurface) { return followSurface ? ArcType$1.GEODESIC : ArcType$1.NONE; } function processPolyline(entity, packet, entityCollection, sourceUri) { var polylineData = packet.polyline; if (!defined(polylineData)) { return; } var interval = intervalFromString(polylineData.interval); var polyline = entity.polyline; if (!defined(polyline)) { entity.polyline = polyline = new PolylineGraphics(); } processPacketData( Boolean, polyline, "show", polylineData.show, interval, sourceUri, entityCollection ); processPositionArray( polyline, "positions", polylineData.positions, entityCollection ); processPacketData( Number, polyline, "width", polylineData.width, interval, sourceUri, entityCollection ); processPacketData( Number, polyline, "granularity", polylineData.granularity, interval, sourceUri, entityCollection ); processMaterialPacketData( polyline, "material", polylineData.material, interval, sourceUri, entityCollection ); processMaterialPacketData( polyline, "depthFailMaterial", polylineData.depthFailMaterial, interval, sourceUri, entityCollection ); processPacketData( ArcType$1, polyline, "arcType", polylineData.arcType, interval, sourceUri, entityCollection ); processPacketData( Boolean, polyline, "clampToGround", polylineData.clampToGround, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, polyline, "shadows", polylineData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, polyline, "distanceDisplayCondition", polylineData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( ClassificationType$1, polyline, "classificationType", polylineData.classificationType, interval, sourceUri, entityCollection ); processPacketData( Number, polyline, "zIndex", polylineData.zIndex, interval, sourceUri, entityCollection ); // for backwards compatibility, adapt CZML followSurface to arcType. if (defined(polylineData.followSurface) && !defined(polylineData.arcType)) { var tempObj = {}; processPacketData( Boolean, tempObj, "followSurface", polylineData.followSurface, interval, sourceUri, entityCollection ); polyline.arcType = createAdapterProperty( tempObj.followSurface, adaptFollowSurfaceToArcType ); } } function processPolylineVolume(entity, packet, entityCollection, sourceUri) { var polylineVolumeData = packet.polylineVolume; if (!defined(polylineVolumeData)) { return; } var interval = intervalFromString(polylineVolumeData.interval); var polylineVolume = entity.polylineVolume; if (!defined(polylineVolume)) { entity.polylineVolume = polylineVolume = new PolylineVolumeGraphics(); } processPositionArray( polylineVolume, "positions", polylineVolumeData.positions, entityCollection ); processShape( polylineVolume, "shape", polylineVolumeData.shape, entityCollection ); processPacketData( Boolean, polylineVolume, "show", polylineVolumeData.show, interval, sourceUri, entityCollection ); processPacketData( CornerType$1, polylineVolume, "cornerType", polylineVolumeData.cornerType, interval, sourceUri, entityCollection ); processPacketData( Boolean, polylineVolume, "fill", polylineVolumeData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( polylineVolume, "material", polylineVolumeData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, polylineVolume, "outline", polylineVolumeData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, polylineVolume, "outlineColor", polylineVolumeData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, polylineVolume, "outlineWidth", polylineVolumeData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( Number, polylineVolume, "granularity", polylineVolumeData.granularity, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, polylineVolume, "shadows", polylineVolumeData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, polylineVolume, "distanceDisplayCondition", polylineVolumeData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processRectangle(entity, packet, entityCollection, sourceUri) { var rectangleData = packet.rectangle; if (!defined(rectangleData)) { return; } var interval = intervalFromString(rectangleData.interval); var rectangle = entity.rectangle; if (!defined(rectangle)) { entity.rectangle = rectangle = new RectangleGraphics(); } processPacketData( Boolean, rectangle, "show", rectangleData.show, interval, sourceUri, entityCollection ); processPacketData( Rectangle, rectangle, "coordinates", rectangleData.coordinates, interval, sourceUri, entityCollection ); processPacketData( Number, rectangle, "height", rectangleData.height, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, rectangle, "heightReference", rectangleData.heightReference, interval, sourceUri, entityCollection ); processPacketData( Number, rectangle, "extrudedHeight", rectangleData.extrudedHeight, interval, sourceUri, entityCollection ); processPacketData( HeightReference$1, rectangle, "extrudedHeightReference", rectangleData.extrudedHeightReference, interval, sourceUri, entityCollection ); processPacketData( Rotation, rectangle, "rotation", rectangleData.rotation, interval, sourceUri, entityCollection ); processPacketData( Rotation, rectangle, "stRotation", rectangleData.stRotation, interval, sourceUri, entityCollection ); processPacketData( Number, rectangle, "granularity", rectangleData.granularity, interval, sourceUri, entityCollection ); processPacketData( Boolean, rectangle, "fill", rectangleData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( rectangle, "material", rectangleData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, rectangle, "outline", rectangleData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, rectangle, "outlineColor", rectangleData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, rectangle, "outlineWidth", rectangleData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, rectangle, "shadows", rectangleData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, rectangle, "distanceDisplayCondition", rectangleData.distanceDisplayCondition, interval, sourceUri, entityCollection ); processPacketData( ClassificationType$1, rectangle, "classificationType", rectangleData.classificationType, interval, sourceUri, entityCollection ); processPacketData( Number, rectangle, "zIndex", rectangleData.zIndex, interval, sourceUri, entityCollection ); } function processTileset(entity, packet, entityCollection, sourceUri) { var tilesetData = packet.tileset; if (!defined(tilesetData)) { return; } var interval = intervalFromString(tilesetData.interval); var tileset = entity.tileset; if (!defined(tileset)) { entity.tileset = tileset = new Cesium3DTilesetGraphics(); } processPacketData( Boolean, tileset, "show", tilesetData.show, interval, sourceUri, entityCollection ); processPacketData( URI, tileset, "uri", tilesetData.uri, interval, sourceUri, entityCollection ); processPacketData( Number, tileset, "maximumScreenSpaceError", tilesetData.maximumScreenSpaceError, interval, sourceUri, entityCollection ); } function processWall(entity, packet, entityCollection, sourceUri) { var wallData = packet.wall; if (!defined(wallData)) { return; } var interval = intervalFromString(wallData.interval); var wall = entity.wall; if (!defined(wall)) { entity.wall = wall = new WallGraphics(); } processPacketData( Boolean, wall, "show", wallData.show, interval, sourceUri, entityCollection ); processPositionArray(wall, "positions", wallData.positions, entityCollection); processArray( wall, "minimumHeights", wallData.minimumHeights, entityCollection ); processArray( wall, "maximumHeights", wallData.maximumHeights, entityCollection ); processPacketData( Number, wall, "granularity", wallData.granularity, interval, sourceUri, entityCollection ); processPacketData( Boolean, wall, "fill", wallData.fill, interval, sourceUri, entityCollection ); processMaterialPacketData( wall, "material", wallData.material, interval, sourceUri, entityCollection ); processPacketData( Boolean, wall, "outline", wallData.outline, interval, sourceUri, entityCollection ); processPacketData( Color, wall, "outlineColor", wallData.outlineColor, interval, sourceUri, entityCollection ); processPacketData( Number, wall, "outlineWidth", wallData.outlineWidth, interval, sourceUri, entityCollection ); processPacketData( ShadowMode$1, wall, "shadows", wallData.shadows, interval, sourceUri, entityCollection ); processPacketData( DistanceDisplayCondition, wall, "distanceDisplayCondition", wallData.distanceDisplayCondition, interval, sourceUri, entityCollection ); } function processCzmlPacket( packet, entityCollection, updaterFunctions, sourceUri, dataSource ) { var objectId = packet.id; if (!defined(objectId)) { objectId = createGuid(); } currentId = objectId; if (!defined(dataSource._version) && objectId !== "document") { throw new RuntimeError( "The first CZML packet is required to be the document object." ); } if (packet["delete"] === true) { entityCollection.removeById(objectId); } else if (objectId === "document") { processDocument$1(packet, dataSource); } else { var entity = entityCollection.getOrCreateEntity(objectId); var parentId = packet.parent; if (defined(parentId)) { entity.parent = entityCollection.getOrCreateEntity(parentId); } for (var i = updaterFunctions.length - 1; i > -1; i--) { updaterFunctions[i](entity, packet, entityCollection, sourceUri); } } currentId = undefined; } function updateClock(dataSource) { var clock; var clockPacket = dataSource._documentPacket.clock; if (!defined(clockPacket)) { if (!defined(dataSource._clock)) { var availability = dataSource._entityCollection.computeAvailability(); if (!availability.start.equals(Iso8601.MINIMUM_VALUE)) { var startTime = availability.start; var stopTime = availability.stop; var totalSeconds = JulianDate.secondsDifference(stopTime, startTime); var multiplier = Math.round(totalSeconds / 120.0); clock = new DataSourceClock(); clock.startTime = JulianDate.clone(startTime); clock.stopTime = JulianDate.clone(stopTime); clock.clockRange = ClockRange$1.LOOP_STOP; clock.multiplier = multiplier; clock.currentTime = JulianDate.clone(startTime); clock.clockStep = ClockStep$1.SYSTEM_CLOCK_MULTIPLIER; dataSource._clock = clock; return true; } } return false; } if (defined(dataSource._clock)) { clock = dataSource._clock.clone(); } else { clock = new DataSourceClock(); clock.startTime = Iso8601.MINIMUM_VALUE.clone(); clock.stopTime = Iso8601.MAXIMUM_VALUE.clone(); clock.currentTime = Iso8601.MINIMUM_VALUE.clone(); clock.clockRange = ClockRange$1.LOOP_STOP; clock.clockStep = ClockStep$1.SYSTEM_CLOCK_MULTIPLIER; clock.multiplier = 1.0; } var interval = intervalFromString(clockPacket.interval); if (defined(interval)) { clock.startTime = interval.start; clock.stopTime = interval.stop; } if (defined(clockPacket.currentTime)) { clock.currentTime = JulianDate.fromIso8601(clockPacket.currentTime); } if (defined(clockPacket.range)) { clock.clockRange = defaultValue( ClockRange$1[clockPacket.range], ClockRange$1.LOOP_STOP ); } if (defined(clockPacket.step)) { clock.clockStep = defaultValue( ClockStep$1[clockPacket.step], ClockStep$1.SYSTEM_CLOCK_MULTIPLIER ); } if (defined(clockPacket.multiplier)) { clock.multiplier = clockPacket.multiplier; } if (!clock.equals(dataSource._clock)) { dataSource._clock = clock.clone(dataSource._clock); return true; } return false; } function load$2(dataSource, czml, options, clear) { //>>includeStart('debug', pragmas.debug); if (!defined(czml)) { throw new DeveloperError("czml is required."); } //>>includeEnd('debug'); options = defaultValue(options, defaultValue.EMPTY_OBJECT); var promise = czml; var sourceUri = options.sourceUri; // User specified credit var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } dataSource._credit = credit; // If the czml is a URL if (typeof czml === "string" || czml instanceof Resource) { czml = Resource.createIfNeeded(czml); promise = czml.fetchJson(); sourceUri = defaultValue(sourceUri, czml.clone()); // Add resource credits to our list of credits to display var resourceCredits = dataSource._resourceCredits; var credits = czml.credits; if (defined(credits)) { var length = credits.length; for (var i = 0; i < length; i++) { resourceCredits.push(credits[i]); } } } sourceUri = Resource.createIfNeeded(sourceUri); DataSource.setLoading(dataSource, true); return when(promise, function (czml) { return loadCzml(dataSource, czml, sourceUri, clear); }).otherwise(function (error) { DataSource.setLoading(dataSource, false); dataSource._error.raiseEvent(dataSource, error); console.log(error); return when.reject(error); }); } function loadCzml(dataSource, czml, sourceUri, clear) { DataSource.setLoading(dataSource, true); var entityCollection = dataSource._entityCollection; if (clear) { dataSource._version = undefined; dataSource._documentPacket = new DocumentPacket(); entityCollection.removeAll(); } CzmlDataSource._processCzml( czml, entityCollection, sourceUri, undefined, dataSource ); var raiseChangedEvent = updateClock(dataSource); var documentPacket = dataSource._documentPacket; if ( defined(documentPacket.name) && dataSource._name !== documentPacket.name ) { dataSource._name = documentPacket.name; raiseChangedEvent = true; } else if (!defined(dataSource._name) && defined(sourceUri)) { dataSource._name = getFilenameFromUri(sourceUri.getUrlComponent()); raiseChangedEvent = true; } DataSource.setLoading(dataSource, false); if (raiseChangedEvent) { dataSource._changed.raiseEvent(dataSource); } return dataSource; } function DocumentPacket() { this.name = undefined; this.clock = undefined; } /** * @typedef {Object} CzmlDataSource.LoadOptions * * Initialization options for the `load` method. * * @property {Resource|string} [sourceUri] Overrides the url to use for resolving relative links. * @property {Credit|string} [credit] A credit for the data source, which is displayed on the canvas. */ /** * A {@link DataSource} which processes {@link https://github.com/AnalyticalGraphicsInc/czml-writer/wiki/CZML-Guide|CZML}. * @alias CzmlDataSource * @constructor * * @param {String} [name] An optional name for the data source. This value will be overwritten if a loaded document contains a name. * * @demo {@link https://sandcastle.cesium.com/index.html?src=CZML.html|Cesium Sandcastle CZML Demo} */ function CzmlDataSource(name) { this._name = name; this._changed = new Event(); this._error = new Event(); this._isLoading = false; this._loading = new Event(); this._clock = undefined; this._documentPacket = new DocumentPacket(); this._version = undefined; this._entityCollection = new EntityCollection(this); this._entityCluster = new EntityCluster(); this._credit = undefined; this._resourceCredits = []; } /** * Creates a Promise to a new instance loaded with the provided CZML data. * * @param {Resource|String|Object} czml A url or CZML object to be processed. * @param {CzmlDataSource.LoadOptions} [options] An object specifying configuration options * * @returns {Promise.} A promise that resolves to the new instance once the data is processed. */ CzmlDataSource.load = function (czml, options) { return new CzmlDataSource().load(czml, options); }; Object.defineProperties(CzmlDataSource.prototype, { /** * Gets a human-readable name for this instance. * @memberof CzmlDataSource.prototype * @type {String} */ name: { get: function () { return this._name; }, }, /** * Gets the clock settings defined by the loaded CZML. If no clock is explicitly * defined in the CZML, the combined availability of all objects is returned. If * only static data exists, this value is undefined. * @memberof CzmlDataSource.prototype * @type {DataSourceClock} */ clock: { get: function () { return this._clock; }, }, /** * Gets the collection of {@link Entity} instances. * @memberof CzmlDataSource.prototype * @type {EntityCollection} */ entities: { get: function () { return this._entityCollection; }, }, /** * Gets a value indicating if the data source is currently loading data. * @memberof CzmlDataSource.prototype * @type {Boolean} */ isLoading: { get: function () { return this._isLoading; }, }, /** * Gets an event that will be raised when the underlying data changes. * @memberof CzmlDataSource.prototype * @type {Event} */ changedEvent: { get: function () { return this._changed; }, }, /** * Gets an event that will be raised if an error is encountered during processing. * @memberof CzmlDataSource.prototype * @type {Event} */ errorEvent: { get: function () { return this._error; }, }, /** * Gets an event that will be raised when the data source either starts or stops loading. * @memberof CzmlDataSource.prototype * @type {Event} */ loadingEvent: { get: function () { return this._loading; }, }, /** * Gets whether or not this data source should be displayed. * @memberof CzmlDataSource.prototype * @type {Boolean} */ show: { get: function () { return this._entityCollection.show; }, set: function (value) { this._entityCollection.show = value; }, }, /** * Gets or sets the clustering options for this data source. This object can be shared between multiple data sources. * * @memberof CzmlDataSource.prototype * @type {EntityCluster} */ clustering: { get: function () { return this._entityCluster; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value must be defined."); } //>>includeEnd('debug'); this._entityCluster = value; }, }, /** * Gets the credit that will be displayed for the data source * @memberof CzmlDataSource.prototype * @type {Credit} */ credit: { get: function () { return this._credit; }, }, }); /** * Gets the array of CZML processing functions. * @memberof CzmlDataSource * @type Array */ CzmlDataSource.updaters = [ processBillboard, // processBox, // processCorridor, // processCylinder, // processEllipse, // processEllipsoid, // processLabel, // processModel, // processName, // processDescription$1, // processPath, // processPoint$2, // processPolygon$2, // processPolyline, // processPolylineVolume, // processProperties, // processRectangle, // processPosition, // processTileset, // processViewFrom, // processWall, // processOrientation, // processAvailability, ]; /** * Processes the provided url or CZML object without clearing any existing data. * * @param {Resource|String|Object} czml A url or CZML object to be processed. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] Overrides the url to use for resolving relative links. * @returns {Promise.} A promise that resolves to this instances once the data is processed. */ CzmlDataSource.prototype.process = function (czml, options) { return load$2(this, czml, options, false); }; /** * Loads the provided url or CZML object, replacing any existing data. * * @param {Resource|String|Object} czml A url or CZML object to be processed. * @param {CzmlDataSource.LoadOptions} [options] An object specifying configuration options * @returns {Promise.} A promise that resolves to this instances once the data is processed. */ CzmlDataSource.prototype.load = function (czml, options) { return load$2(this, czml, options, true); }; /** * Updates the data source to the provided time. This function is optional and * is not required to be implemented. It is provided for data sources which * retrieve data based on the current animation time or scene state. * If implemented, update will be called by {@link DataSourceDisplay} once a frame. * * @param {JulianDate} time The simulation time. * @returns {Boolean} True if this data source is ready to be displayed at the provided time, false otherwise. */ CzmlDataSource.prototype.update = function (time) { return true; }; /** * A helper function used by custom CZML updater functions * which creates or updates a {@link Property} from a CZML packet. * @function * * @param {Function} type The constructor function for the property being processed. * @param {Object} object The object on which the property will be added or updated. * @param {String} propertyName The name of the property on the object. * @param {Object} packetData The CZML packet being processed. * @param {TimeInterval} interval A constraining interval for which the data is valid. * @param {String} sourceUri The originating uri of the data being processed. * @param {EntityCollection} entityCollection The collection being processsed. */ CzmlDataSource.processPacketData = processPacketData; /** * A helper function used by custom CZML updater functions * which creates or updates a {@link PositionProperty} from a CZML packet. * @function * * @param {Object} object The object on which the property will be added or updated. * @param {String} propertyName The name of the property on the object. * @param {Object} packetData The CZML packet being processed. * @param {TimeInterval} interval A constraining interval for which the data is valid. * @param {String} sourceUri The originating uri of the data being processed. * @param {EntityCollection} entityCollection The collection being processsed. */ CzmlDataSource.processPositionPacketData = processPositionPacketData; /** * A helper function used by custom CZML updater functions * which creates or updates a {@link MaterialProperty} from a CZML packet. * @function * * @param {Object} object The object on which the property will be added or updated. * @param {String} propertyName The name of the property on the object. * @param {Object} packetData The CZML packet being processed. * @param {TimeInterval} interval A constraining interval for which the data is valid. * @param {String} sourceUri The originating uri of the data being processed. * @param {EntityCollection} entityCollection The collection being processsed. */ CzmlDataSource.processMaterialPacketData = processMaterialPacketData; CzmlDataSource._processCzml = function ( czml, entityCollection, sourceUri, updaterFunctions, dataSource ) { updaterFunctions = defaultValue(updaterFunctions, CzmlDataSource.updaters); if (Array.isArray(czml)) { for (var i = 0, len = czml.length; i < len; ++i) { processCzmlPacket( czml[i], entityCollection, updaterFunctions, sourceUri, dataSource ); } } else { processCzmlPacket( czml, entityCollection, updaterFunctions, sourceUri, dataSource ); } }; /** * A collection of {@link DataSource} instances. * @alias DataSourceCollection * @constructor */ function DataSourceCollection() { this._dataSources = []; this._dataSourceAdded = new Event(); this._dataSourceRemoved = new Event(); this._dataSourceMoved = new Event(); } Object.defineProperties(DataSourceCollection.prototype, { /** * Gets the number of data sources in this collection. * @memberof DataSourceCollection.prototype * @type {Number} * @readonly */ length: { get: function () { return this._dataSources.length; }, }, /** * An event that is raised when a data source is added to the collection. * Event handlers are passed the data source that was added. * @memberof DataSourceCollection.prototype * @type {Event} * @readonly */ dataSourceAdded: { get: function () { return this._dataSourceAdded; }, }, /** * An event that is raised when a data source is removed from the collection. * Event handlers are passed the data source that was removed. * @memberof DataSourceCollection.prototype * @type {Event} * @readonly */ dataSourceRemoved: { get: function () { return this._dataSourceRemoved; }, }, /** * An event that is raised when a data source changes position in the collection. Event handlers are passed the data source * that was moved, its new index after the move, and its old index prior to the move. * @memberof DataSourceCollection.prototype * @type {Event} * @readonly */ dataSourceMoved: { get: function () { return this._dataSourceMoved; }, }, }); /** * Adds a data source to the collection. * * @param {DataSource|Promise.} dataSource A data source or a promise to a data source to add to the collection. * When passing a promise, the data source will not actually be added * to the collection until the promise resolves successfully. * @returns {Promise.} A Promise that resolves once the data source has been added to the collection. */ DataSourceCollection.prototype.add = function (dataSource) { //>>includeStart('debug', pragmas.debug); if (!defined(dataSource)) { throw new DeveloperError("dataSource is required."); } //>>includeEnd('debug'); var that = this; var dataSources = this._dataSources; return when(dataSource, function (value) { //Only add the data source if removeAll has not been called //Since it was added. if (dataSources === that._dataSources) { that._dataSources.push(value); that._dataSourceAdded.raiseEvent(that, value); } return value; }); }; /** * Removes a data source from this collection, if present. * * @param {DataSource} dataSource The data source to remove. * @param {Boolean} [destroy=false] Whether to destroy the data source in addition to removing it. * @returns {Boolean} true if the data source was in the collection and was removed, * false if the data source was not in the collection. */ DataSourceCollection.prototype.remove = function (dataSource, destroy) { destroy = defaultValue(destroy, false); var index = this._dataSources.indexOf(dataSource); if (index !== -1) { this._dataSources.splice(index, 1); this._dataSourceRemoved.raiseEvent(this, dataSource); if (destroy && typeof dataSource.destroy === "function") { dataSource.destroy(); } return true; } return false; }; /** * Removes all data sources from this collection. * * @param {Boolean} [destroy=false] whether to destroy the data sources in addition to removing them. */ DataSourceCollection.prototype.removeAll = function (destroy) { destroy = defaultValue(destroy, false); var dataSources = this._dataSources; for (var i = 0, len = dataSources.length; i < len; ++i) { var dataSource = dataSources[i]; this._dataSourceRemoved.raiseEvent(this, dataSource); if (destroy && typeof dataSource.destroy === "function") { dataSource.destroy(); } } this._dataSources = []; }; /** * Checks to see if the collection contains a given data source. * * @param {DataSource} dataSource The data source to check for. * @returns {Boolean} true if the collection contains the data source, false otherwise. */ DataSourceCollection.prototype.contains = function (dataSource) { return this.indexOf(dataSource) !== -1; }; /** * Determines the index of a given data source in the collection. * * @param {DataSource} dataSource The data source to find the index of. * @returns {Number} The index of the data source in the collection, or -1 if the data source does not exist in the collection. */ DataSourceCollection.prototype.indexOf = function (dataSource) { return this._dataSources.indexOf(dataSource); }; /** * Gets a data source by index from the collection. * * @param {Number} index the index to retrieve. * @returns {DataSource} The data source at the specified index. */ DataSourceCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); return this._dataSources[index]; }; /** * Gets a data source by name from the collection. * * @param {String} name The name to retrieve. * @returns {DataSource[]} A list of all data sources matching the provided name. */ DataSourceCollection.prototype.getByName = function (name) { //>>includeStart('debug', pragmas.debug); if (!defined(name)) { throw new DeveloperError("name is required."); } //>>includeEnd('debug'); return this._dataSources.filter(function (dataSource) { return dataSource.name === name; }); }; function getIndex(dataSources, dataSource) { //>>includeStart('debug', pragmas.debug); if (!defined(dataSource)) { throw new DeveloperError("dataSource is required."); } //>>includeEnd('debug'); var index = dataSources.indexOf(dataSource); //>>includeStart('debug', pragmas.debug); if (index === -1) { throw new DeveloperError("dataSource is not in this collection."); } //>>includeEnd('debug'); return index; } function swapDataSources(collection, i, j) { var arr = collection._dataSources; var length = arr.length - 1; i = CesiumMath.clamp(i, 0, length); j = CesiumMath.clamp(j, 0, length); if (i === j) { return; } var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; collection.dataSourceMoved.raiseEvent(temp, j, i); } /** * Raises a data source up one position in the collection. * * @param {DataSource} dataSource The data source to move. * * @exception {DeveloperError} dataSource is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ DataSourceCollection.prototype.raise = function (dataSource) { var index = getIndex(this._dataSources, dataSource); swapDataSources(this, index, index + 1); }; /** * Lowers a data source down one position in the collection. * * @param {DataSource} dataSource The data source to move. * * @exception {DeveloperError} dataSource is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ DataSourceCollection.prototype.lower = function (dataSource) { var index = getIndex(this._dataSources, dataSource); swapDataSources(this, index, index - 1); }; /** * Raises a data source to the top of the collection. * * @param {DataSource} dataSource The data source to move. * * @exception {DeveloperError} dataSource is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ DataSourceCollection.prototype.raiseToTop = function (dataSource) { var index = getIndex(this._dataSources, dataSource); if (index === this._dataSources.length - 1) { return; } this._dataSources.splice(index, 1); this._dataSources.push(dataSource); this.dataSourceMoved.raiseEvent( dataSource, this._dataSources.length - 1, index ); }; /** * Lowers a data source to the bottom of the collection. * * @param {DataSource} dataSource The data source to move. * * @exception {DeveloperError} dataSource is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ DataSourceCollection.prototype.lowerToBottom = function (dataSource) { var index = getIndex(this._dataSources, dataSource); if (index === 0) { return; } this._dataSources.splice(index, 1); this._dataSources.splice(0, 0, dataSource); this.dataSourceMoved.raiseEvent(dataSource, 0, index); }; /** * Returns true if this object was destroyed; otherwise, false. * If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} true if this object was destroyed; otherwise, false. * * @see DataSourceCollection#destroy */ DataSourceCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the resources held by all data sources in this collection. Explicitly destroying this * object allows for deterministic release of WebGL resources, instead of relying on the garbage * collector. Once this object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * dataSourceCollection = dataSourceCollection && dataSourceCollection.destroy(); * * @see DataSourceCollection#isDestroyed */ DataSourceCollection.prototype.destroy = function () { this.removeAll(true); return destroyObject(this); }; /** * A collection of primitives. This is most often used with {@link Scene#primitives}, * but PrimitiveCollection is also a primitive itself so collections can * be added to collections forming a hierarchy. * * @alias PrimitiveCollection * @constructor * * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.show=true] Determines if the primitives in the collection will be shown. * @param {Boolean} [options.destroyPrimitives=true] Determines if primitives in the collection are destroyed when they are removed. * * @example * var billboards = new Cesium.BillboardCollection(); * var labels = new Cesium.LabelCollection(); * * var collection = new Cesium.PrimitiveCollection(); * collection.add(billboards); * * scene.primitives.add(collection); // Add collection * scene.primitives.add(labels); // Add regular primitive */ function PrimitiveCollection(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); this._primitives = []; this._guid = createGuid(); // Used by the OrderedGroundPrimitiveCollection this._zIndex = undefined; /** * Determines if primitives in this collection will be shown. * * @type {Boolean} * @default true */ this.show = defaultValue(options.show, true); /** * Determines if primitives in the collection are destroyed when they are removed by * {@link PrimitiveCollection#destroy} or {@link PrimitiveCollection#remove} or implicitly * by {@link PrimitiveCollection#removeAll}. * * @type {Boolean} * @default true * * @example * // Example 1. Primitives are destroyed by default. * var primitives = new Cesium.PrimitiveCollection(); * var labels = primitives.add(new Cesium.LabelCollection()); * primitives = primitives.destroy(); * var b = labels.isDestroyed(); // true * * @example * // Example 2. Do not destroy primitives in a collection. * var primitives = new Cesium.PrimitiveCollection(); * primitives.destroyPrimitives = false; * var labels = primitives.add(new Cesium.LabelCollection()); * primitives = primitives.destroy(); * var b = labels.isDestroyed(); // false * labels = labels.destroy(); // explicitly destroy */ this.destroyPrimitives = defaultValue(options.destroyPrimitives, true); } Object.defineProperties(PrimitiveCollection.prototype, { /** * Gets the number of primitives in the collection. * * @memberof PrimitiveCollection.prototype * * @type {Number} * @readonly */ length: { get: function () { return this._primitives.length; }, }, }); /** * Adds a primitive to the collection. * * @param {Object} primitive The primitive to add. * @param {Number} [index] The index to add the layer at. If omitted, the primitive will be added at the bottom of all existing primitives. * @returns {Object} The primitive added to the collection. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @example * var billboards = scene.primitives.add(new Cesium.BillboardCollection()); */ PrimitiveCollection.prototype.add = function (primitive, index) { var hasIndex = defined(index); //>>includeStart('debug', pragmas.debug); if (!defined(primitive)) { throw new DeveloperError("primitive is required."); } if (hasIndex) { if (index < 0) { throw new DeveloperError("index must be greater than or equal to zero."); } else if (index > this._primitives.length) { throw new DeveloperError( "index must be less than or equal to the number of primitives." ); } } //>>includeEnd('debug'); var external = (primitive._external = primitive._external || {}); var composites = (external._composites = external._composites || {}); composites[this._guid] = { collection: this, }; if (!hasIndex) { this._primitives.push(primitive); } else { this._primitives.splice(index, 0, primitive); } return primitive; }; /** * Removes a primitive from the collection. * * @param {Object} [primitive] The primitive to remove. * @returns {Boolean} true if the primitive was removed; false if the primitive is undefined or was not found in the collection. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * var billboards = scene.primitives.add(new Cesium.BillboardCollection()); * scene.primitives.remove(billboards); // Returns true * * @see PrimitiveCollection#destroyPrimitives */ PrimitiveCollection.prototype.remove = function (primitive) { // PERFORMANCE_IDEA: We can obviously make this a lot faster. if (this.contains(primitive)) { var index = this._primitives.indexOf(primitive); if (index !== -1) { this._primitives.splice(index, 1); delete primitive._external._composites[this._guid]; if (this.destroyPrimitives) { primitive.destroy(); } return true; } // else ... this is not possible, I swear. } return false; }; /** * Removes and destroys a primitive, regardless of destroyPrimitives setting. * @private */ PrimitiveCollection.prototype.removeAndDestroy = function (primitive) { var removed = this.remove(primitive); if (removed && !this.destroyPrimitives) { primitive.destroy(); } return removed; }; /** * Removes all primitives in the collection. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#destroyPrimitives */ PrimitiveCollection.prototype.removeAll = function () { var primitives = this._primitives; var length = primitives.length; for (var i = 0; i < length; ++i) { delete primitives[i]._external._composites[this._guid]; if (this.destroyPrimitives) { primitives[i].destroy(); } } this._primitives = []; }; /** * Determines if this collection contains a primitive. * * @param {Object} [primitive] The primitive to check for. * @returns {Boolean} true if the primitive is in the collection; false if the primitive is undefined or was not found in the collection. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#get */ PrimitiveCollection.prototype.contains = function (primitive) { return !!( defined(primitive) && primitive._external && primitive._external._composites && primitive._external._composites[this._guid] ); }; function getPrimitiveIndex(compositePrimitive, primitive) { //>>includeStart('debug', pragmas.debug); if (!compositePrimitive.contains(primitive)) { throw new DeveloperError("primitive is not in this collection."); } //>>includeEnd('debug'); return compositePrimitive._primitives.indexOf(primitive); } /** * Raises a primitive "up one" in the collection. If all primitives in the collection are drawn * on the globe surface, this visually moves the primitive up one. * * @param {Object} [primitive] The primitive to raise. * * @exception {DeveloperError} primitive is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#raiseToTop * @see PrimitiveCollection#lower * @see PrimitiveCollection#lowerToBottom */ PrimitiveCollection.prototype.raise = function (primitive) { if (defined(primitive)) { var index = getPrimitiveIndex(this, primitive); var primitives = this._primitives; if (index !== primitives.length - 1) { var p = primitives[index]; primitives[index] = primitives[index + 1]; primitives[index + 1] = p; } } }; /** * Raises a primitive to the "top" of the collection. If all primitives in the collection are drawn * on the globe surface, this visually moves the primitive to the top. * * @param {Object} [primitive] The primitive to raise the top. * * @exception {DeveloperError} primitive is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#raise * @see PrimitiveCollection#lower * @see PrimitiveCollection#lowerToBottom */ PrimitiveCollection.prototype.raiseToTop = function (primitive) { if (defined(primitive)) { var index = getPrimitiveIndex(this, primitive); var primitives = this._primitives; if (index !== primitives.length - 1) { // PERFORMANCE_IDEA: Could be faster primitives.splice(index, 1); primitives.push(primitive); } } }; /** * Lowers a primitive "down one" in the collection. If all primitives in the collection are drawn * on the globe surface, this visually moves the primitive down one. * * @param {Object} [primitive] The primitive to lower. * * @exception {DeveloperError} primitive is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#lowerToBottom * @see PrimitiveCollection#raise * @see PrimitiveCollection#raiseToTop */ PrimitiveCollection.prototype.lower = function (primitive) { if (defined(primitive)) { var index = getPrimitiveIndex(this, primitive); var primitives = this._primitives; if (index !== 0) { var p = primitives[index]; primitives[index] = primitives[index - 1]; primitives[index - 1] = p; } } }; /** * Lowers a primitive to the "bottom" of the collection. If all primitives in the collection are drawn * on the globe surface, this visually moves the primitive to the bottom. * * @param {Object} [primitive] The primitive to lower to the bottom. * * @exception {DeveloperError} primitive is not in this collection. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see PrimitiveCollection#lower * @see PrimitiveCollection#raise * @see PrimitiveCollection#raiseToTop */ PrimitiveCollection.prototype.lowerToBottom = function (primitive) { if (defined(primitive)) { var index = getPrimitiveIndex(this, primitive); var primitives = this._primitives; if (index !== 0) { // PERFORMANCE_IDEA: Could be faster primitives.splice(index, 1); primitives.unshift(primitive); } } }; /** * Returns the primitive in the collection at the specified index. * * @param {Number} index The zero-based index of the primitive to return. * @returns {Object} The primitive at the index. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * // Toggle the show property of every primitive in the collection. * var primitives = scene.primitives; * var length = primitives.length; * for (var i = 0; i < length; ++i) { * var p = primitives.get(i); * p.show = !p.show; * } * * @see PrimitiveCollection#length */ PrimitiveCollection.prototype.get = function (index) { //>>includeStart('debug', pragmas.debug); if (!defined(index)) { throw new DeveloperError("index is required."); } //>>includeEnd('debug'); return this._primitives[index]; }; /** * @private */ PrimitiveCollection.prototype.update = function (frameState) { if (!this.show) { return; } var primitives = this._primitives; // Using primitives.length in the loop is a temporary workaround // to allow quadtree updates to add and remove primitives in // update(). This will be changed to manage added and removed lists. for (var i = 0; i < primitives.length; ++i) { primitives[i].update(frameState); } }; /** * @private */ PrimitiveCollection.prototype.prePassesUpdate = function (frameState) { var primitives = this._primitives; // Using primitives.length in the loop is a temporary workaround // to allow quadtree updates to add and remove primitives in // update(). This will be changed to manage added and removed lists. for (var i = 0; i < primitives.length; ++i) { var primitive = primitives[i]; if (defined(primitive.prePassesUpdate)) { primitive.prePassesUpdate(frameState); } } }; /** * @private */ PrimitiveCollection.prototype.updateForPass = function (frameState, passState) { var primitives = this._primitives; // Using primitives.length in the loop is a temporary workaround // to allow quadtree updates to add and remove primitives in // update(). This will be changed to manage added and removed lists. for (var i = 0; i < primitives.length; ++i) { var primitive = primitives[i]; if (defined(primitive.updateForPass)) { primitive.updateForPass(frameState, passState); } } }; /** * @private */ PrimitiveCollection.prototype.postPassesUpdate = function (frameState) { var primitives = this._primitives; // Using primitives.length in the loop is a temporary workaround // to allow quadtree updates to add and remove primitives in // update(). This will be changed to manage added and removed lists. for (var i = 0; i < primitives.length; ++i) { var primitive = primitives[i]; if (defined(primitive.postPassesUpdate)) { primitive.postPassesUpdate(frameState); } } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see PrimitiveCollection#destroy */ PrimitiveCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by each primitive in this collection. Explicitly destroying this * collection allows for deterministic release of WebGL resources, instead of relying on the garbage * collector to destroy this collection. *

* Since destroying a collection destroys all the contained primitives, only destroy a collection * when you are sure no other code is still using any of the contained primitives. *

* Once this collection is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * primitives = primitives && primitives.destroy(); * * @see PrimitiveCollection#isDestroyed */ PrimitiveCollection.prototype.destroy = function () { this.removeAll(); return destroyObject(this); }; /** * A primitive collection for helping maintain the order or ground primitives based on a z-index * * @private */ function OrderedGroundPrimitiveCollection() { this._length = 0; this._collections = {}; this._collectionsArray = []; this.show = true; } Object.defineProperties(OrderedGroundPrimitiveCollection.prototype, { /** * Gets the number of primitives in the collection. * * @memberof OrderedGroundPrimitiveCollection.prototype * * @type {Number} * @readonly */ length: { get: function () { return this._length; }, }, }); /** * Adds a primitive to the collection. * * @param {GroundPrimitive} primitive The primitive to add. * @param {Number} [zIndex = 0] The index of the primitive * @returns {GroundPrimitive} The primitive added to the collection. */ OrderedGroundPrimitiveCollection.prototype.add = function (primitive, zIndex) { //>>includeStart('debug', pragmas.debug); Check.defined("primitive", primitive); if (defined(zIndex)) { Check.typeOf.number("zIndex", zIndex); } //>>includeEnd('debug'); zIndex = defaultValue(zIndex, 0); var collection = this._collections[zIndex]; if (!defined(collection)) { collection = new PrimitiveCollection({ destroyPrimitives: false }); collection._zIndex = zIndex; this._collections[zIndex] = collection; var array = this._collectionsArray; var i = 0; while (i < array.length && array[i]._zIndex < zIndex) { i++; } array.splice(i, 0, collection); } collection.add(primitive); this._length++; primitive._zIndex = zIndex; return primitive; }; /** * Adjusts the z-index * @param {GroundPrimitive} primitive * @param {Number} zIndex */ OrderedGroundPrimitiveCollection.prototype.set = function (primitive, zIndex) { //>>includeStart('debug', pragmas.debug); Check.defined("primitive", primitive); Check.typeOf.number("zIndex", zIndex); //>>includeEnd('debug'); if (zIndex === primitive._zIndex) { return primitive; } this.remove(primitive, true); this.add(primitive, zIndex); return primitive; }; /** * Removes a primitive from the collection. * * @param {Object} primitive The primitive to remove. * @param {Boolean} [doNotDestroy = false] * @returns {Boolean} true if the primitive was removed; false if the primitive is undefined or was not found in the collection. */ OrderedGroundPrimitiveCollection.prototype.remove = function ( primitive, doNotDestroy ) { if (this.contains(primitive)) { var index = primitive._zIndex; var collection = this._collections[index]; var result; if (doNotDestroy) { result = collection.remove(primitive); } else { result = collection.removeAndDestroy(primitive); } if (result) { this._length--; } if (collection.length === 0) { this._collectionsArray.splice( this._collectionsArray.indexOf(collection), 1 ); this._collections[index] = undefined; collection.destroy(); } return result; } return false; }; /** * Removes all primitives in the collection. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * @see OrderedGroundPrimitiveCollection#destroyPrimitives */ OrderedGroundPrimitiveCollection.prototype.removeAll = function () { var collections = this._collectionsArray; for (var i = 0; i < collections.length; i++) { var collection = collections[i]; collection.destroyPrimitives = true; collection.destroy(); } this._collections = {}; this._collectionsArray = []; this._length = 0; }; /** * Determines if this collection contains a primitive. * * @param {Object} primitive The primitive to check for. * @returns {Boolean} true if the primitive is in the collection; false if the primitive is undefined or was not found in the collection. */ OrderedGroundPrimitiveCollection.prototype.contains = function (primitive) { if (!defined(primitive)) { return false; } var collection = this._collections[primitive._zIndex]; return defined(collection) && collection.contains(primitive); }; /** * @private */ OrderedGroundPrimitiveCollection.prototype.update = function (frameState) { if (!this.show) { return; } var collections = this._collectionsArray; for (var i = 0; i < collections.length; i++) { collections[i].update(frameState); } }; /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see OrderedGroundPrimitiveCollection#destroy */ OrderedGroundPrimitiveCollection.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by each primitive in this collection. Explicitly destroying this * collection allows for deterministic release of WebGL resources, instead of relying on the garbage * collector to destroy this collection. *

* Since destroying a collection destroys all the contained primitives, only destroy a collection * when you are sure no other code is still using any of the contained primitives. *

* Once this collection is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * primitives = primitives && primitives.destroy(); * * @see OrderedGroundPrimitiveCollection#isDestroyed */ OrderedGroundPrimitiveCollection.prototype.destroy = function () { this.removeAll(); return destroyObject(this); }; /** * @private */ function DynamicGeometryBatch(primitives, orderedGroundPrimitives) { this._primitives = primitives; this._orderedGroundPrimitives = orderedGroundPrimitives; this._dynamicUpdaters = new AssociativeArray(); } DynamicGeometryBatch.prototype.add = function (time, updater) { this._dynamicUpdaters.set( updater.id, updater.createDynamicUpdater( this._primitives, this._orderedGroundPrimitives ) ); }; DynamicGeometryBatch.prototype.remove = function (updater) { var id = updater.id; var dynamicUpdater = this._dynamicUpdaters.get(id); if (defined(dynamicUpdater)) { this._dynamicUpdaters.remove(id); dynamicUpdater.destroy(); } }; DynamicGeometryBatch.prototype.update = function (time) { var geometries = this._dynamicUpdaters.values; for (var i = 0, len = geometries.length; i < len; i++) { geometries[i].update(time); } return true; }; DynamicGeometryBatch.prototype.removeAllPrimitives = function () { var geometries = this._dynamicUpdaters.values; for (var i = 0, len = geometries.length; i < len; i++) { geometries[i].destroy(); } this._dynamicUpdaters.removeAll(); }; DynamicGeometryBatch.prototype.getBoundingSphere = function (updater, result) { updater = this._dynamicUpdaters.get(updater.id); if (defined(updater) && defined(updater.getBoundingSphere)) { return updater.getBoundingSphere(result); } return BoundingSphereState$1.FAILED; }; var scratchColor$b = new Color(); var defaultOffset$6 = Cartesian3.ZERO; var offsetScratch$6 = new Cartesian3(); var scratchRectangle$3 = new Rectangle(); function EllipseGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.center = undefined; this.semiMajorAxis = undefined; this.semiMinorAxis = undefined; this.rotation = undefined; this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; this.stRotation = undefined; this.numberOfVerticalLines = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for ellipses. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias EllipseGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function EllipseGeometryUpdater(entity, scene) { GroundGeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new EllipseGeometryOptions(entity), geometryPropertyName: "ellipse", observedPropertyNames: ["availability", "position", "ellipse"], }); this._onEntityPropertyChanged(entity, "ellipse", entity.ellipse, undefined); } if (defined(Object.create)) { EllipseGeometryUpdater.prototype = Object.create( GroundGeometryUpdater.prototype ); EllipseGeometryUpdater.prototype.constructor = EllipseGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ EllipseGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( this._distanceDisplayConditionProperty.getValue(time) ), offset: undefined, color: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$b); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$6, offsetScratch$6 ) ); } return new GeometryInstance({ id: entity, geometry: new EllipseGeometry(this._options), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ EllipseGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$b ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$6, offsetScratch$6 ) ); } return new GeometryInstance({ id: entity, geometry: new EllipseOutlineGeometry(this._options), attributes: attributes, }); }; EllipseGeometryUpdater.prototype._computeCenter = function (time, result) { return Property.getValueOrUndefined(this._entity.position, time, result); }; EllipseGeometryUpdater.prototype._isHidden = function (entity, ellipse) { var position = entity.position; return ( !defined(position) || !defined(ellipse.semiMajorAxis) || !defined(ellipse.semiMinorAxis) || GeometryUpdater.prototype._isHidden.call(this, entity, ellipse) ); }; EllipseGeometryUpdater.prototype._isDynamic = function (entity, ellipse) { return ( !entity.position.isConstant || // !ellipse.semiMajorAxis.isConstant || // !ellipse.semiMinorAxis.isConstant || // !Property.isConstant(ellipse.rotation) || // !Property.isConstant(ellipse.height) || // !Property.isConstant(ellipse.extrudedHeight) || // !Property.isConstant(ellipse.granularity) || // !Property.isConstant(ellipse.stRotation) || // !Property.isConstant(ellipse.outlineWidth) || // !Property.isConstant(ellipse.numberOfVerticalLines) || // !Property.isConstant(ellipse.zIndex) || // (this._onTerrain && !Property.isConstant(this._materialProperty) && !(this._materialProperty instanceof ColorMaterialProperty)) ); }; EllipseGeometryUpdater.prototype._setStaticOptions = function ( entity, ellipse ) { var heightValue = Property.getValueOrUndefined( ellipse.height, Iso8601.MINIMUM_VALUE ); var heightReferenceValue = Property.getValueOrDefault( ellipse.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( ellipse.extrudedHeight, Iso8601.MINIMUM_VALUE ); var extrudedHeightReferenceValue = Property.getValueOrDefault( ellipse.extrudedHeightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } var options = this._options; options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.center = entity.position.getValue( Iso8601.MINIMUM_VALUE, options.center ); options.semiMajorAxis = ellipse.semiMajorAxis.getValue( Iso8601.MINIMUM_VALUE, options.semiMajorAxis ); options.semiMinorAxis = ellipse.semiMinorAxis.getValue( Iso8601.MINIMUM_VALUE, options.semiMinorAxis ); options.rotation = Property.getValueOrUndefined( ellipse.rotation, Iso8601.MINIMUM_VALUE ); options.granularity = Property.getValueOrUndefined( ellipse.granularity, Iso8601.MINIMUM_VALUE ); options.stRotation = Property.getValueOrUndefined( ellipse.stRotation, Iso8601.MINIMUM_VALUE ); options.numberOfVerticalLines = Property.getValueOrUndefined( ellipse.numberOfVerticalLines, Iso8601.MINIMUM_VALUE ); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( EllipseGeometry.computeRectangle(options, scratchRectangle$3) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; EllipseGeometryUpdater.DynamicGeometryUpdater = DynamicEllipseGeometryUpdater; /** * @private */ function DynamicEllipseGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicEllipseGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicEllipseGeometryUpdater.prototype.constructor = DynamicEllipseGeometryUpdater; } DynamicEllipseGeometryUpdater.prototype._isHidden = function ( entity, ellipse, time ) { var options = this._options; return ( !defined(options.center) || !defined(options.semiMajorAxis) || !defined(options.semiMinorAxis) || DynamicGeometryUpdater$1.prototype._isHidden.call(this, entity, ellipse, time) ); }; DynamicEllipseGeometryUpdater.prototype._setOptions = function ( entity, ellipse, time ) { var options = this._options; var heightValue = Property.getValueOrUndefined(ellipse.height, time); var heightReferenceValue = Property.getValueOrDefault( ellipse.heightReference, time, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( ellipse.extrudedHeight, time ); var extrudedHeightReferenceValue = Property.getValueOrDefault( ellipse.extrudedHeightReference, time, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } options.center = Property.getValueOrUndefined( entity.position, time, options.center ); options.semiMajorAxis = Property.getValueOrUndefined( ellipse.semiMajorAxis, time ); options.semiMinorAxis = Property.getValueOrUndefined( ellipse.semiMinorAxis, time ); options.rotation = Property.getValueOrUndefined(ellipse.rotation, time); options.granularity = Property.getValueOrUndefined(ellipse.granularity, time); options.stRotation = Property.getValueOrUndefined(ellipse.stRotation, time); options.numberOfVerticalLines = Property.getValueOrUndefined( ellipse.numberOfVerticalLines, time ); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( EllipseGeometry.computeRectangle(options, scratchRectangle$3) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; var defaultMaterial$1 = new ColorMaterialProperty(Color.WHITE); var defaultOffset$5 = Cartesian3.ZERO; var offsetScratch$5 = new Cartesian3(); var radiiScratch = new Cartesian3(); var innerRadiiScratch = new Cartesian3(); var scratchColor$a = new Color(); var unitSphere = new Cartesian3(1, 1, 1); function EllipsoidGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.radii = undefined; this.innerRadii = undefined; this.minimumClock = undefined; this.maximumClock = undefined; this.minimumCone = undefined; this.maximumCone = undefined; this.stackPartitions = undefined; this.slicePartitions = undefined; this.subdivisions = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for ellipsoids. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias EllipsoidGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function EllipsoidGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new EllipsoidGeometryOptions(entity), geometryPropertyName: "ellipsoid", observedPropertyNames: [ "availability", "position", "orientation", "ellipsoid", ], }); this._onEntityPropertyChanged( entity, "ellipsoid", entity.ellipsoid, undefined ); } if (defined(Object.create)) { EllipsoidGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); EllipsoidGeometryUpdater.prototype.constructor = EllipsoidGeometryUpdater; } Object.defineProperties(EllipsoidGeometryUpdater.prototype, { /** * Gets the terrain offset property * @type {TerrainOffsetProperty} * @memberof EllipsoidGeometryUpdater.prototype * @readonly * @private */ terrainOffsetProperty: { get: function () { return this._terrainOffsetProperty; }, }, }); /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @param {Boolean} [skipModelMatrix=false] Whether to compute a model matrix for the geometry instance * @param {Matrix4} [modelMatrixResult] Used to store the result of the model matrix calculation * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ EllipsoidGeometryUpdater.prototype.createFillGeometryInstance = function ( time, skipModelMatrix, modelMatrixResult ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var color; var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); var attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: undefined, offset: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$a); } if (!defined(currentColor)) { currentColor = Color.WHITE; } color = ColorGeometryInstanceAttribute.fromColor(currentColor); attributes.color = color; } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$5, offsetScratch$5 ) ); } return new GeometryInstance({ id: entity, geometry: new EllipsoidGeometry(this._options), modelMatrix: skipModelMatrix ? undefined : entity.computeModelMatrixForHeightReference( time, entity.ellipsoid.heightReference, this._options.radii.z * 0.5, this._scene.mapProjection.ellipsoid, modelMatrixResult ), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @param {Boolean} [skipModelMatrix=false] Whether to compute a model matrix for the geometry instance * @param {Matrix4} [modelMatrixResult] Used to store the result of the model matrix calculation * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ EllipsoidGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time, skipModelMatrix, modelMatrixResult ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$a ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$5, offsetScratch$5 ) ); } return new GeometryInstance({ id: entity, geometry: new EllipsoidOutlineGeometry(this._options), modelMatrix: skipModelMatrix ? undefined : entity.computeModelMatrixForHeightReference( time, entity.ellipsoid.heightReference, this._options.radii.z * 0.5, this._scene.mapProjection.ellipsoid, modelMatrixResult ), attributes: attributes, }); }; EllipsoidGeometryUpdater.prototype._computeCenter = function (time, result) { return Property.getValueOrUndefined(this._entity.position, time, result); }; EllipsoidGeometryUpdater.prototype._isHidden = function (entity, ellipsoid) { return ( !defined(entity.position) || !defined(ellipsoid.radii) || GeometryUpdater.prototype._isHidden.call(this, entity, ellipsoid) ); }; EllipsoidGeometryUpdater.prototype._isDynamic = function (entity, ellipsoid) { return ( !entity.position.isConstant || // !Property.isConstant(entity.orientation) || // !ellipsoid.radii.isConstant || // !Property.isConstant(ellipsoid.innerRadii) || // !Property.isConstant(ellipsoid.stackPartitions) || // !Property.isConstant(ellipsoid.slicePartitions) || // !Property.isConstant(ellipsoid.outlineWidth) || // !Property.isConstant(ellipsoid.minimumClock) || // !Property.isConstant(ellipsoid.maximumClock) || // !Property.isConstant(ellipsoid.minimumCone) || // !Property.isConstant(ellipsoid.maximumCone) || // !Property.isConstant(ellipsoid.subdivisions) ); }; EllipsoidGeometryUpdater.prototype._setStaticOptions = function ( entity, ellipsoid ) { var heightReference = Property.getValueOrDefault( ellipsoid.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var options = this._options; options.vertexFormat = this._materialProperty instanceof ColorMaterialProperty ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.radii = ellipsoid.radii.getValue( Iso8601.MINIMUM_VALUE, options.radii ); options.innerRadii = Property.getValueOrUndefined( ellipsoid.innerRadii, options.radii ); options.minimumClock = Property.getValueOrUndefined( ellipsoid.minimumClock, Iso8601.MINIMUM_VALUE ); options.maximumClock = Property.getValueOrUndefined( ellipsoid.maximumClock, Iso8601.MINIMUM_VALUE ); options.minimumCone = Property.getValueOrUndefined( ellipsoid.minimumCone, Iso8601.MINIMUM_VALUE ); options.maximumCone = Property.getValueOrUndefined( ellipsoid.maximumCone, Iso8601.MINIMUM_VALUE ); options.stackPartitions = Property.getValueOrUndefined( ellipsoid.stackPartitions, Iso8601.MINIMUM_VALUE ); options.slicePartitions = Property.getValueOrUndefined( ellipsoid.slicePartitions, Iso8601.MINIMUM_VALUE ); options.subdivisions = Property.getValueOrUndefined( ellipsoid.subdivisions, Iso8601.MINIMUM_VALUE ); options.offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; }; EllipsoidGeometryUpdater.prototype._onEntityPropertyChanged = heightReferenceOnEntityPropertyChanged; EllipsoidGeometryUpdater.DynamicGeometryUpdater = DynamicEllipsoidGeometryUpdater; /** * @private */ function DynamicEllipsoidGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); this._scene = geometryUpdater._scene; this._modelMatrix = new Matrix4(); this._attributes = undefined; this._outlineAttributes = undefined; this._lastSceneMode = undefined; this._lastShow = undefined; this._lastOutlineShow = undefined; this._lastOutlineWidth = undefined; this._lastOutlineColor = undefined; this._lastOffset = new Cartesian3(); this._material = {}; } if (defined(Object.create)) { DynamicEllipsoidGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicEllipsoidGeometryUpdater.prototype.constructor = DynamicEllipsoidGeometryUpdater; } DynamicEllipsoidGeometryUpdater.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var entity = this._entity; var ellipsoid = entity.ellipsoid; if ( !entity.isShowing || !entity.isAvailable(time) || !Property.getValueOrDefault(ellipsoid.show, time, true) ) { if (defined(this._primitive)) { this._primitive.show = false; } if (defined(this._outlinePrimitive)) { this._outlinePrimitive.show = false; } return; } var radii = Property.getValueOrUndefined(ellipsoid.radii, time, radiiScratch); var modelMatrix = defined(radii) ? entity.computeModelMatrixForHeightReference( time, ellipsoid.heightReference, radii.z * 0.5, this._scene.mapProjection.ellipsoid, this._modelMatrix ) : undefined; if (!defined(modelMatrix) || !defined(radii)) { if (defined(this._primitive)) { this._primitive.show = false; } if (defined(this._outlinePrimitive)) { this._outlinePrimitive.show = false; } return; } //Compute attributes and material. var showFill = Property.getValueOrDefault(ellipsoid.fill, time, true); var showOutline = Property.getValueOrDefault(ellipsoid.outline, time, false); var outlineColor = Property.getValueOrClonedDefault( ellipsoid.outlineColor, time, Color.BLACK, scratchColor$a ); var material = MaterialProperty.getValue( time, defaultValue(ellipsoid.material, defaultMaterial$1), this._material ); // Check properties that could trigger a primitive rebuild. var innerRadii = Property.getValueOrUndefined( ellipsoid.innerRadii, time, innerRadiiScratch ); var minimumClock = Property.getValueOrUndefined(ellipsoid.minimumClock, time); var maximumClock = Property.getValueOrUndefined(ellipsoid.maximumClock, time); var minimumCone = Property.getValueOrUndefined(ellipsoid.minimumCone, time); var maximumCone = Property.getValueOrUndefined(ellipsoid.maximumCone, time); var stackPartitions = Property.getValueOrUndefined( ellipsoid.stackPartitions, time ); var slicePartitions = Property.getValueOrUndefined( ellipsoid.slicePartitions, time ); var subdivisions = Property.getValueOrUndefined(ellipsoid.subdivisions, time); var outlineWidth = Property.getValueOrDefault( ellipsoid.outlineWidth, time, 1.0 ); var heightReference = Property.getValueOrDefault( ellipsoid.heightReference, time, HeightReference$1.NONE ); var offsetAttribute = heightReference !== HeightReference$1.NONE ? GeometryOffsetAttribute$1.ALL : undefined; //In 3D we use a fast path by modifying Primitive.modelMatrix instead of regenerating the primitive every frame. //Also check for height reference because this method doesn't work when the height is relative to terrain. var sceneMode = this._scene.mode; var in3D = sceneMode === SceneMode$1.SCENE3D && heightReference === HeightReference$1.NONE; var options = this._options; var shadows = this._geometryUpdater.shadowsProperty.getValue(time); var distanceDisplayConditionProperty = this._geometryUpdater .distanceDisplayConditionProperty; var distanceDisplayCondition = distanceDisplayConditionProperty.getValue( time ); var offset = Property.getValueOrDefault( this._geometryUpdater.terrainOffsetProperty, time, defaultOffset$5, offsetScratch$5 ); //We only rebuild the primitive if something other than the radii has changed //For the radii, we use unit sphere and then deform it with a scale matrix. var rebuildPrimitives = !in3D || this._lastSceneMode !== sceneMode || !defined(this._primitive) || // options.stackPartitions !== stackPartitions || options.slicePartitions !== slicePartitions || // (defined(innerRadii) && !Cartesian3.equals(options.innerRadii !== innerRadii)) || options.minimumClock !== minimumClock || // options.maximumClock !== maximumClock || options.minimumCone !== minimumCone || // options.maximumCone !== maximumCone || options.subdivisions !== subdivisions || // this._lastOutlineWidth !== outlineWidth || options.offsetAttribute !== offsetAttribute; if (rebuildPrimitives) { var primitives = this._primitives; primitives.removeAndDestroy(this._primitive); primitives.removeAndDestroy(this._outlinePrimitive); this._primitive = undefined; this._outlinePrimitive = undefined; this._lastSceneMode = sceneMode; this._lastOutlineWidth = outlineWidth; options.stackPartitions = stackPartitions; options.slicePartitions = slicePartitions; options.subdivisions = subdivisions; options.offsetAttribute = offsetAttribute; options.radii = Cartesian3.clone(in3D ? unitSphere : radii, options.radii); if (defined(innerRadii)) { if (in3D) { var mag = Cartesian3.magnitude(radii); options.innerRadii = Cartesian3.fromElements( innerRadii.x / mag, innerRadii.y / mag, innerRadii.z / mag, options.innerRadii ); } else { options.innerRadii = Cartesian3.clone(innerRadii, options.innerRadii); } } else { options.innerRadii = undefined; } options.minimumClock = minimumClock; options.maximumClock = maximumClock; options.minimumCone = minimumCone; options.maximumCone = maximumCone; var appearance = new MaterialAppearance({ material: material, translucent: material.isTranslucent(), closed: true, }); options.vertexFormat = appearance.vertexFormat; var fillInstance = this._geometryUpdater.createFillGeometryInstance( time, in3D, this._modelMatrix ); this._primitive = primitives.add( new Primitive({ geometryInstances: fillInstance, appearance: appearance, asynchronous: false, shadows: shadows, }) ); var outlineInstance = this._geometryUpdater.createOutlineGeometryInstance( time, in3D, this._modelMatrix ); this._outlinePrimitive = primitives.add( new Primitive({ geometryInstances: outlineInstance, appearance: new PerInstanceColorAppearance({ flat: true, translucent: outlineInstance.attributes.color.value[3] !== 255, renderState: { lineWidth: this._geometryUpdater._scene.clampLineWidth( outlineWidth ), }, }), asynchronous: false, shadows: shadows, }) ); this._lastShow = showFill; this._lastOutlineShow = showOutline; this._lastOutlineColor = Color.clone(outlineColor, this._lastOutlineColor); this._lastDistanceDisplayCondition = distanceDisplayCondition; this._lastOffset = Cartesian3.clone(offset, this._lastOffset); } else if (this._primitive.ready) { //Update attributes only. var primitive = this._primitive; var outlinePrimitive = this._outlinePrimitive; primitive.show = true; outlinePrimitive.show = true; primitive.appearance.material = material; var attributes = this._attributes; if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(entity); this._attributes = attributes; } if (showFill !== this._lastShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( showFill, attributes.show ); this._lastShow = showFill; } var outlineAttributes = this._outlineAttributes; if (!defined(outlineAttributes)) { outlineAttributes = outlinePrimitive.getGeometryInstanceAttributes( entity ); this._outlineAttributes = outlineAttributes; } if (showOutline !== this._lastOutlineShow) { outlineAttributes.show = ShowGeometryInstanceAttribute.toValue( showOutline, outlineAttributes.show ); this._lastOutlineShow = showOutline; } if (!Color.equals(outlineColor, this._lastOutlineColor)) { outlineAttributes.color = ColorGeometryInstanceAttribute.toValue( outlineColor, outlineAttributes.color ); Color.clone(outlineColor, this._lastOutlineColor); } if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, this._lastDistanceDisplayCondition ) ) { attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); outlineAttributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, outlineAttributes.distanceDisplayCondition ); DistanceDisplayCondition.clone( distanceDisplayCondition, this._lastDistanceDisplayCondition ); } if (!Cartesian3.equals(offset, this._lastOffset)) { attributes.offset = OffsetGeometryInstanceAttribute.toValue( offset, attributes.offset ); outlineAttributes.offset = OffsetGeometryInstanceAttribute.toValue( offset, attributes.offset ); Cartesian3.clone(offset, this._lastOffset); } } if (in3D) { //Since we are scaling a unit sphere, we can't let any of the values go to zero. //Instead we clamp them to a small value. To the naked eye, this produces the same results //that you get passing EllipsoidGeometry a radii with a zero component. radii.x = Math.max(radii.x, 0.001); radii.y = Math.max(radii.y, 0.001); radii.z = Math.max(radii.z, 0.001); modelMatrix = Matrix4.multiplyByScale(modelMatrix, radii, modelMatrix); this._primitive.modelMatrix = modelMatrix; this._outlinePrimitive.modelMatrix = modelMatrix; } }; var positionScratch$2 = new Cartesian3(); var scratchColor$9 = new Color(); function PlaneGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.plane = undefined; this.dimensions = undefined; } /** * A {@link GeometryUpdater} for planes. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias PlaneGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function PlaneGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new PlaneGeometryOptions(entity), geometryPropertyName: "plane", observedPropertyNames: ["availability", "position", "orientation", "plane"], }); this._onEntityPropertyChanged(entity, "plane", entity.plane, undefined); } if (defined(Object.create)) { PlaneGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); PlaneGeometryUpdater.prototype.constructor = PlaneGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ PlaneGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes; var color; var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$9); } if (!defined(currentColor)) { currentColor = Color.WHITE; } color = ColorGeometryInstanceAttribute.fromColor(currentColor); attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: color, }; } else { attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, }; } var planeGraphics = entity.plane; var options = this._options; var modelMatrix = entity.computeModelMatrix(time); var plane = Property.getValueOrDefault( planeGraphics.plane, time, options.plane ); var dimensions = Property.getValueOrUndefined( planeGraphics.dimensions, time, options.dimensions ); options.plane = plane; options.dimensions = dimensions; modelMatrix = createPrimitiveMatrix( plane, dimensions, modelMatrix, modelMatrix ); return new GeometryInstance({ id: entity, geometry: new PlaneGeometry(this._options), modelMatrix: modelMatrix, attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ PlaneGeometryUpdater.prototype.createOutlineGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$9 ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var planeGraphics = entity.plane; var options = this._options; var modelMatrix = entity.computeModelMatrix(time); var plane = Property.getValueOrDefault( planeGraphics.plane, time, options.plane ); var dimensions = Property.getValueOrUndefined( planeGraphics.dimensions, time, options.dimensions ); options.plane = plane; options.dimensions = dimensions; modelMatrix = createPrimitiveMatrix( plane, dimensions, modelMatrix, modelMatrix ); return new GeometryInstance({ id: entity, geometry: new PlaneOutlineGeometry(), modelMatrix: modelMatrix, attributes: { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), }, }); }; PlaneGeometryUpdater.prototype._isHidden = function (entity, plane) { return ( !defined(plane.plane) || !defined(plane.dimensions) || !defined(entity.position) || GeometryUpdater.prototype._isHidden.call(this, entity, plane) ); }; PlaneGeometryUpdater.prototype._getIsClosed = function (options) { return false; }; PlaneGeometryUpdater.prototype._isDynamic = function (entity, plane) { return ( !entity.position.isConstant || // !Property.isConstant(entity.orientation) || // !plane.plane.isConstant || // !plane.dimensions.isConstant || // !Property.isConstant(plane.outlineWidth) ); }; PlaneGeometryUpdater.prototype._setStaticOptions = function (entity, plane) { var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; var options = this._options; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.plane = plane.plane.getValue(Iso8601.MINIMUM_VALUE, options.plane); options.dimensions = plane.dimensions.getValue( Iso8601.MINIMUM_VALUE, options.dimensions ); }; PlaneGeometryUpdater.DynamicGeometryUpdater = DynamicPlaneGeometryUpdater; /** * @private */ function DynamicPlaneGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicPlaneGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicPlaneGeometryUpdater.prototype.constructor = DynamicPlaneGeometryUpdater; } DynamicPlaneGeometryUpdater.prototype._isHidden = function ( entity, plane, time ) { var options = this._options; var position = Property.getValueOrUndefined( entity.position, time, positionScratch$2 ); return ( !defined(position) || !defined(options.plane) || !defined(options.dimensions) || DynamicGeometryUpdater$1.prototype._isHidden.call(this, entity, plane, time) ); }; DynamicPlaneGeometryUpdater.prototype._setOptions = function ( entity, plane, time ) { var options = this._options; options.plane = Property.getValueOrDefault(plane.plane, time, options.plane); options.dimensions = Property.getValueOrUndefined( plane.dimensions, time, options.dimensions ); }; var scratchAxis = new Cartesian3(); var scratchUp$2 = new Cartesian3(); var scratchTranslation$1 = new Cartesian3(); var scratchScale$1 = new Cartesian3(); var scratchRotation$1 = new Matrix3(); var scratchRotationScale = new Matrix3(); var scratchLocalTransform = new Matrix4(); function createPrimitiveMatrix(plane, dimensions, transform, result) { var normal = plane.normal; var distance = plane.distance; var translation = Cartesian3.multiplyByScalar( normal, -distance, scratchTranslation$1 ); var up = Cartesian3.clone(Cartesian3.UNIT_Z, scratchUp$2); if ( CesiumMath.equalsEpsilon( Math.abs(Cartesian3.dot(up, normal)), 1.0, CesiumMath.EPSILON8 ) ) { up = Cartesian3.clone(Cartesian3.UNIT_Y, up); } var left = Cartesian3.cross(up, normal, scratchAxis); up = Cartesian3.cross(normal, left, up); Cartesian3.normalize(left, left); Cartesian3.normalize(up, up); var rotationMatrix = scratchRotation$1; Matrix3.setColumn(rotationMatrix, 0, left, rotationMatrix); Matrix3.setColumn(rotationMatrix, 1, up, rotationMatrix); Matrix3.setColumn(rotationMatrix, 2, normal, rotationMatrix); var scale = Cartesian3.fromElements( dimensions.x, dimensions.y, 1.0, scratchScale$1 ); var rotationScaleMatrix = Matrix3.multiplyByScale( rotationMatrix, scale, scratchRotationScale ); var localTransform = Matrix4.fromRotationTranslation( rotationScaleMatrix, translation, scratchLocalTransform ); return Matrix4.multiplyTransformation(transform, localTransform, result); } /** * @private */ PlaneGeometryUpdater.createPrimitiveMatrix = createPrimitiveMatrix; var heightAndPerPositionHeightWarning = "Entity polygons cannot have both height and perPositionHeight. height will be ignored"; var heightReferenceAndPerPositionHeightWarning = "heightReference is not supported for entity polygons with perPositionHeight. heightReference will be ignored"; var scratchColor$8 = new Color(); var defaultOffset$4 = Cartesian3.ZERO; var offsetScratch$4 = new Cartesian3(); var scratchRectangle$2 = new Rectangle(); var scratch2DPositions = []; var cart2Scratch = new Cartesian2(); function PolygonGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.polygonHierarchy = undefined; this.perPositionHeight = undefined; this.closeTop = undefined; this.closeBottom = undefined; this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; this.stRotation = undefined; this.offsetAttribute = undefined; this.arcType = undefined; } /** * A {@link GeometryUpdater} for polygons. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias PolygonGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function PolygonGeometryUpdater(entity, scene) { GroundGeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new PolygonGeometryOptions(entity), geometryPropertyName: "polygon", observedPropertyNames: ["availability", "polygon"], }); this._onEntityPropertyChanged(entity, "polygon", entity.polygon, undefined); } if (defined(Object.create)) { PolygonGeometryUpdater.prototype = Object.create( GroundGeometryUpdater.prototype ); PolygonGeometryUpdater.prototype.constructor = PolygonGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ PolygonGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var options = this._options; var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( this._distanceDisplayConditionProperty.getValue(time) ), offset: undefined, color: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$8); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$4, offsetScratch$4 ) ); } var geometry; if (options.perPositionHeight && !defined(options.extrudedHeight)) { geometry = new CoplanarPolygonGeometry(options); } else { geometry = new PolygonGeometry(options); } return new GeometryInstance({ id: entity, geometry: geometry, attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ PolygonGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var options = this._options; var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$8 ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$4, offsetScratch$4 ) ); } var geometry; if (options.perPositionHeight && !defined(options.extrudedHeight)) { geometry = new CoplanarPolygonOutlineGeometry(options); } else { geometry = new PolygonOutlineGeometry(options); } return new GeometryInstance({ id: entity, geometry: geometry, attributes: attributes, }); }; PolygonGeometryUpdater.prototype._computeCenter = function (time, result) { var hierarchy = Property.getValueOrUndefined( this._entity.polygon.hierarchy, time ); if (!defined(hierarchy)) { return; } var positions = hierarchy.positions; if (positions.length === 0) { return; } var ellipsoid = this._scene.mapProjection.ellipsoid; var tangentPlane = EllipsoidTangentPlane.fromPoints(positions, ellipsoid); var positions2D = tangentPlane.projectPointsOntoPlane( positions, scratch2DPositions ); var length = positions2D.length; var area = 0; var j = length - 1; var centroid2D = new Cartesian2(); for (var i = 0; i < length; j = i++) { var p1 = positions2D[i]; var p2 = positions2D[j]; var f = p1.x * p2.y - p2.x * p1.y; var sum = Cartesian2.add(p1, p2, cart2Scratch); sum = Cartesian2.multiplyByScalar(sum, f, sum); centroid2D = Cartesian2.add(centroid2D, sum, centroid2D); area += f; } var a = 1.0 / (area * 3.0); centroid2D = Cartesian2.multiplyByScalar(centroid2D, a, centroid2D); return tangentPlane.projectPointOntoEllipsoid(centroid2D, result); }; PolygonGeometryUpdater.prototype._isHidden = function (entity, polygon) { return ( !defined(polygon.hierarchy) || GeometryUpdater.prototype._isHidden.call(this, entity, polygon) ); }; PolygonGeometryUpdater.prototype._isOnTerrain = function (entity, polygon) { var onTerrain = GroundGeometryUpdater.prototype._isOnTerrain.call( this, entity, polygon ); var perPositionHeightProperty = polygon.perPositionHeight; var perPositionHeightEnabled = defined(perPositionHeightProperty) && (perPositionHeightProperty.isConstant ? perPositionHeightProperty.getValue(Iso8601.MINIMUM_VALUE) : true); return onTerrain && !perPositionHeightEnabled; }; PolygonGeometryUpdater.prototype._isDynamic = function (entity, polygon) { return ( !polygon.hierarchy.isConstant || // !Property.isConstant(polygon.height) || // !Property.isConstant(polygon.extrudedHeight) || // !Property.isConstant(polygon.granularity) || // !Property.isConstant(polygon.stRotation) || // !Property.isConstant(polygon.outlineWidth) || // !Property.isConstant(polygon.perPositionHeight) || // !Property.isConstant(polygon.closeTop) || // !Property.isConstant(polygon.closeBottom) || // !Property.isConstant(polygon.zIndex) || // !Property.isConstant(polygon.arcType) || // (this._onTerrain && !Property.isConstant(this._materialProperty) && !(this._materialProperty instanceof ColorMaterialProperty)) ); }; PolygonGeometryUpdater.prototype._setStaticOptions = function ( entity, polygon ) { var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; var options = this._options; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; var hierarchyValue = polygon.hierarchy.getValue(Iso8601.MINIMUM_VALUE); var heightValue = Property.getValueOrUndefined( polygon.height, Iso8601.MINIMUM_VALUE ); var heightReferenceValue = Property.getValueOrDefault( polygon.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( polygon.extrudedHeight, Iso8601.MINIMUM_VALUE ); var extrudedHeightReferenceValue = Property.getValueOrDefault( polygon.extrudedHeightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var perPositionHeightValue = Property.getValueOrDefault( polygon.perPositionHeight, Iso8601.MINIMUM_VALUE, false ); heightValue = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); var offsetAttribute; if (perPositionHeightValue) { if (defined(heightValue)) { heightValue = undefined; oneTimeWarning(heightAndPerPositionHeightWarning); } if ( heightReferenceValue !== HeightReference$1.NONE && perPositionHeightValue ) { heightValue = undefined; oneTimeWarning(heightReferenceAndPerPositionHeightWarning); } } else { if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); } options.polygonHierarchy = hierarchyValue; options.granularity = Property.getValueOrUndefined( polygon.granularity, Iso8601.MINIMUM_VALUE ); options.stRotation = Property.getValueOrUndefined( polygon.stRotation, Iso8601.MINIMUM_VALUE ); options.perPositionHeight = perPositionHeightValue; options.closeTop = Property.getValueOrDefault( polygon.closeTop, Iso8601.MINIMUM_VALUE, true ); options.closeBottom = Property.getValueOrDefault( polygon.closeBottom, Iso8601.MINIMUM_VALUE, true ); options.offsetAttribute = offsetAttribute; options.height = heightValue; options.arcType = Property.getValueOrDefault( polygon.arcType, Iso8601.MINIMUM_VALUE, ArcType$1.GEODESIC ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( PolygonGeometry.computeRectangle(options, scratchRectangle$2) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; PolygonGeometryUpdater.prototype._getIsClosed = function (options) { var height = options.height; var extrudedHeight = options.extrudedHeight; var isExtruded = defined(extrudedHeight) && extrudedHeight !== height; return ( !options.perPositionHeight && ((!isExtruded && height === 0) || (isExtruded && options.closeTop && options.closeBottom)) ); }; PolygonGeometryUpdater.DynamicGeometryUpdater = DyanmicPolygonGeometryUpdater; /** * @private */ function DyanmicPolygonGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DyanmicPolygonGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DyanmicPolygonGeometryUpdater.prototype.constructor = DyanmicPolygonGeometryUpdater; } DyanmicPolygonGeometryUpdater.prototype._isHidden = function ( entity, polygon, time ) { return ( !defined(this._options.polygonHierarchy) || DynamicGeometryUpdater$1.prototype._isHidden.call(this, entity, polygon, time) ); }; DyanmicPolygonGeometryUpdater.prototype._setOptions = function ( entity, polygon, time ) { var options = this._options; options.polygonHierarchy = Property.getValueOrUndefined( polygon.hierarchy, time ); var heightValue = Property.getValueOrUndefined(polygon.height, time); var heightReferenceValue = Property.getValueOrDefault( polygon.heightReference, time, HeightReference$1.NONE ); var extrudedHeightReferenceValue = Property.getValueOrDefault( polygon.extrudedHeightReference, time, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( polygon.extrudedHeight, time ); var perPositionHeightValue = Property.getValueOrUndefined( polygon.perPositionHeight, time ); heightValue = GroundGeometryUpdater.getGeometryHeight( heightValue, extrudedHeightReferenceValue ); var offsetAttribute; if (perPositionHeightValue) { if (defined(heightValue)) { heightValue = undefined; oneTimeWarning(heightAndPerPositionHeightWarning); } if ( heightReferenceValue !== HeightReference$1.NONE && perPositionHeightValue ) { heightValue = undefined; oneTimeWarning(heightReferenceAndPerPositionHeightWarning); } } else { if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); } options.granularity = Property.getValueOrUndefined(polygon.granularity, time); options.stRotation = Property.getValueOrUndefined(polygon.stRotation, time); options.perPositionHeight = Property.getValueOrUndefined( polygon.perPositionHeight, time ); options.closeTop = Property.getValueOrDefault(polygon.closeTop, time, true); options.closeBottom = Property.getValueOrDefault( polygon.closeBottom, time, true ); options.offsetAttribute = offsetAttribute; options.height = heightValue; options.arcType = Property.getValueOrDefault( polygon.arcType, time, ArcType$1.GEODESIC ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( PolygonGeometry.computeRectangle(options, scratchRectangle$2) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; var scratchColor$7 = new Color(); function PolylineVolumeGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.polylinePositions = undefined; this.shapePositions = undefined; this.cornerType = undefined; this.granularity = undefined; } /** * A {@link GeometryUpdater} for polyline volumes. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias PolylineVolumeGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function PolylineVolumeGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new PolylineVolumeGeometryOptions(entity), geometryPropertyName: "polylineVolume", observedPropertyNames: ["availability", "polylineVolume"], }); this._onEntityPropertyChanged( entity, "polylineVolume", entity.polylineVolume, undefined ); } if (defined(Object.create)) { PolylineVolumeGeometryUpdater.prototype = Object.create( GeometryUpdater.prototype ); PolylineVolumeGeometryUpdater.prototype.constructor = PolylineVolumeGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ PolylineVolumeGeometryUpdater.prototype.createFillGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes; var color; var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$7); } if (!defined(currentColor)) { currentColor = Color.WHITE; } color = ColorGeometryInstanceAttribute.fromColor(currentColor); attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: color, }; } else { attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, }; } return new GeometryInstance({ id: entity, geometry: new PolylineVolumeGeometry(this._options), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ PolylineVolumeGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$7 ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); return new GeometryInstance({ id: entity, geometry: new PolylineVolumeOutlineGeometry(this._options), attributes: { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), }, }); }; PolylineVolumeGeometryUpdater.prototype._isHidden = function ( entity, polylineVolume ) { return ( !defined(polylineVolume.positions) || !defined(polylineVolume.shape) || GeometryUpdater.prototype._isHidden.call(this, entity, polylineVolume) ); }; PolylineVolumeGeometryUpdater.prototype._isDynamic = function ( entity, polylineVolume ) { return ( !polylineVolume.positions.isConstant || // !polylineVolume.shape.isConstant || // !Property.isConstant(polylineVolume.granularity) || // !Property.isConstant(polylineVolume.outlineWidth) || // !Property.isConstant(polylineVolume.cornerType) ); }; PolylineVolumeGeometryUpdater.prototype._setStaticOptions = function ( entity, polylineVolume ) { var granularity = polylineVolume.granularity; var cornerType = polylineVolume.cornerType; var options = this._options; var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.polylinePositions = polylineVolume.positions.getValue( Iso8601.MINIMUM_VALUE, options.polylinePositions ); options.shapePositions = polylineVolume.shape.getValue( Iso8601.MINIMUM_VALUE, options.shape ); options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; options.cornerType = defined(cornerType) ? cornerType.getValue(Iso8601.MINIMUM_VALUE) : undefined; }; PolylineVolumeGeometryUpdater.DynamicGeometryUpdater = DynamicPolylineVolumeGeometryUpdater; /** * @private */ function DynamicPolylineVolumeGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicPolylineVolumeGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicPolylineVolumeGeometryUpdater.prototype.constructor = DynamicPolylineVolumeGeometryUpdater; } DynamicPolylineVolumeGeometryUpdater.prototype._isHidden = function ( entity, polylineVolume, time ) { var options = this._options; return ( !defined(options.polylinePositions) || !defined(options.shapePositions) || DynamicGeometryUpdater$1.prototype._isHidden.call( this, entity, polylineVolume, time ) ); }; DynamicPolylineVolumeGeometryUpdater.prototype._setOptions = function ( entity, polylineVolume, time ) { var options = this._options; options.polylinePositions = Property.getValueOrUndefined( polylineVolume.positions, time, options.polylinePositions ); options.shapePositions = Property.getValueOrUndefined( polylineVolume.shape, time ); options.granularity = Property.getValueOrUndefined( polylineVolume.granularity, time ); options.cornerType = Property.getValueOrUndefined( polylineVolume.cornerType, time ); }; var scratchColor$6 = new Color(); var defaultOffset$3 = Cartesian3.ZERO; var offsetScratch$3 = new Cartesian3(); var scratchRectangle$1 = new Rectangle(); var scratchCenterRect = new Rectangle(); var scratchCarto = new Cartographic(); function RectangleGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.rectangle = undefined; this.height = undefined; this.extrudedHeight = undefined; this.granularity = undefined; this.stRotation = undefined; this.rotation = undefined; this.offsetAttribute = undefined; } /** * A {@link GeometryUpdater} for rectangles. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias RectangleGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function RectangleGeometryUpdater(entity, scene) { GroundGeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new RectangleGeometryOptions(entity), geometryPropertyName: "rectangle", observedPropertyNames: ["availability", "rectangle"], }); this._onEntityPropertyChanged( entity, "rectangle", entity.rectangle, undefined ); } if (defined(Object.create)) { RectangleGeometryUpdater.prototype = Object.create( GroundGeometryUpdater.prototype ); RectangleGeometryUpdater.prototype.constructor = RectangleGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ RectangleGeometryUpdater.prototype.createFillGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( this._distanceDisplayConditionProperty.getValue(time) ), offset: undefined, color: undefined, }; if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$6); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$3, offsetScratch$3 ) ); } return new GeometryInstance({ id: entity, geometry: new RectangleGeometry(this._options), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ RectangleGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$6 ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var attributes = { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), offset: undefined, }; if (defined(this._options.offsetAttribute)) { attributes.offset = OffsetGeometryInstanceAttribute.fromCartesian3( Property.getValueOrDefault( this._terrainOffsetProperty, time, defaultOffset$3, offsetScratch$3 ) ); } return new GeometryInstance({ id: entity, geometry: new RectangleOutlineGeometry(this._options), attributes: attributes, }); }; RectangleGeometryUpdater.prototype._computeCenter = function (time, result) { var rect = Property.getValueOrUndefined( this._entity.rectangle.coordinates, time, scratchCenterRect ); if (!defined(rect)) { return; } var center = Rectangle.center(rect, scratchCarto); return Cartographic.toCartesian(center, Ellipsoid.WGS84, result); }; RectangleGeometryUpdater.prototype._isHidden = function (entity, rectangle) { return ( !defined(rectangle.coordinates) || GeometryUpdater.prototype._isHidden.call(this, entity, rectangle) ); }; RectangleGeometryUpdater.prototype._isDynamic = function (entity, rectangle) { return ( !rectangle.coordinates.isConstant || // !Property.isConstant(rectangle.height) || // !Property.isConstant(rectangle.extrudedHeight) || // !Property.isConstant(rectangle.granularity) || // !Property.isConstant(rectangle.stRotation) || // !Property.isConstant(rectangle.rotation) || // !Property.isConstant(rectangle.outlineWidth) || // !Property.isConstant(rectangle.zIndex) || // (this._onTerrain && !Property.isConstant(this._materialProperty) && !(this._materialProperty instanceof ColorMaterialProperty)) ); }; RectangleGeometryUpdater.prototype._setStaticOptions = function ( entity, rectangle ) { var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; var heightValue = Property.getValueOrUndefined( rectangle.height, Iso8601.MINIMUM_VALUE ); var heightReferenceValue = Property.getValueOrDefault( rectangle.heightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( rectangle.extrudedHeight, Iso8601.MINIMUM_VALUE ); var extrudedHeightReferenceValue = Property.getValueOrDefault( rectangle.extrudedHeightReference, Iso8601.MINIMUM_VALUE, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } var options = this._options; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.rectangle = rectangle.coordinates.getValue( Iso8601.MINIMUM_VALUE, options.rectangle ); options.granularity = Property.getValueOrUndefined( rectangle.granularity, Iso8601.MINIMUM_VALUE ); options.stRotation = Property.getValueOrUndefined( rectangle.stRotation, Iso8601.MINIMUM_VALUE ); options.rotation = Property.getValueOrUndefined( rectangle.rotation, Iso8601.MINIMUM_VALUE ); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( RectangleGeometry.computeRectangle(options, scratchRectangle$1) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; RectangleGeometryUpdater.DynamicGeometryUpdater = DynamicRectangleGeometryUpdater; /** * @private */ function DynamicRectangleGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicRectangleGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicRectangleGeometryUpdater.prototype.constructor = DynamicRectangleGeometryUpdater; } DynamicRectangleGeometryUpdater.prototype._isHidden = function ( entity, rectangle, time ) { return ( !defined(this._options.rectangle) || DynamicGeometryUpdater$1.prototype._isHidden.call( this, entity, rectangle, time ) ); }; DynamicRectangleGeometryUpdater.prototype._setOptions = function ( entity, rectangle, time ) { var options = this._options; var heightValue = Property.getValueOrUndefined(rectangle.height, time); var heightReferenceValue = Property.getValueOrDefault( rectangle.heightReference, time, HeightReference$1.NONE ); var extrudedHeightValue = Property.getValueOrUndefined( rectangle.extrudedHeight, time ); var extrudedHeightReferenceValue = Property.getValueOrDefault( rectangle.extrudedHeightReference, time, HeightReference$1.NONE ); if (defined(extrudedHeightValue) && !defined(heightValue)) { heightValue = 0; } options.rectangle = Property.getValueOrUndefined( rectangle.coordinates, time, options.rectangle ); options.granularity = Property.getValueOrUndefined( rectangle.granularity, time ); options.stRotation = Property.getValueOrUndefined(rectangle.stRotation, time); options.rotation = Property.getValueOrUndefined(rectangle.rotation, time); options.offsetAttribute = GroundGeometryUpdater.computeGeometryOffsetAttribute( heightValue, heightReferenceValue, extrudedHeightValue, extrudedHeightReferenceValue ); options.height = GroundGeometryUpdater.getGeometryHeight( heightValue, heightReferenceValue ); extrudedHeightValue = GroundGeometryUpdater.getGeometryExtrudedHeight( extrudedHeightValue, extrudedHeightReferenceValue ); if (extrudedHeightValue === GroundGeometryUpdater.CLAMP_TO_GROUND) { extrudedHeightValue = ApproximateTerrainHeights.getMinimumMaximumHeights( RectangleGeometry.computeRectangle(options, scratchRectangle$1) ).minimumTerrainHeight; } options.extrudedHeight = extrudedHeightValue; }; var colorScratch$5 = new Color(); var distanceDisplayConditionScratch$7 = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition$6 = new DistanceDisplayCondition(); var defaultOffset$2 = Cartesian3.ZERO; var offsetScratch$2 = new Cartesian3(); function Batch$5( primitives, translucent, appearanceType, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows ) { this.translucent = translucent; this.appearanceType = appearanceType; this.depthFailAppearanceType = depthFailAppearanceType; this.depthFailMaterialProperty = depthFailMaterialProperty; this.depthFailMaterial = undefined; this.closed = closed; this.shadows = shadows; this.primitives = primitives; this.createPrimitive = false; this.waitingOnCreate = false; this.primitive = undefined; this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.updaters = new AssociativeArray(); this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); this.itemsToRemove = []; this.invalidated = false; var removeMaterialSubscription; if (defined(depthFailMaterialProperty)) { removeMaterialSubscription = depthFailMaterialProperty.definitionChanged.addEventListener( Batch$5.prototype.onMaterialChanged, this ); } this.removeMaterialSubscription = removeMaterialSubscription; } Batch$5.prototype.onMaterialChanged = function () { this.invalidated = true; }; Batch$5.prototype.isMaterial = function (updater) { var material = this.depthFailMaterialProperty; var updaterMaterial = updater.depthFailMaterialProperty; if (updaterMaterial === material) { return true; } if (defined(material)) { return material.equals(updaterMaterial); } return false; }; Batch$5.prototype.add = function (updater, instance) { var id = updater.id; this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); if ( !updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } }; Batch$5.prototype.remove = function (updater) { var id = updater.id; this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); this.showsUpdated.remove(id); } return true; } return false; }; Batch$5.prototype.update = function (time) { var isUpdated = true; var removedCount = 0; var primitive = this.primitive; var primitives = this.primitives; var i; if (this.createPrimitive) { var geometries = this.geometry.values; var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { primitives.remove(primitive); } } var depthFailAppearance; if (defined(this.depthFailAppearanceType)) { if (defined(this.depthFailMaterialProperty)) { this.depthFailMaterial = MaterialProperty.getValue( time, this.depthFailMaterialProperty, this.depthFailMaterial ); } depthFailAppearance = new this.depthFailAppearanceType({ material: this.depthFailMaterial, translucent: this.translucent, closed: this.closed, }); } primitive = new Primitive({ show: false, asynchronous: true, geometryInstances: geometries.slice(), appearance: new this.appearanceType({ translucent: this.translucent, closed: this.closed, }), depthFailAppearance: depthFailAppearance, shadows: this.shadows, }); primitives.add(primitive); isUpdated = false; } else { if (defined(primitive)) { primitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; this.waitingOnCreate = true; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } if ( defined(this.depthFailAppearanceType) && !(this.depthFailMaterialProperty instanceof ColorMaterialProperty) ) { this.depthFailMaterial = MaterialProperty.getValue( time, this.depthFailMaterialProperty, this.depthFailMaterial ); this.primitive.depthFailAppearance.material = this.depthFailMaterial; } var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; var waitingOnCreate = this.waitingOnCreate; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } if (!updater.fillMaterialProperty.isConstant || waitingOnCreate) { var colorProperty = updater.fillMaterialProperty.color; var resultColor = Property.getValueOrDefault( colorProperty, time, Color.WHITE, colorScratch$5 ); if (!Color.equals(attributes._lastColor, resultColor)) { attributes._lastColor = Color.clone( resultColor, attributes._lastColor ); attributes.color = ColorGeometryInstanceAttribute.toValue( resultColor, attributes.color ); if ( (this.translucent && attributes.color[3] === 255) || (!this.translucent && attributes.color[3] !== 255) ) { this.itemsToRemove[removedCount++] = updater; } } } if ( defined(this.depthFailAppearanceType) && updater.depthFailMaterialProperty instanceof ColorMaterialProperty && (!updater.depthFailMaterialProperty.isConstant || waitingOnCreate) ) { var depthFailColorProperty = updater.depthFailMaterialProperty.color; var depthColor = Property.getValueOrDefault( depthFailColorProperty, time, Color.WHITE, colorScratch$5 ); if (!Color.equals(attributes._lastDepthFailColor, depthColor)) { attributes._lastDepthFailColor = Color.clone( depthColor, attributes._lastDepthFailColor ); attributes.depthFailColor = ColorGeometryInstanceAttribute.toValue( depthColor, attributes.depthFailColor ); } } var show = updater.entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition$6, distanceDisplayConditionScratch$7 ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } var offsetProperty = updater.terrainOffsetProperty; if (!Property.isConstant(offsetProperty)) { var offset = Property.getValueOrDefault( offsetProperty, time, defaultOffset$2, offsetScratch$2 ); if (!Cartesian3.equals(offset, attributes._lastOffset)) { attributes._lastOffset = Cartesian3.clone( offset, attributes._lastOffset ); attributes.offset = OffsetGeometryInstanceAttribute.toValue( offset, attributes.offset ); } } } this.updateShows(primitive); this.waitingOnCreate = false; } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } this.itemsToRemove.length = removedCount; return isUpdated; }; Batch$5.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = updater.entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch$5.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch$5.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var attributes = primitive.getGeometryInstanceAttributes(updater.entity); if ( !defined(attributes) || !defined(attributes.boundingSphere) || // (defined(attributes.show) && attributes.show[0] === 0) ) { return BoundingSphereState$1.FAILED; } attributes.boundingSphere.clone(result); return BoundingSphereState$1.DONE; }; Batch$5.prototype.destroy = function () { var primitive = this.primitive; var primitives = this.primitives; if (defined(primitive)) { primitives.remove(primitive); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); } if (defined(this.removeMaterialSubscription)) { this.removeMaterialSubscription(); } }; /** * @private */ function StaticGeometryColorBatch( primitives, appearanceType, depthFailAppearanceType, closed, shadows ) { this._solidItems = []; this._translucentItems = []; this._primitives = primitives; this._appearanceType = appearanceType; this._depthFailAppearanceType = depthFailAppearanceType; this._closed = closed; this._shadows = shadows; } StaticGeometryColorBatch.prototype.add = function (time, updater) { var items; var translucent; var instance = updater.createFillGeometryInstance(time); if (instance.attributes.color.value[3] === 255) { items = this._solidItems; translucent = false; } else { items = this._translucentItems; translucent = true; } var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.isMaterial(updater)) { item.add(updater, instance); return; } } var batch = new Batch$5( this._primitives, translucent, this._appearanceType, this._depthFailAppearanceType, updater.depthFailMaterialProperty, this._closed, this._shadows ); batch.add(updater, instance); items.push(batch); }; function removeItem(items, updater) { var length = items.length; for (var i = length - 1; i >= 0; i--) { var item = items[i]; if (item.remove(updater)) { if (item.updaters.length === 0) { items.splice(i, 1); item.destroy(); } return true; } } return false; } StaticGeometryColorBatch.prototype.remove = function (updater) { if (!removeItem(this._solidItems, updater)) { removeItem(this._translucentItems, updater); } }; function moveItems(batch, items, time) { var itemsMoved = false; var length = items.length; for (var i = 0; i < length; ++i) { var item = items[i]; var itemsToRemove = item.itemsToRemove; var itemsToMoveLength = itemsToRemove.length; if (itemsToMoveLength > 0) { for (i = 0; i < itemsToMoveLength; i++) { var updater = itemsToRemove[i]; item.remove(updater); batch.add(time, updater); itemsMoved = true; } } } return itemsMoved; } function updateItems(batch, items, time, isUpdated) { var length = items.length; var i; for (i = length - 1; i >= 0; i--) { var item = items[i]; if (item.invalidated) { items.splice(i, 1); var updaters = item.updaters.values; var updatersLength = updaters.length; for (var h = 0; h < updatersLength; h++) { batch.add(time, updaters[h]); } item.destroy(); } } length = items.length; for (i = 0; i < length; ++i) { isUpdated = items[i].update(time) && isUpdated; } return isUpdated; } StaticGeometryColorBatch.prototype.update = function (time) { //Perform initial update var isUpdated = updateItems(this, this._solidItems, time, true); isUpdated = updateItems(this, this._translucentItems, time, isUpdated) && isUpdated; //If any items swapped between solid/translucent, we need to //move them between batches var solidsMoved = moveItems(this, this._solidItems, time); var translucentsMoved = moveItems(this, this._translucentItems, time); //If we moved anything around, we need to re-build the primitive if (solidsMoved || translucentsMoved) { isUpdated = updateItems(this, this._solidItems, time, isUpdated) && isUpdated; isUpdated = updateItems(this, this._translucentItems, time, isUpdated) && isUpdated; } return isUpdated; }; function getBoundingSphere(items, updater, result) { var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.contains(updater)) { return item.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; } StaticGeometryColorBatch.prototype.getBoundingSphere = function ( updater, result ) { var boundingSphere = getBoundingSphere(this._solidItems, updater, result); if (boundingSphere === BoundingSphereState$1.FAILED) { return getBoundingSphere(this._translucentItems, updater, result); } return boundingSphere; }; function removeAllPrimitives(items) { var length = items.length; for (var i = 0; i < length; i++) { items[i].destroy(); } items.length = 0; } StaticGeometryColorBatch.prototype.removeAllPrimitives = function () { removeAllPrimitives(this._solidItems); removeAllPrimitives(this._translucentItems); }; var distanceDisplayConditionScratch$6 = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition$5 = new DistanceDisplayCondition(); var defaultOffset$1 = Cartesian3.ZERO; var offsetScratch$1 = new Cartesian3(); function Batch$4( primitives, appearanceType, materialProperty, depthFailAppearanceType, depthFailMaterialProperty, closed, shadows ) { this.primitives = primitives; this.appearanceType = appearanceType; this.materialProperty = materialProperty; this.depthFailAppearanceType = depthFailAppearanceType; this.depthFailMaterialProperty = depthFailMaterialProperty; this.closed = closed; this.shadows = shadows; this.updaters = new AssociativeArray(); this.createPrimitive = true; this.primitive = undefined; this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.material = undefined; this.depthFailMaterial = undefined; this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.invalidated = false; this.removeMaterialSubscription = materialProperty.definitionChanged.addEventListener( Batch$4.prototype.onMaterialChanged, this ); this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); } Batch$4.prototype.onMaterialChanged = function () { this.invalidated = true; }; Batch$4.prototype.isMaterial = function (updater) { var material = this.materialProperty; var updaterMaterial = updater.fillMaterialProperty; var depthFailMaterial = this.depthFailMaterialProperty; var updaterDepthFailMaterial = updater.depthFailMaterialProperty; if ( updaterMaterial === material && updaterDepthFailMaterial === depthFailMaterial ) { return true; } var equals = defined(material) && material.equals(updaterMaterial); equals = ((!defined(depthFailMaterial) && !defined(updaterDepthFailMaterial)) || (defined(depthFailMaterial) && depthFailMaterial.equals(updaterDepthFailMaterial))) && equals; return equals; }; Batch$4.prototype.add = function (time, updater) { var id = updater.id; this.updaters.set(id, updater); this.geometry.set(id, updater.createFillGeometryInstance(time)); if ( !updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } this.createPrimitive = true; }; Batch$4.prototype.remove = function (updater) { var id = updater.id; this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); this.showsUpdated.remove(id); } return true; } return false; }; var colorScratch$4 = new Color(); Batch$4.prototype.update = function (time) { var isUpdated = true; var primitive = this.primitive; var primitives = this.primitives; var geometries = this.geometry.values; var i; if (this.createPrimitive) { var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { primitives.remove(primitive); } } this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); var depthFailAppearance; if (defined(this.depthFailMaterialProperty)) { this.depthFailMaterial = MaterialProperty.getValue( time, this.depthFailMaterialProperty, this.depthFailMaterial ); depthFailAppearance = new this.depthFailAppearanceType({ material: this.depthFailMaterial, translucent: this.depthFailMaterial.isTranslucent(), closed: this.closed, }); } primitive = new Primitive({ show: false, asynchronous: true, geometryInstances: geometries.slice(), appearance: new this.appearanceType({ material: this.material, translucent: this.material.isTranslucent(), closed: this.closed, }), depthFailAppearance: depthFailAppearance, shadows: this.shadows, }); primitives.add(primitive); isUpdated = false; } else { if (defined(primitive)) { primitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); this.primitive.appearance.material = this.material; if ( defined(this.depthFailAppearanceType) && !(this.depthFailMaterialProperty instanceof ColorMaterialProperty) ) { this.depthFailMaterial = MaterialProperty.getValue( time, this.depthFailMaterialProperty, this.depthFailMaterial ); this.primitive.depthFailAppearance.material = this.depthFailMaterial; } var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } if ( defined(this.depthFailAppearanceType) && this.depthFailMaterialProperty instanceof ColorMaterialProperty && !updater.depthFailMaterialProperty.isConstant ) { var depthFailColorProperty = updater.depthFailMaterialProperty.color; var depthFailColor = Property.getValueOrDefault( depthFailColorProperty, time, Color.WHITE, colorScratch$4 ); if (!Color.equals(attributes._lastDepthFailColor, depthFailColor)) { attributes._lastDepthFailColor = Color.clone( depthFailColor, attributes._lastDepthFailColor ); attributes.depthFailColor = ColorGeometryInstanceAttribute.toValue( depthFailColor, attributes.depthFailColor ); } } var show = entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition$5, distanceDisplayConditionScratch$6 ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } var offsetProperty = updater.terrainOffsetProperty; if (!Property.isConstant(offsetProperty)) { var offset = Property.getValueOrDefault( offsetProperty, time, defaultOffset$1, offsetScratch$1 ); if (!Cartesian3.equals(offset, attributes._lastOffset)) { attributes._lastOffset = Cartesian3.clone( offset, attributes._lastOffset ); attributes.offset = OffsetGeometryInstanceAttribute.toValue( offset, attributes.offset ); } } } this.updateShows(primitive); } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } return isUpdated; }; Batch$4.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch$4.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch$4.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var attributes = primitive.getGeometryInstanceAttributes(updater.entity); if ( !defined(attributes) || !defined(attributes.boundingSphere) || (defined(attributes.show) && attributes.show[0] === 0) ) { return BoundingSphereState$1.FAILED; } attributes.boundingSphere.clone(result); return BoundingSphereState$1.DONE; }; Batch$4.prototype.destroy = function () { var primitive = this.primitive; var primitives = this.primitives; if (defined(primitive)) { primitives.remove(primitive); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); } this.removeMaterialSubscription(); }; /** * @private */ function StaticGeometryPerMaterialBatch( primitives, appearanceType, depthFailAppearanceType, closed, shadows ) { this._items = []; this._primitives = primitives; this._appearanceType = appearanceType; this._depthFailAppearanceType = depthFailAppearanceType; this._closed = closed; this._shadows = shadows; } StaticGeometryPerMaterialBatch.prototype.add = function (time, updater) { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.isMaterial(updater)) { item.add(time, updater); return; } } var batch = new Batch$4( this._primitives, this._appearanceType, updater.fillMaterialProperty, this._depthFailAppearanceType, updater.depthFailMaterialProperty, this._closed, this._shadows ); batch.add(time, updater); items.push(batch); }; StaticGeometryPerMaterialBatch.prototype.remove = function (updater) { var items = this._items; var length = items.length; for (var i = length - 1; i >= 0; i--) { var item = items[i]; if (item.remove(updater)) { if (item.updaters.length === 0) { items.splice(i, 1); item.destroy(); } break; } } }; StaticGeometryPerMaterialBatch.prototype.update = function (time) { var i; var items = this._items; var length = items.length; for (i = length - 1; i >= 0; i--) { var item = items[i]; if (item.invalidated) { items.splice(i, 1); var updaters = item.updaters.values; var updatersLength = updaters.length; for (var h = 0; h < updatersLength; h++) { this.add(time, updaters[h]); } item.destroy(); } } var isUpdated = true; for (i = 0; i < items.length; i++) { isUpdated = items[i].update(time) && isUpdated; } return isUpdated; }; StaticGeometryPerMaterialBatch.prototype.getBoundingSphere = function ( updater, result ) { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.contains(updater)) { return item.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; }; StaticGeometryPerMaterialBatch.prototype.removeAllPrimitives = function () { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { items[i].destroy(); } this._items.length = 0; }; var colorScratch$3 = new Color(); var distanceDisplayConditionScratch$5 = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition$4 = new DistanceDisplayCondition(); function Batch$3(primitives, classificationType, color, zIndex) { this.primitives = primitives; this.zIndex = zIndex; this.classificationType = classificationType; this.color = color; this.createPrimitive = false; this.waitingOnCreate = false; this.primitive = undefined; this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.updaters = new AssociativeArray(); this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); this.itemsToRemove = []; this.isDirty = false; this.rectangleCollisionCheck = new RectangleCollisionChecker(); } Batch$3.prototype.overlapping = function (rectangle) { return this.rectangleCollisionCheck.collides(rectangle); }; Batch$3.prototype.add = function (updater, instance) { var id = updater.id; this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); this.rectangleCollisionCheck.insert(id, instance.geometry.rectangle); if ( !updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } }; Batch$3.prototype.remove = function (updater) { var id = updater.id; var geometryInstance = this.geometry.get(id); this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.rectangleCollisionCheck.remove( id, geometryInstance.geometry.rectangle ); this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); this.showsUpdated.remove(id); } return true; } return false; }; Batch$3.prototype.update = function (time) { var isUpdated = true; var removedCount = 0; var primitive = this.primitive; var primitives = this.primitives; var i; if (this.createPrimitive) { var geometries = this.geometry.values; var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { primitives.remove(primitive); } } primitive = new GroundPrimitive({ show: false, asynchronous: true, geometryInstances: geometries.slice(), classificationType: this.classificationType, }); primitives.add(primitive, this.zIndex); isUpdated = false; } else { if (defined(primitive)) { primitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; this.waitingOnCreate = true; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; var waitingOnCreate = this.waitingOnCreate; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } if (!updater.fillMaterialProperty.isConstant || waitingOnCreate) { var colorProperty = updater.fillMaterialProperty.color; var fillColor = Property.getValueOrDefault( colorProperty, time, Color.WHITE, colorScratch$3 ); if (!Color.equals(attributes._lastColor, fillColor)) { attributes._lastColor = Color.clone(fillColor, attributes._lastColor); attributes.color = ColorGeometryInstanceAttribute.toValue( fillColor, attributes.color ); } } var show = updater.entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition$4, distanceDisplayConditionScratch$5 ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } } this.updateShows(primitive); this.waitingOnCreate = false; } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } this.itemsToRemove.length = removedCount; return isUpdated; }; Batch$3.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = updater.entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch$3.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch$3.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var bs = primitive.getBoundingSphere(updater.entity); if (!defined(bs)) { return BoundingSphereState$1.FAILED; } bs.clone(result); return BoundingSphereState$1.DONE; }; Batch$3.prototype.removeAllPrimitives = function () { var primitives = this.primitives; var primitive = this.primitive; if (defined(primitive)) { primitives.remove(primitive); this.primitive = undefined; this.geometry.removeAll(); this.updaters.removeAll(); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } }; /** * @private */ function StaticGroundGeometryColorBatch(primitives, classificationType) { this._batches = []; this._primitives = primitives; this._classificationType = classificationType; } StaticGroundGeometryColorBatch.prototype.add = function (time, updater) { var instance = updater.createFillGeometryInstance(time); var batches = this._batches; var zIndex = Property.getValueOrDefault(updater.zIndex, 0); var batch; var length = batches.length; for (var i = 0; i < length; ++i) { var item = batches[i]; if ( item.zIndex === zIndex && !item.overlapping(instance.geometry.rectangle) ) { batch = item; break; } } if (!defined(batch)) { batch = new Batch$3( this._primitives, this._classificationType, instance.attributes.color.value, zIndex ); batches.push(batch); } batch.add(updater, instance); return batch; }; StaticGroundGeometryColorBatch.prototype.remove = function (updater) { var batches = this._batches; var count = batches.length; for (var i = 0; i < count; ++i) { if (batches[i].remove(updater)) { return; } } }; StaticGroundGeometryColorBatch.prototype.update = function (time) { var i; var updater; //Perform initial update var isUpdated = true; var batches = this._batches; var batchCount = batches.length; for (i = 0; i < batchCount; ++i) { isUpdated = batches[i].update(time) && isUpdated; } //If any items swapped between batches we need to move them for (i = 0; i < batchCount; ++i) { var oldBatch = batches[i]; var itemsToRemove = oldBatch.itemsToRemove; var itemsToMoveLength = itemsToRemove.length; for (var j = 0; j < itemsToMoveLength; j++) { updater = itemsToRemove[j]; oldBatch.remove(updater); var newBatch = this.add(time, updater); oldBatch.isDirty = true; newBatch.isDirty = true; } } //If we moved anything around, we need to re-build the primitive and remove empty batches for (i = batchCount - 1; i >= 0; --i) { var batch = batches[i]; if (batch.isDirty) { isUpdated = batches[i].update(time) && isUpdated; batch.isDirty = false; } if (batch.geometry.length === 0) { batches.splice(i, 1); } } return isUpdated; }; StaticGroundGeometryColorBatch.prototype.getBoundingSphere = function ( updater, result ) { var batches = this._batches; var batchCount = batches.length; for (var i = 0; i < batchCount; ++i) { var batch = batches[i]; if (batch.contains(updater)) { return batch.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; }; StaticGroundGeometryColorBatch.prototype.removeAllPrimitives = function () { var batches = this._batches; var batchCount = batches.length; for (var i = 0; i < batchCount; ++i) { batches[i].removeAllPrimitives(); } }; var distanceDisplayConditionScratch$4 = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition$3 = new DistanceDisplayCondition(); // Encapsulates a Primitive and all the entities that it represents. function Batch$2( primitives, classificationType, appearanceType, materialProperty, usingSphericalTextureCoordinates, zIndex ) { this.primitives = primitives; // scene level primitive collection this.classificationType = classificationType; this.appearanceType = appearanceType; this.materialProperty = materialProperty; this.updaters = new AssociativeArray(); this.createPrimitive = true; this.primitive = undefined; // a GroundPrimitive encapsulating all the entities this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.material = undefined; this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.invalidated = false; this.removeMaterialSubscription = materialProperty.definitionChanged.addEventListener( Batch$2.prototype.onMaterialChanged, this ); this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); this.usingSphericalTextureCoordinates = usingSphericalTextureCoordinates; this.zIndex = zIndex; this.rectangleCollisionCheck = new RectangleCollisionChecker(); } Batch$2.prototype.onMaterialChanged = function () { this.invalidated = true; }; Batch$2.prototype.overlapping = function (rectangle) { return this.rectangleCollisionCheck.collides(rectangle); }; // Check if the given updater's material is compatible with this batch Batch$2.prototype.isMaterial = function (updater) { var material = this.materialProperty; var updaterMaterial = updater.fillMaterialProperty; if ( updaterMaterial === material || (updaterMaterial instanceof ColorMaterialProperty && material instanceof ColorMaterialProperty) ) { return true; } return defined(material) && material.equals(updaterMaterial); }; Batch$2.prototype.add = function (time, updater, geometryInstance) { var id = updater.id; this.updaters.set(id, updater); this.geometry.set(id, geometryInstance); this.rectangleCollisionCheck.insert(id, geometryInstance.geometry.rectangle); // Updaters with dynamic attributes must be tracked separately, may exit the batch if ( !updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; // Listen for show changes. These will be synchronized in updateShows. this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } this.createPrimitive = true; }; Batch$2.prototype.remove = function (updater) { var id = updater.id; var geometryInstance = this.geometry.get(id); this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.rectangleCollisionCheck.remove( id, geometryInstance.geometry.rectangle ); this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); } return true; } return false; }; Batch$2.prototype.update = function (time) { var isUpdated = true; var primitive = this.primitive; var primitives = this.primitives; var geometries = this.geometry.values; var i; if (this.createPrimitive) { var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { // Keep a handle to the old primitive so it can be removed when the updated version is ready. if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { // For if the new primitive changes again before it is ready. primitives.remove(primitive); } } this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); primitive = new GroundPrimitive({ show: false, asynchronous: true, geometryInstances: geometries.slice(), appearance: new this.appearanceType({ material: this.material, // translucent and closed properties overridden }), classificationType: this.classificationType, }); primitives.add(primitive, this.zIndex); isUpdated = false; } else { if (defined(primitive)) { primitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); this.primitive.appearance.material = this.material; var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition$3, distanceDisplayConditionScratch$4 ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } } this.updateShows(primitive); } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } return isUpdated; }; Batch$2.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch$2.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch$2.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var attributes = primitive.getGeometryInstanceAttributes(updater.entity); if ( !defined(attributes) || !defined(attributes.boundingSphere) || (defined(attributes.show) && attributes.show[0] === 0) ) { return BoundingSphereState$1.FAILED; } attributes.boundingSphere.clone(result); return BoundingSphereState$1.DONE; }; Batch$2.prototype.destroy = function () { var primitive = this.primitive; var primitives = this.primitives; if (defined(primitive)) { primitives.remove(primitive); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); } this.removeMaterialSubscription(); }; /** * @private */ function StaticGroundGeometryPerMaterialBatch( primitives, classificationType, appearanceType ) { this._items = []; this._primitives = primitives; this._classificationType = classificationType; this._appearanceType = appearanceType; } StaticGroundGeometryPerMaterialBatch.prototype.add = function (time, updater) { var items = this._items; var length = items.length; var geometryInstance = updater.createFillGeometryInstance(time); var usingSphericalTextureCoordinates = ShadowVolumeAppearance.shouldUseSphericalCoordinates( geometryInstance.geometry.rectangle ); var zIndex = Property.getValueOrDefault(updater.zIndex, 0); // Check if the Entity represented by the updater can be placed in an existing batch. Requirements: // * compatible material (same material or same color) // * same type of texture coordinates (spherical vs. planar) // * conservatively non-overlapping with any entities in the existing batch for (var i = 0; i < length; ++i) { var item = items[i]; if ( item.isMaterial(updater) && item.usingSphericalTextureCoordinates === usingSphericalTextureCoordinates && item.zIndex === zIndex && !item.overlapping(geometryInstance.geometry.rectangle) ) { item.add(time, updater, geometryInstance); return; } } // If a compatible batch wasn't found, create a new batch. var batch = new Batch$2( this._primitives, this._classificationType, this._appearanceType, updater.fillMaterialProperty, usingSphericalTextureCoordinates, zIndex ); batch.add(time, updater, geometryInstance); items.push(batch); }; StaticGroundGeometryPerMaterialBatch.prototype.remove = function (updater) { var items = this._items; var length = items.length; for (var i = length - 1; i >= 0; i--) { var item = items[i]; if (item.remove(updater)) { if (item.updaters.length === 0) { items.splice(i, 1); item.destroy(); } break; } } }; StaticGroundGeometryPerMaterialBatch.prototype.update = function (time) { var i; var items = this._items; var length = items.length; for (i = length - 1; i >= 0; i--) { var item = items[i]; if (item.invalidated) { items.splice(i, 1); var updaters = item.updaters.values; var updatersLength = updaters.length; for (var h = 0; h < updatersLength; h++) { this.add(time, updaters[h]); } item.destroy(); } } var isUpdated = true; for (i = 0; i < items.length; i++) { isUpdated = items[i].update(time) && isUpdated; } return isUpdated; }; StaticGroundGeometryPerMaterialBatch.prototype.getBoundingSphere = function ( updater, result ) { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.contains(updater)) { return item.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; }; StaticGroundGeometryPerMaterialBatch.prototype.removeAllPrimitives = function () { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { items[i].destroy(); } this._items.length = 0; }; var colorScratch$2 = new Color(); var distanceDisplayConditionScratch$3 = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition$2 = new DistanceDisplayCondition(); var defaultOffset = Cartesian3.ZERO; var offsetScratch = new Cartesian3(); function Batch$1(primitives, translucent, width, shadows) { this.translucent = translucent; this.width = width; this.shadows = shadows; this.primitives = primitives; this.createPrimitive = false; this.waitingOnCreate = false; this.primitive = undefined; this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.updaters = new AssociativeArray(); this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.itemsToRemove = []; this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); } Batch$1.prototype.add = function (updater, instance) { var id = updater.id; this.createPrimitive = true; this.geometry.set(id, instance); this.updaters.set(id, updater); if ( !updater.hasConstantOutline || !updater.outlineColorProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) || !Property.isConstant(updater.terrainOffsetProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } }; Batch$1.prototype.remove = function (updater) { var id = updater.id; this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); this.showsUpdated.remove(id); } return true; } return false; }; Batch$1.prototype.update = function (time) { var isUpdated = true; var removedCount = 0; var primitive = this.primitive; var primitives = this.primitives; var i; if (this.createPrimitive) { var geometries = this.geometry.values; var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { primitives.remove(primitive); } } primitive = new Primitive({ show: false, asynchronous: true, geometryInstances: geometries.slice(), appearance: new PerInstanceColorAppearance({ flat: true, translucent: this.translucent, renderState: { lineWidth: this.width, }, }), shadows: this.shadows, }); primitives.add(primitive); isUpdated = false; } else { if (defined(primitive)) { primitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; this.waitingOnCreate = true; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { primitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; var waitingOnCreate = this.waitingOnCreate; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } if (!updater.outlineColorProperty.isConstant || waitingOnCreate) { var outlineColorProperty = updater.outlineColorProperty; var outlineColor = Property.getValueOrDefault( outlineColorProperty, time, Color.WHITE, colorScratch$2 ); if (!Color.equals(attributes._lastColor, outlineColor)) { attributes._lastColor = Color.clone( outlineColor, attributes._lastColor ); attributes.color = ColorGeometryInstanceAttribute.toValue( outlineColor, attributes.color ); if ( (this.translucent && attributes.color[3] === 255) || (!this.translucent && attributes.color[3] !== 255) ) { this.itemsToRemove[removedCount++] = updater; } } } var show = updater.entity.isShowing && (updater.hasConstantOutline || updater.isOutlineVisible(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition$2, distanceDisplayConditionScratch$3 ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } var offsetProperty = updater.terrainOffsetProperty; if (!Property.isConstant(offsetProperty)) { var offset = Property.getValueOrDefault( offsetProperty, time, defaultOffset, offsetScratch ); if (!Cartesian3.equals(offset, attributes._lastOffset)) { attributes._lastOffset = Cartesian3.clone( offset, attributes._lastOffset ); attributes.offset = OffsetGeometryInstanceAttribute.toValue( offset, attributes.offset ); } } } this.updateShows(primitive); this.waitingOnCreate = false; } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } this.itemsToRemove.length = removedCount; return isUpdated; }; Batch$1.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = updater.entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch$1.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch$1.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var attributes = primitive.getGeometryInstanceAttributes(updater.entity); if ( !defined(attributes) || !defined(attributes.boundingSphere) || // (defined(attributes.show) && attributes.show[0] === 0) ) { return BoundingSphereState$1.FAILED; } attributes.boundingSphere.clone(result); return BoundingSphereState$1.DONE; }; Batch$1.prototype.removeAllPrimitives = function () { var primitives = this.primitives; var primitive = this.primitive; if (defined(primitive)) { primitives.remove(primitive); this.primitive = undefined; this.geometry.removeAll(); this.updaters.removeAll(); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { primitives.remove(oldPrimitive); this.oldPrimitive = undefined; } }; /** * @private */ function StaticOutlineGeometryBatch(primitives, scene, shadows) { this._primitives = primitives; this._scene = scene; this._shadows = shadows; this._solidBatches = new AssociativeArray(); this._translucentBatches = new AssociativeArray(); } StaticOutlineGeometryBatch.prototype.add = function (time, updater) { var instance = updater.createOutlineGeometryInstance(time); var width = this._scene.clampLineWidth(updater.outlineWidth); var batches; var batch; if (instance.attributes.color.value[3] === 255) { batches = this._solidBatches; batch = batches.get(width); if (!defined(batch)) { batch = new Batch$1(this._primitives, false, width, this._shadows); batches.set(width, batch); } batch.add(updater, instance); } else { batches = this._translucentBatches; batch = batches.get(width); if (!defined(batch)) { batch = new Batch$1(this._primitives, true, width, this._shadows); batches.set(width, batch); } batch.add(updater, instance); } }; StaticOutlineGeometryBatch.prototype.remove = function (updater) { var i; var solidBatches = this._solidBatches.values; var solidBatchesLength = solidBatches.length; for (i = 0; i < solidBatchesLength; i++) { if (solidBatches[i].remove(updater)) { return; } } var translucentBatches = this._translucentBatches.values; var translucentBatchesLength = translucentBatches.length; for (i = 0; i < translucentBatchesLength; i++) { if (translucentBatches[i].remove(updater)) { return; } } }; StaticOutlineGeometryBatch.prototype.update = function (time) { var i; var x; var updater; var batch; var solidBatches = this._solidBatches.values; var solidBatchesLength = solidBatches.length; var translucentBatches = this._translucentBatches.values; var translucentBatchesLength = translucentBatches.length; var itemsToRemove; var isUpdated = true; var needUpdate = false; do { needUpdate = false; for (x = 0; x < solidBatchesLength; x++) { batch = solidBatches[x]; //Perform initial update isUpdated = batch.update(time); //If any items swapped between solid/translucent, we need to //move them between batches itemsToRemove = batch.itemsToRemove; var solidsToMoveLength = itemsToRemove.length; if (solidsToMoveLength > 0) { needUpdate = true; for (i = 0; i < solidsToMoveLength; i++) { updater = itemsToRemove[i]; batch.remove(updater); this.add(time, updater); } } } for (x = 0; x < translucentBatchesLength; x++) { batch = translucentBatches[x]; //Perform initial update isUpdated = batch.update(time); //If any items swapped between solid/translucent, we need to //move them between batches itemsToRemove = batch.itemsToRemove; var translucentToMoveLength = itemsToRemove.length; if (translucentToMoveLength > 0) { needUpdate = true; for (i = 0; i < translucentToMoveLength; i++) { updater = itemsToRemove[i]; batch.remove(updater); this.add(time, updater); } } } } while (needUpdate); return isUpdated; }; StaticOutlineGeometryBatch.prototype.getBoundingSphere = function ( updater, result ) { var i; var solidBatches = this._solidBatches.values; var solidBatchesLength = solidBatches.length; for (i = 0; i < solidBatchesLength; i++) { var solidBatch = solidBatches[i]; if (solidBatch.contains(updater)) { return solidBatch.getBoundingSphere(updater, result); } } var translucentBatches = this._translucentBatches.values; var translucentBatchesLength = translucentBatches.length; for (i = 0; i < translucentBatchesLength; i++) { var translucentBatch = translucentBatches[i]; if (translucentBatch.contains(updater)) { return translucentBatch.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; }; StaticOutlineGeometryBatch.prototype.removeAllPrimitives = function () { var i; var solidBatches = this._solidBatches.values; var solidBatchesLength = solidBatches.length; for (i = 0; i < solidBatchesLength; i++) { solidBatches[i].removeAllPrimitives(); } var translucentBatches = this._translucentBatches.values; var translucentBatchesLength = translucentBatches.length; for (i = 0; i < translucentBatchesLength; i++) { translucentBatches[i].removeAllPrimitives(); } }; var scratchColor$5 = new Color(); function WallGeometryOptions(entity) { this.id = entity; this.vertexFormat = undefined; this.positions = undefined; this.minimumHeights = undefined; this.maximumHeights = undefined; this.granularity = undefined; } /** * A {@link GeometryUpdater} for walls. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias WallGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function WallGeometryUpdater(entity, scene) { GeometryUpdater.call(this, { entity: entity, scene: scene, geometryOptions: new WallGeometryOptions(entity), geometryPropertyName: "wall", observedPropertyNames: ["availability", "wall"], }); this._onEntityPropertyChanged(entity, "wall", entity.wall, undefined); } if (defined(Object.create)) { WallGeometryUpdater.prototype = Object.create(GeometryUpdater.prototype); WallGeometryUpdater.prototype.constructor = WallGeometryUpdater; } /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ WallGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var attributes; var color; var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._fillProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); if (this._materialProperty instanceof ColorMaterialProperty) { var currentColor; if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$5); } if (!defined(currentColor)) { currentColor = Color.WHITE; } color = ColorGeometryInstanceAttribute.fromColor(currentColor); attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, color: color, }; } else { attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, }; } return new GeometryInstance({ id: entity, geometry: new WallGeometry(this._options), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ WallGeometryUpdater.prototype.createOutlineGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); if (!this._outlineEnabled) { throw new DeveloperError( "This instance does not represent an outlined geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var outlineColor = Property.getValueOrDefault( this._outlineColorProperty, time, Color.BLACK, scratchColor$5 ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); return new GeometryInstance({ id: entity, geometry: new WallOutlineGeometry(this._options), attributes: { show: new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) && this._showOutlineProperty.getValue(time) ), color: ColorGeometryInstanceAttribute.fromColor(outlineColor), distanceDisplayCondition: DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ), }, }); }; WallGeometryUpdater.prototype._isHidden = function (entity, wall) { return ( !defined(wall.positions) || GeometryUpdater.prototype._isHidden.call(this, entity, wall) ); }; WallGeometryUpdater.prototype._getIsClosed = function (options) { return false; }; WallGeometryUpdater.prototype._isDynamic = function (entity, wall) { return ( !wall.positions.isConstant || // !Property.isConstant(wall.minimumHeights) || // !Property.isConstant(wall.maximumHeights) || // !Property.isConstant(wall.outlineWidth) || // !Property.isConstant(wall.granularity) ); }; WallGeometryUpdater.prototype._setStaticOptions = function (entity, wall) { var minimumHeights = wall.minimumHeights; var maximumHeights = wall.maximumHeights; var granularity = wall.granularity; var isColorMaterial = this._materialProperty instanceof ColorMaterialProperty; var options = this._options; options.vertexFormat = isColorMaterial ? PerInstanceColorAppearance.VERTEX_FORMAT : MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat; options.positions = wall.positions.getValue( Iso8601.MINIMUM_VALUE, options.positions ); options.minimumHeights = defined(minimumHeights) ? minimumHeights.getValue(Iso8601.MINIMUM_VALUE, options.minimumHeights) : undefined; options.maximumHeights = defined(maximumHeights) ? maximumHeights.getValue(Iso8601.MINIMUM_VALUE, options.maximumHeights) : undefined; options.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; }; WallGeometryUpdater.DynamicGeometryUpdater = DynamicWallGeometryUpdater; /** * @private */ function DynamicWallGeometryUpdater( geometryUpdater, primitives, groundPrimitives ) { DynamicGeometryUpdater$1.call( this, geometryUpdater, primitives, groundPrimitives ); } if (defined(Object.create)) { DynamicWallGeometryUpdater.prototype = Object.create( DynamicGeometryUpdater$1.prototype ); DynamicWallGeometryUpdater.prototype.constructor = DynamicWallGeometryUpdater; } DynamicWallGeometryUpdater.prototype._isHidden = function (entity, wall, time) { return ( !defined(this._options.positions) || DynamicGeometryUpdater$1.prototype._isHidden.call(this, entity, wall, time) ); }; DynamicWallGeometryUpdater.prototype._setOptions = function ( entity, wall, time ) { var options = this._options; options.positions = Property.getValueOrUndefined( wall.positions, time, options.positions ); options.minimumHeights = Property.getValueOrUndefined( wall.minimumHeights, time, options.minimumHeights ); options.maximumHeights = Property.getValueOrUndefined( wall.maximumHeights, time, options.maximumHeights ); options.granularity = Property.getValueOrUndefined(wall.granularity, time); }; var emptyArray$1 = []; var geometryUpdaters = [ BoxGeometryUpdater, CylinderGeometryUpdater, CorridorGeometryUpdater, EllipseGeometryUpdater, EllipsoidGeometryUpdater, PlaneGeometryUpdater, PolygonGeometryUpdater, PolylineVolumeGeometryUpdater, RectangleGeometryUpdater, WallGeometryUpdater, ]; function GeometryUpdaterSet(entity, scene) { this.entity = entity; this.scene = scene; var updaters = new Array(geometryUpdaters.length); var geometryChanged = new Event(); function raiseEvent(geometry) { geometryChanged.raiseEvent(geometry); } var eventHelper = new EventHelper(); for (var i = 0; i < updaters.length; i++) { var updater = new geometryUpdaters[i](entity, scene); eventHelper.add(updater.geometryChanged, raiseEvent); updaters[i] = updater; } this.updaters = updaters; this.geometryChanged = geometryChanged; this.eventHelper = eventHelper; this._removeEntitySubscription = entity.definitionChanged.addEventListener( GeometryUpdaterSet.prototype._onEntityPropertyChanged, this ); } GeometryUpdaterSet.prototype._onEntityPropertyChanged = function ( entity, propertyName, newValue, oldValue ) { var updaters = this.updaters; for (var i = 0; i < updaters.length; i++) { updaters[i]._onEntityPropertyChanged( entity, propertyName, newValue, oldValue ); } }; GeometryUpdaterSet.prototype.forEach = function (callback) { var updaters = this.updaters; for (var i = 0; i < updaters.length; i++) { callback(updaters[i]); } }; GeometryUpdaterSet.prototype.destroy = function () { this.eventHelper.removeAll(); var updaters = this.updaters; for (var i = 0; i < updaters.length; i++) { updaters[i].destroy(); } this._removeEntitySubscription(); destroyObject(this); }; /** * A general purpose visualizer for geometry represented by {@link Primitive} instances. * @alias GeometryVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities */ function GeometryVisualizer( scene, entityCollection, primitives, groundPrimitives ) { //>>includeStart('debug', pragmas.debug); Check.defined("scene", scene); Check.defined("entityCollection", entityCollection); //>>includeEnd('debug'); primitives = defaultValue(primitives, scene.primitives); groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives); this._scene = scene; this._primitives = primitives; this._groundPrimitives = groundPrimitives; this._entityCollection = undefined; this._addedObjects = new AssociativeArray(); this._removedObjects = new AssociativeArray(); this._changedObjects = new AssociativeArray(); var numberOfShadowModes = ShadowMode$1.NUMBER_OF_SHADOW_MODES; this._outlineBatches = new Array(numberOfShadowModes * 2); this._closedColorBatches = new Array(numberOfShadowModes * 2); this._closedMaterialBatches = new Array(numberOfShadowModes * 2); this._openColorBatches = new Array(numberOfShadowModes * 2); this._openMaterialBatches = new Array(numberOfShadowModes * 2); var supportsMaterialsforEntitiesOnTerrain = Entity.supportsMaterialsforEntitiesOnTerrain( scene ); this._supportsMaterialsforEntitiesOnTerrain = supportsMaterialsforEntitiesOnTerrain; var i; for (i = 0; i < numberOfShadowModes; ++i) { this._outlineBatches[i] = new StaticOutlineGeometryBatch( primitives, scene, i, false ); this._outlineBatches[ numberOfShadowModes + i ] = new StaticOutlineGeometryBatch(primitives, scene, i, true); this._closedColorBatches[i] = new StaticGeometryColorBatch( primitives, PerInstanceColorAppearance, undefined, true, i, true ); this._closedColorBatches[ numberOfShadowModes + i ] = new StaticGeometryColorBatch( primitives, PerInstanceColorAppearance, undefined, true, i, false ); this._closedMaterialBatches[i] = new StaticGeometryPerMaterialBatch( primitives, MaterialAppearance, undefined, true, i, true ); this._closedMaterialBatches[ numberOfShadowModes + i ] = new StaticGeometryPerMaterialBatch( primitives, MaterialAppearance, undefined, true, i, false ); this._openColorBatches[i] = new StaticGeometryColorBatch( primitives, PerInstanceColorAppearance, undefined, false, i, true ); this._openColorBatches[ numberOfShadowModes + i ] = new StaticGeometryColorBatch( primitives, PerInstanceColorAppearance, undefined, false, i, false ); this._openMaterialBatches[i] = new StaticGeometryPerMaterialBatch( primitives, MaterialAppearance, undefined, false, i, true ); this._openMaterialBatches[ numberOfShadowModes + i ] = new StaticGeometryPerMaterialBatch( primitives, MaterialAppearance, undefined, false, i, false ); } var numberOfClassificationTypes = ClassificationType$1.NUMBER_OF_CLASSIFICATION_TYPES; var groundColorBatches = new Array(numberOfClassificationTypes); var groundMaterialBatches = []; if (supportsMaterialsforEntitiesOnTerrain) { for (i = 0; i < numberOfClassificationTypes; ++i) { groundMaterialBatches.push( new StaticGroundGeometryPerMaterialBatch( groundPrimitives, i, MaterialAppearance ) ); groundColorBatches[i] = new StaticGroundGeometryColorBatch( groundPrimitives, i ); } } else { for (i = 0; i < numberOfClassificationTypes; ++i) { groundColorBatches[i] = new StaticGroundGeometryColorBatch( groundPrimitives, i ); } } this._groundColorBatches = groundColorBatches; this._groundMaterialBatches = groundMaterialBatches; this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives); this._batches = this._outlineBatches.concat( this._closedColorBatches, this._closedMaterialBatches, this._openColorBatches, this._openMaterialBatches, this._groundColorBatches, this._groundMaterialBatches, this._dynamicBatch ); this._subscriptions = new AssociativeArray(); this._updaterSets = new AssociativeArray(); this._entityCollection = entityCollection; entityCollection.collectionChanged.addEventListener( GeometryVisualizer.prototype._onCollectionChanged, this ); this._onCollectionChanged( entityCollection, entityCollection.values, emptyArray$1 ); } /** * Updates all of the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} True if the visualizer successfully updated to the provided time, * false if the visualizer is waiting for asynchronous primitives to be created. */ GeometryVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var addedObjects = this._addedObjects; var added = addedObjects.values; var removedObjects = this._removedObjects; var removed = removedObjects.values; var changedObjects = this._changedObjects; var changed = changedObjects.values; var i; var entity; var id; var updaterSet; var that = this; for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; id = entity.id; updaterSet = this._updaterSets.get(id); //If in a single update, an entity gets removed and a new instance //re-added with the same id, the updater no longer tracks the //correct entity, we need to both remove the old one and //add the new one, which is done by pushing the entity //onto the removed/added lists. if (updaterSet.entity === entity) { updaterSet.forEach(function (updater) { that._removeUpdater(updater); that._insertUpdaterIntoBatch(time, updater); }); } else { removed.push(entity); added.push(entity); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; updaterSet = this._updaterSets.get(id); updaterSet.forEach(this._removeUpdater.bind(this)); updaterSet.destroy(); this._updaterSets.remove(id); this._subscriptions.get(id)(); this._subscriptions.remove(id); } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; updaterSet = new GeometryUpdaterSet(entity, this._scene); this._updaterSets.set(id, updaterSet); updaterSet.forEach(function (updater) { that._insertUpdaterIntoBatch(time, updater); }); this._subscriptions.set( id, updaterSet.geometryChanged.addEventListener( GeometryVisualizer._onGeometryChanged, this ) ); } addedObjects.removeAll(); removedObjects.removeAll(); changedObjects.removeAll(); var isUpdated = true; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { isUpdated = batches[i].update(time) && isUpdated; } return isUpdated; }; var getBoundingSphereArrayScratch$2 = []; var getBoundingSphereBoundingSphereScratch$2 = new BoundingSphere(); /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ GeometryVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); Check.defined("entity", entity); Check.defined("result", result); //>>includeEnd('debug'); var boundingSpheres = getBoundingSphereArrayScratch$2; var tmp = getBoundingSphereBoundingSphereScratch$2; var count = 0; var state = BoundingSphereState$1.DONE; var batches = this._batches; var batchesLength = batches.length; var id = entity.id; var updaters = this._updaterSets.get(id).updaters; for (var j = 0; j < updaters.length; j++) { var updater = updaters[j]; for (var i = 0; i < batchesLength; i++) { state = batches[i].getBoundingSphere(updater, tmp); if (state === BoundingSphereState$1.PENDING) { return BoundingSphereState$1.PENDING; } else if (state === BoundingSphereState$1.DONE) { boundingSpheres[count] = BoundingSphere.clone( tmp, boundingSpheres[count] ); count++; } } } if (count === 0) { return BoundingSphereState$1.FAILED; } boundingSpheres.length = count; BoundingSphere.fromBoundingSpheres(boundingSpheres, result); return BoundingSphereState$1.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ GeometryVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ GeometryVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( GeometryVisualizer.prototype._onCollectionChanged, this ); this._addedObjects.removeAll(); this._removedObjects.removeAll(); var i; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { batches[i].removeAllPrimitives(); } var subscriptions = this._subscriptions.values; length = subscriptions.length; for (i = 0; i < length; i++) { subscriptions[i](); } this._subscriptions.removeAll(); var updaterSets = this._updaterSets.values; length = updaterSets.length; for (i = 0; i < length; i++) { updaterSets[i].destroy(); } this._updaterSets.removeAll(); return destroyObject(this); }; /** * @private */ GeometryVisualizer.prototype._removeUpdater = function (updater) { //We don't keep track of which batch an updater is in, so just remove it from all of them. var batches = this._batches; var length = batches.length; for (var i = 0; i < length; i++) { batches[i].remove(updater); } }; /** * @private */ GeometryVisualizer.prototype._insertUpdaterIntoBatch = function ( time, updater ) { if (updater.isDynamic) { this._dynamicBatch.add(time, updater); return; } var shadows; if (updater.outlineEnabled || updater.fillEnabled) { shadows = updater.shadowsProperty.getValue(time); } var numberOfShadowModes = ShadowMode$1.NUMBER_OF_SHADOW_MODES; if (updater.outlineEnabled) { if (defined(updater.terrainOffsetProperty)) { this._outlineBatches[numberOfShadowModes + shadows].add(time, updater); } else { this._outlineBatches[shadows].add(time, updater); } } if (updater.fillEnabled) { if (updater.onTerrain) { var classificationType = updater.classificationTypeProperty.getValue( time ); if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { this._groundColorBatches[classificationType].add(time, updater); } else { // If unsupported, updater will not be on terrain. this._groundMaterialBatches[classificationType].add(time, updater); } } else if (updater.isClosed) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { if (defined(updater.terrainOffsetProperty)) { this._closedColorBatches[numberOfShadowModes + shadows].add( time, updater ); } else { this._closedColorBatches[shadows].add(time, updater); } } else if (defined(updater.terrainOffsetProperty)) { this._closedMaterialBatches[numberOfShadowModes + shadows].add( time, updater ); } else { this._closedMaterialBatches[shadows].add(time, updater); } } else if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { if (defined(updater.terrainOffsetProperty)) { this._openColorBatches[numberOfShadowModes + shadows].add( time, updater ); } else { this._openColorBatches[shadows].add(time, updater); } } else if (defined(updater.terrainOffsetProperty)) { this._openMaterialBatches[numberOfShadowModes + shadows].add( time, updater ); } else { this._openMaterialBatches[shadows].add(time, updater); } } }; /** * @private */ GeometryVisualizer._onGeometryChanged = function (updater) { var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var entity = updater.entity; var id = entity.id; if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) { changedObjects.set(id, entity); } }; /** * @private */ GeometryVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed ) { var addedObjects = this._addedObjects; var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var i; var id; var entity; for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; if (!addedObjects.remove(id)) { removedObjects.set(id, entity); changedObjects.remove(id); } } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; if (removedObjects.remove(id)) { changedObjects.set(id, entity); } else { addedObjects.set(id, entity); } } }; var defaultScale$1 = 1.0; var defaultFont = "30px sans-serif"; var defaultStyle = LabelStyle$1.FILL; var defaultFillColor = Color.WHITE; var defaultOutlineColor$1 = Color.BLACK; var defaultOutlineWidth$1 = 1.0; var defaultShowBackground = false; var defaultBackgroundColor$1 = new Color(0.165, 0.165, 0.165, 0.8); var defaultBackgroundPadding = new Cartesian2(7, 5); var defaultPixelOffset = Cartesian2.ZERO; var defaultEyeOffset = Cartesian3.ZERO; var defaultHeightReference$1 = HeightReference$1.NONE; var defaultHorizontalOrigin = HorizontalOrigin$1.CENTER; var defaultVerticalOrigin = VerticalOrigin$1.CENTER; var positionScratch$1 = new Cartesian3(); var fillColorScratch = new Color(); var outlineColorScratch$1 = new Color(); var backgroundColorScratch = new Color(); var backgroundPaddingScratch = new Cartesian2(); var eyeOffsetScratch = new Cartesian3(); var pixelOffsetScratch = new Cartesian2(); var translucencyByDistanceScratch$1 = new NearFarScalar(); var pixelOffsetScaleByDistanceScratch = new NearFarScalar(); var scaleByDistanceScratch$1 = new NearFarScalar(); var distanceDisplayConditionScratch$2 = new DistanceDisplayCondition(); function EntityData$2(entity) { this.entity = entity; this.label = undefined; this.index = undefined; } /** * A {@link Visualizer} which maps the {@link LabelGraphics} instance * in {@link Entity#label} to a {@link Label}. * @alias LabelVisualizer * @constructor * * @param {EntityCluster} entityCluster The entity cluster to manage the collection of billboards and optionally cluster with other entities. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function LabelVisualizer(entityCluster, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(entityCluster)) { throw new DeveloperError("entityCluster is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( LabelVisualizer.prototype._onCollectionChanged, this ); this._cluster = entityCluster; this._entityCollection = entityCollection; this._items = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ LabelVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var items = this._items.values; var cluster = this._cluster; for (var i = 0, len = items.length; i < len; i++) { var item = items[i]; var entity = item.entity; var labelGraphics = entity._label; var text; var label = item.label; var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(labelGraphics._show, time, true); var position; if (show) { position = Property.getValueOrUndefined( entity._position, time, positionScratch$1 ); text = Property.getValueOrUndefined(labelGraphics._text, time); show = defined(position) && defined(text); } if (!show) { //don't bother creating or updating anything else returnPrimitive$1(item, entity, cluster); continue; } if (!Property.isConstant(entity._position)) { cluster._clusterDirty = true; } var updateClamping = false; var heightReference = Property.getValueOrDefault( labelGraphics._heightReference, time, defaultHeightReference$1 ); if (!defined(label)) { label = cluster.getLabel(entity); label.id = entity; item.label = label; // If this new label happens to have a position and height reference that match our new values, // label._updateClamping will not be called automatically. That's a problem because the clamped // height may be based on different terrain than is now loaded. So we'll manually call // _updateClamping below. updateClamping = Cartesian3.equals(label.position, position) && label.heightReference === heightReference; } label.show = true; label.position = position; label.text = text; label.scale = Property.getValueOrDefault( labelGraphics._scale, time, defaultScale$1 ); label.font = Property.getValueOrDefault( labelGraphics._font, time, defaultFont ); label.style = Property.getValueOrDefault( labelGraphics._style, time, defaultStyle ); label.fillColor = Property.getValueOrDefault( labelGraphics._fillColor, time, defaultFillColor, fillColorScratch ); label.outlineColor = Property.getValueOrDefault( labelGraphics._outlineColor, time, defaultOutlineColor$1, outlineColorScratch$1 ); label.outlineWidth = Property.getValueOrDefault( labelGraphics._outlineWidth, time, defaultOutlineWidth$1 ); label.showBackground = Property.getValueOrDefault( labelGraphics._showBackground, time, defaultShowBackground ); label.backgroundColor = Property.getValueOrDefault( labelGraphics._backgroundColor, time, defaultBackgroundColor$1, backgroundColorScratch ); label.backgroundPadding = Property.getValueOrDefault( labelGraphics._backgroundPadding, time, defaultBackgroundPadding, backgroundPaddingScratch ); label.pixelOffset = Property.getValueOrDefault( labelGraphics._pixelOffset, time, defaultPixelOffset, pixelOffsetScratch ); label.eyeOffset = Property.getValueOrDefault( labelGraphics._eyeOffset, time, defaultEyeOffset, eyeOffsetScratch ); label.heightReference = heightReference; label.horizontalOrigin = Property.getValueOrDefault( labelGraphics._horizontalOrigin, time, defaultHorizontalOrigin ); label.verticalOrigin = Property.getValueOrDefault( labelGraphics._verticalOrigin, time, defaultVerticalOrigin ); label.translucencyByDistance = Property.getValueOrUndefined( labelGraphics._translucencyByDistance, time, translucencyByDistanceScratch$1 ); label.pixelOffsetScaleByDistance = Property.getValueOrUndefined( labelGraphics._pixelOffsetScaleByDistance, time, pixelOffsetScaleByDistanceScratch ); label.scaleByDistance = Property.getValueOrUndefined( labelGraphics._scaleByDistance, time, scaleByDistanceScratch$1 ); label.distanceDisplayCondition = Property.getValueOrUndefined( labelGraphics._distanceDisplayCondition, time, distanceDisplayConditionScratch$2 ); label.disableDepthTestDistance = Property.getValueOrUndefined( labelGraphics._disableDepthTestDistance, time ); if (updateClamping) { label._updateClamping(); } } return true; }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ LabelVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var item = this._items.get(entity.id); if (!defined(item) || !defined(item.label)) { return BoundingSphereState$1.FAILED; } var label = item.label; result.center = Cartesian3.clone( defaultValue(label._clampedPosition, label.position), result.center ); result.radius = 0; return BoundingSphereState$1.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ LabelVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ LabelVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( LabelVisualizer.prototype._onCollectionChanged, this ); var entities = this._entityCollection.values; for (var i = 0; i < entities.length; i++) { this._cluster.removeLabel(entities[i]); } return destroyObject(this); }; LabelVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var items = this._items; var cluster = this._cluster; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._label) && defined(entity._position)) { items.set(entity.id, new EntityData$2(entity)); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._label) && defined(entity._position)) { if (!items.contains(entity.id)) { items.set(entity.id, new EntityData$2(entity)); } } else { returnPrimitive$1(items.get(entity.id), entity, cluster); items.remove(entity.id); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; returnPrimitive$1(items.get(entity.id), entity, cluster); items.remove(entity.id); } }; function returnPrimitive$1(item, entity, cluster) { if (defined(item)) { item.label = undefined; cluster.removeLabel(entity); } } var defaultScale = 1.0; var defaultMinimumPixelSize = 0.0; var defaultIncrementallyLoadTextures = true; var defaultClampAnimations = true; var defaultShadows$1 = ShadowMode$1.ENABLED; var defaultHeightReference = HeightReference$1.NONE; var defaultSilhouetteColor = Color.RED; var defaultSilhouetteSize = 0.0; var defaultColor$2 = Color.WHITE; var defaultColorBlendMode = ColorBlendMode$1.HIGHLIGHT; var defaultColorBlendAmount = 0.5; var defaultImageBasedLightingFactor = new Cartesian2(1.0, 1.0); var modelMatrixScratch = new Matrix4(); var nodeMatrixScratch = new Matrix4(); /** * A {@link Visualizer} which maps {@link Entity#model} to a {@link Model}. * @alias ModelVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function ModelVisualizer(scene, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError("scene is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( ModelVisualizer.prototype._onCollectionChanged, this ); this._scene = scene; this._primitives = scene.primitives; this._entityCollection = entityCollection; this._modelHash = {}; this._entitiesToVisualize = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates models created this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ ModelVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var entities = this._entitiesToVisualize.values; var modelHash = this._modelHash; var primitives = this._primitives; for (var i = 0, len = entities.length; i < len; i++) { var entity = entities[i]; var modelGraphics = entity._model; var resource; var modelData = modelHash[entity.id]; var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(modelGraphics._show, time, true); var modelMatrix; if (show) { modelMatrix = entity.computeModelMatrix(time, modelMatrixScratch); resource = Resource.createIfNeeded( Property.getValueOrUndefined(modelGraphics._uri, time) ); show = defined(modelMatrix) && defined(resource); } if (!show) { if (defined(modelData)) { modelData.modelPrimitive.show = false; } continue; } var model = defined(modelData) ? modelData.modelPrimitive : undefined; if (!defined(model) || resource.url !== modelData.url) { if (defined(model)) { primitives.removeAndDestroy(model); delete modelHash[entity.id]; } model = Model.fromGltf({ url: resource, incrementallyLoadTextures: Property.getValueOrDefault( modelGraphics._incrementallyLoadTextures, time, defaultIncrementallyLoadTextures ), scene: this._scene, }); model.id = entity; primitives.add(model); modelData = { modelPrimitive: model, url: resource.url, animationsRunning: false, nodeTransformationsScratch: {}, articulationsScratch: {}, loadFail: false, }; modelHash[entity.id] = modelData; checkModelLoad(model, entity, modelHash); } model.show = true; model.scale = Property.getValueOrDefault( modelGraphics._scale, time, defaultScale ); model.minimumPixelSize = Property.getValueOrDefault( modelGraphics._minimumPixelSize, time, defaultMinimumPixelSize ); model.maximumScale = Property.getValueOrUndefined( modelGraphics._maximumScale, time ); model.modelMatrix = Matrix4.clone(modelMatrix, model.modelMatrix); model.shadows = Property.getValueOrDefault( modelGraphics._shadows, time, defaultShadows$1 ); model.heightReference = Property.getValueOrDefault( modelGraphics._heightReference, time, defaultHeightReference ); model.distanceDisplayCondition = Property.getValueOrUndefined( modelGraphics._distanceDisplayCondition, time ); model.silhouetteColor = Property.getValueOrDefault( modelGraphics._silhouetteColor, time, defaultSilhouetteColor, model._silhouetteColor ); model.silhouetteSize = Property.getValueOrDefault( modelGraphics._silhouetteSize, time, defaultSilhouetteSize ); model.color = Property.getValueOrDefault( modelGraphics._color, time, defaultColor$2, model._color ); model.colorBlendMode = Property.getValueOrDefault( modelGraphics._colorBlendMode, time, defaultColorBlendMode ); model.colorBlendAmount = Property.getValueOrDefault( modelGraphics._colorBlendAmount, time, defaultColorBlendAmount ); model.clippingPlanes = Property.getValueOrUndefined( modelGraphics._clippingPlanes, time ); model.clampAnimations = Property.getValueOrDefault( modelGraphics._clampAnimations, time, defaultClampAnimations ); model.imageBasedLightingFactor = Property.getValueOrDefault( modelGraphics._imageBasedLightingFactor, time, defaultImageBasedLightingFactor ); model.lightColor = Property.getValueOrUndefined( modelGraphics._lightColor, time ); if (model.ready) { var runAnimations = Property.getValueOrDefault( modelGraphics._runAnimations, time, true ); if (modelData.animationsRunning !== runAnimations) { if (runAnimations) { model.activeAnimations.addAll({ loop: ModelAnimationLoop$1.REPEAT, }); } else { model.activeAnimations.removeAll(); } modelData.animationsRunning = runAnimations; } // Apply node transformations var nodeTransformations = Property.getValueOrUndefined( modelGraphics._nodeTransformations, time, modelData.nodeTransformationsScratch ); if (defined(nodeTransformations)) { var nodeNames = Object.keys(nodeTransformations); for ( var nodeIndex = 0, nodeLength = nodeNames.length; nodeIndex < nodeLength; ++nodeIndex ) { var nodeName = nodeNames[nodeIndex]; var nodeTransformation = nodeTransformations[nodeName]; if (!defined(nodeTransformation)) { continue; } var modelNode = model.getNode(nodeName); if (!defined(modelNode)) { continue; } var transformationMatrix = Matrix4.fromTranslationRotationScale( nodeTransformation, nodeMatrixScratch ); modelNode.matrix = Matrix4.multiply( modelNode.originalMatrix, transformationMatrix, transformationMatrix ); } } // Apply articulations var anyArticulationUpdated = false; var articulations = Property.getValueOrUndefined( modelGraphics._articulations, time, modelData.articulationsScratch ); if (defined(articulations)) { var articulationStageKeys = Object.keys(articulations); for ( var s = 0, numKeys = articulationStageKeys.length; s < numKeys; ++s ) { var key = articulationStageKeys[s]; var articulationStageValue = articulations[key]; if (!defined(articulationStageValue)) { continue; } anyArticulationUpdated = true; model.setArticulationStage(key, articulationStageValue); } } if (anyArticulationUpdated) { model.applyArticulations(); } } } return true; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ ModelVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ ModelVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( ModelVisualizer.prototype._onCollectionChanged, this ); var entities = this._entitiesToVisualize.values; var modelHash = this._modelHash; var primitives = this._primitives; for (var i = entities.length - 1; i > -1; i--) { removeModel(this, entities[i], modelHash, primitives); } return destroyObject(this); }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ ModelVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var modelData = this._modelHash[entity.id]; if (!defined(modelData) || modelData.loadFail) { return BoundingSphereState$1.FAILED; } var model = modelData.modelPrimitive; if (!defined(model) || !model.show) { return BoundingSphereState$1.FAILED; } if (!model.ready) { return BoundingSphereState$1.PENDING; } if (model.heightReference === HeightReference$1.NONE) { BoundingSphere.transform(model.boundingSphere, model.modelMatrix, result); } else { if (!defined(model._clampedModelMatrix)) { return BoundingSphereState$1.PENDING; } BoundingSphere.transform( model.boundingSphere, model._clampedModelMatrix, result ); } return BoundingSphereState$1.DONE; }; /** * @private */ ModelVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var entities = this._entitiesToVisualize; var modelHash = this._modelHash; var primitives = this._primitives; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._model) && defined(entity._position)) { entities.set(entity.id, entity); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._model) && defined(entity._position)) { clearNodeTransformationsArticulationsScratch(entity, modelHash); entities.set(entity.id, entity); } else { removeModel(this, entity, modelHash, primitives); entities.remove(entity.id); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; removeModel(this, entity, modelHash, primitives); entities.remove(entity.id); } }; function removeModel(visualizer, entity, modelHash, primitives) { var modelData = modelHash[entity.id]; if (defined(modelData)) { primitives.removeAndDestroy(modelData.modelPrimitive); delete modelHash[entity.id]; } } function clearNodeTransformationsArticulationsScratch(entity, modelHash) { var modelData = modelHash[entity.id]; if (defined(modelData)) { modelData.nodeTransformationsScratch = {}; modelData.articulationsScratch = {}; } } function checkModelLoad(model, entity, modelHash) { model.readyPromise.otherwise(function (error) { console.error(error); modelHash[entity.id].loadFail = true; }); } /** * This is a temporary class for scaling position properties to the WGS84 surface. * It will go away or be refactored to support data with arbitrary height references. * @private */ function ScaledPositionProperty(value) { this._definitionChanged = new Event(); this._value = undefined; this._removeSubscription = undefined; this.setValue(value); } Object.defineProperties(ScaledPositionProperty.prototype, { isConstant: { get: function () { return Property.isConstant(this._value); }, }, definitionChanged: { get: function () { return this._definitionChanged; }, }, referenceFrame: { get: function () { return defined(this._value) ? this._value.referenceFrame : ReferenceFrame$1.FIXED; }, }, }); ScaledPositionProperty.prototype.getValue = function (time, result) { return this.getValueInReferenceFrame(time, ReferenceFrame$1.FIXED, result); }; ScaledPositionProperty.prototype.setValue = function (value) { if (this._value !== value) { this._value = value; if (defined(this._removeSubscription)) { this._removeSubscription(); this._removeSubscription = undefined; } if (defined(value)) { this._removeSubscription = value.definitionChanged.addEventListener( this._raiseDefinitionChanged, this ); } this._definitionChanged.raiseEvent(this); } }; ScaledPositionProperty.prototype.getValueInReferenceFrame = function ( time, referenceFrame, result ) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!defined(referenceFrame)) { throw new DeveloperError("referenceFrame is required."); } //>>includeEnd('debug'); if (!defined(this._value)) { return undefined; } result = this._value.getValueInReferenceFrame(time, referenceFrame, result); return defined(result) ? Ellipsoid.WGS84.scaleToGeodeticSurface(result, result) : undefined; }; ScaledPositionProperty.prototype.equals = function (other) { return ( this === other || (other instanceof ScaledPositionProperty && this._value === other._value) ); }; ScaledPositionProperty.prototype._raiseDefinitionChanged = function () { this._definitionChanged.raiseEvent(this); }; var defaultResolution = 60.0; var defaultWidth = 1.0; var scratchTimeInterval = new TimeInterval(); var subSampleCompositePropertyScratch = new TimeInterval(); var subSampleIntervalPropertyScratch = new TimeInterval(); function EntityData$1(entity) { this.entity = entity; this.polyline = undefined; this.index = undefined; this.updater = undefined; } function subSampleSampledProperty( property, start, stop, times, updateTime, referenceFrame, maximumStep, startingIndex, result ) { var r = startingIndex; //Always step exactly on start (but only use it if it exists.) var tmp; tmp = property.getValueInReferenceFrame(start, referenceFrame, result[r]); if (defined(tmp)) { result[r++] = tmp; } var steppedOnNow = !defined(updateTime) || JulianDate.lessThanOrEquals(updateTime, start) || JulianDate.greaterThanOrEquals(updateTime, stop); //Iterate over all interval times and add the ones that fall in our //time range. Note that times can contain data outside of //the intervals range. This is by design for use with interpolation. var t = 0; var len = times.length; var current = times[t]; var loopStop = stop; var sampling = false; var sampleStepsToTake; var sampleStepsTaken; var sampleStepSize; while (t < len) { if (!steppedOnNow && JulianDate.greaterThanOrEquals(current, updateTime)) { tmp = property.getValueInReferenceFrame( updateTime, referenceFrame, result[r] ); if (defined(tmp)) { result[r++] = tmp; } steppedOnNow = true; } if ( JulianDate.greaterThan(current, start) && JulianDate.lessThan(current, loopStop) && !current.equals(updateTime) ) { tmp = property.getValueInReferenceFrame( current, referenceFrame, result[r] ); if (defined(tmp)) { result[r++] = tmp; } } if (t < len - 1) { if (maximumStep > 0 && !sampling) { var next = times[t + 1]; var secondsUntilNext = JulianDate.secondsDifference(next, current); sampling = secondsUntilNext > maximumStep; if (sampling) { sampleStepsToTake = Math.ceil(secondsUntilNext / maximumStep); sampleStepsTaken = 0; sampleStepSize = secondsUntilNext / Math.max(sampleStepsToTake, 2); sampleStepsToTake = Math.max(sampleStepsToTake - 1, 1); } } if (sampling && sampleStepsTaken < sampleStepsToTake) { current = JulianDate.addSeconds( current, sampleStepSize, new JulianDate() ); sampleStepsTaken++; continue; } } sampling = false; t++; current = times[t]; } //Always step exactly on stop (but only use it if it exists.) tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[r]); if (defined(tmp)) { result[r++] = tmp; } return r; } function subSampleGenericProperty( property, start, stop, updateTime, referenceFrame, maximumStep, startingIndex, result ) { var tmp; var i = 0; var index = startingIndex; var time = start; var stepSize = Math.max(maximumStep, 60); var steppedOnNow = !defined(updateTime) || JulianDate.lessThanOrEquals(updateTime, start) || JulianDate.greaterThanOrEquals(updateTime, stop); while (JulianDate.lessThan(time, stop)) { if (!steppedOnNow && JulianDate.greaterThanOrEquals(time, updateTime)) { steppedOnNow = true; tmp = property.getValueInReferenceFrame( updateTime, referenceFrame, result[index] ); if (defined(tmp)) { result[index] = tmp; index++; } } tmp = property.getValueInReferenceFrame( time, referenceFrame, result[index] ); if (defined(tmp)) { result[index] = tmp; index++; } i++; time = JulianDate.addSeconds(start, stepSize * i, new JulianDate()); } //Always sample stop. tmp = property.getValueInReferenceFrame(stop, referenceFrame, result[index]); if (defined(tmp)) { result[index] = tmp; index++; } return index; } function subSampleIntervalProperty( property, start, stop, updateTime, referenceFrame, maximumStep, startingIndex, result ) { subSampleIntervalPropertyScratch.start = start; subSampleIntervalPropertyScratch.stop = stop; var index = startingIndex; var intervals = property.intervals; for (var i = 0; i < intervals.length; i++) { var interval = intervals.get(i); if ( !TimeInterval.intersect( interval, subSampleIntervalPropertyScratch, scratchTimeInterval ).isEmpty ) { var time = interval.start; if (!interval.isStartIncluded) { if (interval.isStopIncluded) { time = interval.stop; } else { time = JulianDate.addSeconds( interval.start, JulianDate.secondsDifference(interval.stop, interval.start) / 2, new JulianDate() ); } } var tmp = property.getValueInReferenceFrame( time, referenceFrame, result[index] ); if (defined(tmp)) { result[index] = tmp; index++; } } } return index; } function subSampleConstantProperty( property, start, stop, updateTime, referenceFrame, maximumStep, startingIndex, result ) { var tmp = property.getValueInReferenceFrame( start, referenceFrame, result[startingIndex] ); if (defined(tmp)) { result[startingIndex++] = tmp; } return startingIndex; } function subSampleCompositeProperty( property, start, stop, updateTime, referenceFrame, maximumStep, startingIndex, result ) { subSampleCompositePropertyScratch.start = start; subSampleCompositePropertyScratch.stop = stop; var index = startingIndex; var intervals = property.intervals; for (var i = 0; i < intervals.length; i++) { var interval = intervals.get(i); if ( !TimeInterval.intersect( interval, subSampleCompositePropertyScratch, scratchTimeInterval ).isEmpty ) { var intervalStart = interval.start; var intervalStop = interval.stop; var sampleStart = start; if (JulianDate.greaterThan(intervalStart, sampleStart)) { sampleStart = intervalStart; } var sampleStop = stop; if (JulianDate.lessThan(intervalStop, sampleStop)) { sampleStop = intervalStop; } index = reallySubSample( interval.data, sampleStart, sampleStop, updateTime, referenceFrame, maximumStep, index, result ); } } return index; } function reallySubSample( property, start, stop, updateTime, referenceFrame, maximumStep, index, result ) { //Unwrap any references until we have the actual property. while (property instanceof ReferenceProperty) { property = property.resolvedProperty; } if (property instanceof SampledPositionProperty) { var times = property._property._times; index = subSampleSampledProperty( property, start, stop, times, updateTime, referenceFrame, maximumStep, index, result ); } else if (property instanceof CompositePositionProperty) { index = subSampleCompositeProperty( property, start, stop, updateTime, referenceFrame, maximumStep, index, result ); } else if (property instanceof TimeIntervalCollectionPositionProperty) { index = subSampleIntervalProperty( property, start, stop, updateTime, referenceFrame, maximumStep, index, result ); } else if ( property instanceof ConstantPositionProperty || (property instanceof ScaledPositionProperty && Property.isConstant(property)) ) { index = subSampleConstantProperty( property, start, stop, updateTime, referenceFrame, maximumStep, index, result ); } else { //Fallback to generic sampling. index = subSampleGenericProperty( property, start, stop, updateTime, referenceFrame, maximumStep, index, result ); } return index; } function subSample( property, start, stop, updateTime, referenceFrame, maximumStep, result ) { if (!defined(result)) { result = []; } var length = reallySubSample( property, start, stop, updateTime, referenceFrame, maximumStep, 0, result ); result.length = length; return result; } var toFixedScratch = new Matrix3(); function PolylineUpdater(scene, referenceFrame) { this._unusedIndexes = []; this._polylineCollection = new PolylineCollection(); this._scene = scene; this._referenceFrame = referenceFrame; scene.primitives.add(this._polylineCollection); } PolylineUpdater.prototype.update = function (time) { if (this._referenceFrame === ReferenceFrame$1.INERTIAL) { var toFixed = Transforms.computeIcrfToFixedMatrix(time, toFixedScratch); if (!defined(toFixed)) { toFixed = Transforms.computeTemeToPseudoFixedMatrix(time, toFixedScratch); } Matrix4.fromRotationTranslation( toFixed, Cartesian3.ZERO, this._polylineCollection.modelMatrix ); } }; PolylineUpdater.prototype.updateObject = function (time, item) { var entity = item.entity; var pathGraphics = entity._path; var positionProperty = entity._position; var sampleStart; var sampleStop; var showProperty = pathGraphics._show; var polyline = item.polyline; var show = entity.isShowing && (!defined(showProperty) || showProperty.getValue(time)); //While we want to show the path, there may not actually be anything to show //depending on lead/trail settings. Compute the interval of the path to //show and check against actual availability. if (show) { var leadTime = Property.getValueOrUndefined(pathGraphics._leadTime, time); var trailTime = Property.getValueOrUndefined(pathGraphics._trailTime, time); var availability = entity._availability; var hasAvailability = defined(availability); var hasLeadTime = defined(leadTime); var hasTrailTime = defined(trailTime); //Objects need to have either defined availability or both a lead and trail time in order to //draw a path (since we can't draw "infinite" paths. show = hasAvailability || (hasLeadTime && hasTrailTime); //The final step is to compute the actual start/stop times of the path to show. //If current time is outside of the availability interval, there's a chance that //we won't have to draw anything anyway. if (show) { if (hasTrailTime) { sampleStart = JulianDate.addSeconds(time, -trailTime, new JulianDate()); } if (hasLeadTime) { sampleStop = JulianDate.addSeconds(time, leadTime, new JulianDate()); } if (hasAvailability) { var start = availability.start; var stop = availability.stop; if (!hasTrailTime || JulianDate.greaterThan(start, sampleStart)) { sampleStart = start; } if (!hasLeadTime || JulianDate.lessThan(stop, sampleStop)) { sampleStop = stop; } } show = JulianDate.lessThan(sampleStart, sampleStop); } } if (!show) { //don't bother creating or updating anything else if (defined(polyline)) { this._unusedIndexes.push(item.index); item.polyline = undefined; polyline.show = false; item.index = undefined; } return; } if (!defined(polyline)) { var unusedIndexes = this._unusedIndexes; var length = unusedIndexes.length; if (length > 0) { var index = unusedIndexes.pop(); polyline = this._polylineCollection.get(index); item.index = index; } else { item.index = this._polylineCollection.length; polyline = this._polylineCollection.add(); } polyline.id = entity; item.polyline = polyline; } var resolution = Property.getValueOrDefault( pathGraphics._resolution, time, defaultResolution ); polyline.show = true; polyline.positions = subSample( positionProperty, sampleStart, sampleStop, time, this._referenceFrame, resolution, polyline.positions.slice() ); polyline.material = MaterialProperty.getValue( time, pathGraphics._material, polyline.material ); polyline.width = Property.getValueOrDefault( pathGraphics._width, time, defaultWidth ); polyline.distanceDisplayCondition = Property.getValueOrUndefined( pathGraphics._distanceDisplayCondition, time, polyline.distanceDisplayCondition ); }; PolylineUpdater.prototype.removeObject = function (item) { var polyline = item.polyline; if (defined(polyline)) { this._unusedIndexes.push(item.index); item.polyline = undefined; polyline.show = false; polyline.id = undefined; item.index = undefined; } }; PolylineUpdater.prototype.destroy = function () { this._scene.primitives.remove(this._polylineCollection); return destroyObject(this); }; /** * A {@link Visualizer} which maps {@link Entity#path} to a {@link Polyline}. * @alias PathVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function PathVisualizer(scene, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(scene)) { throw new DeveloperError("scene is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( PathVisualizer.prototype._onCollectionChanged, this ); this._scene = scene; this._updaters = {}; this._entityCollection = entityCollection; this._items = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates all of the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ PathVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var updaters = this._updaters; for (var key in updaters) { if (updaters.hasOwnProperty(key)) { updaters[key].update(time); } } var items = this._items.values; if ( items.length === 0 && defined(this._updaters) && Object.keys(this._updaters).length > 0 ) { for (var u in updaters) { if (updaters.hasOwnProperty(u)) { updaters[u].destroy(); } } this._updaters = {}; } for (var i = 0, len = items.length; i < len; i++) { var item = items[i]; var entity = item.entity; var positionProperty = entity._position; var lastUpdater = item.updater; var frameToVisualize = ReferenceFrame$1.FIXED; if (this._scene.mode === SceneMode$1.SCENE3D) { frameToVisualize = positionProperty.referenceFrame; } var currentUpdater = this._updaters[frameToVisualize]; if (lastUpdater === currentUpdater && defined(currentUpdater)) { currentUpdater.updateObject(time, item); continue; } if (defined(lastUpdater)) { lastUpdater.removeObject(item); } if (!defined(currentUpdater)) { currentUpdater = new PolylineUpdater(this._scene, frameToVisualize); currentUpdater.update(time); this._updaters[frameToVisualize] = currentUpdater; } item.updater = currentUpdater; if (defined(currentUpdater)) { currentUpdater.updateObject(time, item); } } return true; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ PathVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ PathVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( PathVisualizer.prototype._onCollectionChanged, this ); var updaters = this._updaters; for (var key in updaters) { if (updaters.hasOwnProperty(key)) { updaters[key].destroy(); } } return destroyObject(this); }; PathVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var item; var items = this._items; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._path) && defined(entity._position)) { items.set(entity.id, new EntityData$1(entity)); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._path) && defined(entity._position)) { if (!items.contains(entity.id)) { items.set(entity.id, new EntityData$1(entity)); } } else { item = items.get(entity.id); if (defined(item)) { if (defined(item.updater)) { item.updater.removeObject(item); } items.remove(entity.id); } } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; item = items.get(entity.id); if (defined(item)) { if (defined(item.updater)) { item.updater.removeObject(item); } items.remove(entity.id); } } }; //for testing PathVisualizer._subSample = subSample; var defaultColor$1 = Color.WHITE; var defaultOutlineColor = Color.BLACK; var defaultOutlineWidth = 0.0; var defaultPixelSize = 1.0; var defaultDisableDepthTestDistance = 0.0; var colorScratch$1 = new Color(); var positionScratch = new Cartesian3(); var outlineColorScratch = new Color(); var scaleByDistanceScratch = new NearFarScalar(); var translucencyByDistanceScratch = new NearFarScalar(); var distanceDisplayConditionScratch$1 = new DistanceDisplayCondition(); function EntityData(entity) { this.entity = entity; this.pointPrimitive = undefined; this.billboard = undefined; this.color = undefined; this.outlineColor = undefined; this.pixelSize = undefined; this.outlineWidth = undefined; } /** * A {@link Visualizer} which maps {@link Entity#point} to a {@link PointPrimitive}. * @alias PointVisualizer * @constructor * * @param {EntityCluster} entityCluster The entity cluster to manage the collection of billboards and optionally cluster with other entities. * @param {EntityCollection} entityCollection The entityCollection to visualize. */ function PointVisualizer(entityCluster, entityCollection) { //>>includeStart('debug', pragmas.debug); if (!defined(entityCluster)) { throw new DeveloperError("entityCluster is required."); } if (!defined(entityCollection)) { throw new DeveloperError("entityCollection is required."); } //>>includeEnd('debug'); entityCollection.collectionChanged.addEventListener( PointVisualizer.prototype._onCollectionChanged, this ); this._cluster = entityCluster; this._entityCollection = entityCollection; this._items = new AssociativeArray(); this._onCollectionChanged(entityCollection, entityCollection.values, [], []); } /** * Updates the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} This function always returns true. */ PointVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } //>>includeEnd('debug'); var items = this._items.values; var cluster = this._cluster; for (var i = 0, len = items.length; i < len; i++) { var item = items[i]; var entity = item.entity; var pointGraphics = entity._point; var pointPrimitive = item.pointPrimitive; var billboard = item.billboard; var heightReference = Property.getValueOrDefault( pointGraphics._heightReference, time, HeightReference$1.NONE ); var show = entity.isShowing && entity.isAvailable(time) && Property.getValueOrDefault(pointGraphics._show, time, true); var position; if (show) { position = Property.getValueOrUndefined( entity._position, time, positionScratch ); show = defined(position); } if (!show) { returnPrimitive(item, entity, cluster); continue; } if (!Property.isConstant(entity._position)) { cluster._clusterDirty = true; } var needsRedraw = false; var updateClamping = false; if (heightReference !== HeightReference$1.NONE && !defined(billboard)) { if (defined(pointPrimitive)) { returnPrimitive(item, entity, cluster); pointPrimitive = undefined; } billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = undefined; item.billboard = billboard; needsRedraw = true; // If this new billboard happens to have a position and height reference that match our new values, // billboard._updateClamping will not be called automatically. That's a problem because the clamped // height may be based on different terrain than is now loaded. So we'll manually call // _updateClamping below. updateClamping = Cartesian3.equals(billboard.position, position) && billboard.heightReference === heightReference; } else if ( heightReference === HeightReference$1.NONE && !defined(pointPrimitive) ) { if (defined(billboard)) { returnPrimitive(item, entity, cluster); billboard = undefined; } pointPrimitive = cluster.getPoint(entity); pointPrimitive.id = entity; item.pointPrimitive = pointPrimitive; } if (defined(pointPrimitive)) { pointPrimitive.show = true; pointPrimitive.position = position; pointPrimitive.scaleByDistance = Property.getValueOrUndefined( pointGraphics._scaleByDistance, time, scaleByDistanceScratch ); pointPrimitive.translucencyByDistance = Property.getValueOrUndefined( pointGraphics._translucencyByDistance, time, translucencyByDistanceScratch ); pointPrimitive.color = Property.getValueOrDefault( pointGraphics._color, time, defaultColor$1, colorScratch$1 ); pointPrimitive.outlineColor = Property.getValueOrDefault( pointGraphics._outlineColor, time, defaultOutlineColor, outlineColorScratch ); pointPrimitive.outlineWidth = Property.getValueOrDefault( pointGraphics._outlineWidth, time, defaultOutlineWidth ); pointPrimitive.pixelSize = Property.getValueOrDefault( pointGraphics._pixelSize, time, defaultPixelSize ); pointPrimitive.distanceDisplayCondition = Property.getValueOrUndefined( pointGraphics._distanceDisplayCondition, time, distanceDisplayConditionScratch$1 ); pointPrimitive.disableDepthTestDistance = Property.getValueOrDefault( pointGraphics._disableDepthTestDistance, time, defaultDisableDepthTestDistance ); } else if (defined(billboard)) { billboard.show = true; billboard.position = position; billboard.scaleByDistance = Property.getValueOrUndefined( pointGraphics._scaleByDistance, time, scaleByDistanceScratch ); billboard.translucencyByDistance = Property.getValueOrUndefined( pointGraphics._translucencyByDistance, time, translucencyByDistanceScratch ); billboard.distanceDisplayCondition = Property.getValueOrUndefined( pointGraphics._distanceDisplayCondition, time, distanceDisplayConditionScratch$1 ); billboard.disableDepthTestDistance = Property.getValueOrDefault( pointGraphics._disableDepthTestDistance, time, defaultDisableDepthTestDistance ); billboard.heightReference = heightReference; var newColor = Property.getValueOrDefault( pointGraphics._color, time, defaultColor$1, colorScratch$1 ); var newOutlineColor = Property.getValueOrDefault( pointGraphics._outlineColor, time, defaultOutlineColor, outlineColorScratch ); var newOutlineWidth = Math.round( Property.getValueOrDefault( pointGraphics._outlineWidth, time, defaultOutlineWidth ) ); var newPixelSize = Math.max( 1, Math.round( Property.getValueOrDefault( pointGraphics._pixelSize, time, defaultPixelSize ) ) ); if (newOutlineWidth > 0) { billboard.scale = 1.0; needsRedraw = needsRedraw || // newOutlineWidth !== item.outlineWidth || // newPixelSize !== item.pixelSize || // !Color.equals(newColor, item.color) || // !Color.equals(newOutlineColor, item.outlineColor); } else { billboard.scale = newPixelSize / 50.0; newPixelSize = 50.0; needsRedraw = needsRedraw || // newOutlineWidth !== item.outlineWidth || // !Color.equals(newColor, item.color) || // !Color.equals(newOutlineColor, item.outlineColor); } if (needsRedraw) { item.color = Color.clone(newColor, item.color); item.outlineColor = Color.clone(newOutlineColor, item.outlineColor); item.pixelSize = newPixelSize; item.outlineWidth = newOutlineWidth; var centerAlpha = newColor.alpha; var cssColor = newColor.toCssColorString(); var cssOutlineColor = newOutlineColor.toCssColorString(); var textureId = JSON.stringify([ cssColor, newPixelSize, cssOutlineColor, newOutlineWidth, ]); billboard.setImage( textureId, createBillboardPointCallback( centerAlpha, cssColor, cssOutlineColor, newOutlineWidth, newPixelSize ) ); } if (updateClamping) { billboard._updateClamping(); } } } return true; }; /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ PointVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required."); } if (!defined(result)) { throw new DeveloperError("result is required."); } //>>includeEnd('debug'); var item = this._items.get(entity.id); if ( !defined(item) || !(defined(item.pointPrimitive) || defined(item.billboard)) ) { return BoundingSphereState$1.FAILED; } if (defined(item.pointPrimitive)) { result.center = Cartesian3.clone( item.pointPrimitive.position, result.center ); } else { var billboard = item.billboard; if (!defined(billboard._clampedPosition)) { return BoundingSphereState$1.PENDING; } result.center = Cartesian3.clone(billboard._clampedPosition, result.center); } result.radius = 0; return BoundingSphereState$1.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ PointVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ PointVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( PointVisualizer.prototype._onCollectionChanged, this ); var entities = this._entityCollection.values; for (var i = 0; i < entities.length; i++) { this._cluster.removePoint(entities[i]); } return destroyObject(this); }; PointVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed, changed ) { var i; var entity; var items = this._items; var cluster = this._cluster; for (i = added.length - 1; i > -1; i--) { entity = added[i]; if (defined(entity._point) && defined(entity._position)) { items.set(entity.id, new EntityData(entity)); } } for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; if (defined(entity._point) && defined(entity._position)) { if (!items.contains(entity.id)) { items.set(entity.id, new EntityData(entity)); } } else { returnPrimitive(items.get(entity.id), entity, cluster); items.remove(entity.id); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; returnPrimitive(items.get(entity.id), entity, cluster); items.remove(entity.id); } }; function returnPrimitive(item, entity, cluster) { if (defined(item)) { var pointPrimitive = item.pointPrimitive; if (defined(pointPrimitive)) { item.pointPrimitive = undefined; cluster.removePoint(entity); return; } var billboard = item.billboard; if (defined(billboard)) { item.billboard = undefined; cluster.removeBillboard(entity); } } } var defaultZIndex = new ConstantProperty(0); //We use this object to create one polyline collection per-scene. var polylineCollections = {}; var scratchColor$4 = new Color(); var defaultMaterial = new ColorMaterialProperty(Color.WHITE); var defaultShow = new ConstantProperty(true); var defaultShadows = new ConstantProperty(ShadowMode$1.DISABLED); var defaultDistanceDisplayCondition$1 = new ConstantProperty( new DistanceDisplayCondition() ); var defaultClassificationType = new ConstantProperty(ClassificationType$1.BOTH); function GeometryOptions() { this.vertexFormat = undefined; this.positions = undefined; this.width = undefined; this.arcType = undefined; this.granularity = undefined; } function GroundGeometryOptions() { this.positions = undefined; this.width = undefined; this.arcType = undefined; this.granularity = undefined; } /** * A {@link GeometryUpdater} for polylines. * Clients do not normally create this class directly, but instead rely on {@link DataSourceDisplay}. * @alias PolylineGeometryUpdater * @constructor * * @param {Entity} entity The entity containing the geometry to be visualized. * @param {Scene} scene The scene where visualization is taking place. */ function PolylineGeometryUpdater(entity, scene) { //>>includeStart('debug', pragmas.debug); if (!defined(entity)) { throw new DeveloperError("entity is required"); } if (!defined(scene)) { throw new DeveloperError("scene is required"); } //>>includeEnd('debug'); this._entity = entity; this._scene = scene; this._entitySubscription = entity.definitionChanged.addEventListener( PolylineGeometryUpdater.prototype._onEntityPropertyChanged, this ); this._fillEnabled = false; this._dynamic = false; this._geometryChanged = new Event(); this._showProperty = undefined; this._materialProperty = undefined; this._shadowsProperty = undefined; this._distanceDisplayConditionProperty = undefined; this._classificationTypeProperty = undefined; this._depthFailMaterialProperty = undefined; this._geometryOptions = new GeometryOptions(); this._groundGeometryOptions = new GroundGeometryOptions(); this._id = "polyline-" + entity.id; this._clampToGround = false; this._supportsPolylinesOnTerrain = Entity.supportsPolylinesOnTerrain(scene); this._zIndex = 0; this._onEntityPropertyChanged(entity, "polyline", entity.polyline, undefined); } Object.defineProperties(PolylineGeometryUpdater.prototype, { /** * Gets the unique ID associated with this updater * @memberof PolylineGeometryUpdater.prototype * @type {String} * @readonly */ id: { get: function () { return this._id; }, }, /** * Gets the entity associated with this geometry. * @memberof PolylineGeometryUpdater.prototype * * @type {Entity} * @readonly */ entity: { get: function () { return this._entity; }, }, /** * Gets a value indicating if the geometry has a fill component. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ fillEnabled: { get: function () { return this._fillEnabled; }, }, /** * Gets a value indicating if fill visibility varies with simulation time. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ hasConstantFill: { get: function () { return ( !this._fillEnabled || (!defined(this._entity.availability) && Property.isConstant(this._showProperty)) ); }, }, /** * Gets the material property used to fill the geometry. * @memberof PolylineGeometryUpdater.prototype * * @type {MaterialProperty} * @readonly */ fillMaterialProperty: { get: function () { return this._materialProperty; }, }, /** * Gets the material property used to fill the geometry when it fails the depth test. * @memberof PolylineGeometryUpdater.prototype * * @type {MaterialProperty} * @readonly */ depthFailMaterialProperty: { get: function () { return this._depthFailMaterialProperty; }, }, /** * Gets a value indicating if the geometry has an outline component. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ outlineEnabled: { value: false, }, /** * Gets a value indicating if outline visibility varies with simulation time. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ hasConstantOutline: { value: true, }, /** * Gets the {@link Color} property for the geometry outline. * @memberof PolylineGeometryUpdater.prototype * * @type {Property} * @readonly */ outlineColorProperty: { value: undefined, }, /** * Gets the property specifying whether the geometry * casts or receives shadows from light sources. * @memberof PolylineGeometryUpdater.prototype * * @type {Property} * @readonly */ shadowsProperty: { get: function () { return this._shadowsProperty; }, }, /** * Gets or sets the {@link DistanceDisplayCondition} Property specifying at what distance from the camera that this geometry will be displayed. * @memberof PolylineGeometryUpdater.prototype * * @type {Property} * @readonly */ distanceDisplayConditionProperty: { get: function () { return this._distanceDisplayConditionProperty; }, }, /** * Gets or sets the {@link ClassificationType} Property specifying if this geometry will classify terrain, 3D Tiles, or both when on the ground. * @memberof PolylineGeometryUpdater.prototype * * @type {Property} * @readonly */ classificationTypeProperty: { get: function () { return this._classificationTypeProperty; }, }, /** * Gets a value indicating if the geometry is time-varying. * If true, all visualization is delegated to the {@link DynamicGeometryUpdater} * returned by GeometryUpdater#createDynamicUpdater. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ isDynamic: { get: function () { return this._dynamic; }, }, /** * Gets a value indicating if the geometry is closed. * This property is only valid for static geometry. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ isClosed: { value: false, }, /** * Gets an event that is raised whenever the public properties * of this updater change. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ geometryChanged: { get: function () { return this._geometryChanged; }, }, /** * Gets a value indicating if the path of the line. * @memberof PolylineGeometryUpdater.prototype * * @type {ArcType} * @readonly */ arcType: { get: function () { return this._arcType; }, }, /** * Gets a value indicating if the geometry is clamped to the ground. * Returns false if polylines on terrain is not supported. * @memberof PolylineGeometryUpdater.prototype * * @type {Boolean} * @readonly */ clampToGround: { get: function () { return this._clampToGround && this._supportsPolylinesOnTerrain; }, }, /** * Gets the zindex * @type {Number} * @memberof PolylineGeometryUpdater.prototype * @readonly */ zIndex: { get: function () { return this._zIndex; }, }, }); /** * Checks if the geometry is outlined at the provided time. * * @param {JulianDate} time The time for which to retrieve visibility. * @returns {Boolean} true if geometry is outlined at the provided time, false otherwise. */ PolylineGeometryUpdater.prototype.isOutlineVisible = function (time) { return false; }; /** * Checks if the geometry is filled at the provided time. * * @param {JulianDate} time The time for which to retrieve visibility. * @returns {Boolean} true if geometry is filled at the provided time, false otherwise. */ PolylineGeometryUpdater.prototype.isFilled = function (time) { var entity = this._entity; var visible = this._fillEnabled && entity.isAvailable(time) && this._showProperty.getValue(time); return defaultValue(visible, false); }; /** * Creates the geometry instance which represents the fill of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the filled portion of the geometry. * * @exception {DeveloperError} This instance does not represent a filled geometry. */ PolylineGeometryUpdater.prototype.createFillGeometryInstance = function (time) { //>>includeStart('debug', pragmas.debug); if (!defined(time)) { throw new DeveloperError("time is required."); } if (!this._fillEnabled) { throw new DeveloperError( "This instance does not represent a filled geometry." ); } //>>includeEnd('debug'); var entity = this._entity; var isAvailable = entity.isAvailable(time); var show = new ShowGeometryInstanceAttribute( isAvailable && entity.isShowing && this._showProperty.getValue(time) ); var distanceDisplayCondition = this._distanceDisplayConditionProperty.getValue( time ); var distanceDisplayConditionAttribute = DistanceDisplayConditionGeometryInstanceAttribute.fromDistanceDisplayCondition( distanceDisplayCondition ); var attributes = { show: show, distanceDisplayCondition: distanceDisplayConditionAttribute, }; var currentColor; if (this._materialProperty instanceof ColorMaterialProperty) { if ( defined(this._materialProperty.color) && (this._materialProperty.color.isConstant || isAvailable) ) { currentColor = this._materialProperty.color.getValue(time, scratchColor$4); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.color = ColorGeometryInstanceAttribute.fromColor(currentColor); } if (this.clampToGround) { return new GeometryInstance({ id: entity, geometry: new GroundPolylineGeometry(this._groundGeometryOptions), attributes: attributes, }); } if ( defined(this._depthFailMaterialProperty) && this._depthFailMaterialProperty instanceof ColorMaterialProperty ) { if ( defined(this._depthFailMaterialProperty.color) && (this._depthFailMaterialProperty.color.isConstant || isAvailable) ) { currentColor = this._depthFailMaterialProperty.color.getValue( time, scratchColor$4 ); } if (!defined(currentColor)) { currentColor = Color.WHITE; } attributes.depthFailColor = ColorGeometryInstanceAttribute.fromColor( currentColor ); } return new GeometryInstance({ id: entity, geometry: new PolylineGeometry(this._geometryOptions), attributes: attributes, }); }; /** * Creates the geometry instance which represents the outline of the geometry. * * @param {JulianDate} time The time to use when retrieving initial attribute values. * @returns {GeometryInstance} The geometry instance representing the outline portion of the geometry. * * @exception {DeveloperError} This instance does not represent an outlined geometry. */ PolylineGeometryUpdater.prototype.createOutlineGeometryInstance = function ( time ) { //>>includeStart('debug', pragmas.debug); throw new DeveloperError( "This instance does not represent an outlined geometry." ); //>>includeEnd('debug'); }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ PolylineGeometryUpdater.prototype.isDestroyed = function () { return false; }; /** * Destroys and resources used by the object. Once an object is destroyed, it should not be used. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */ PolylineGeometryUpdater.prototype.destroy = function () { this._entitySubscription(); destroyObject(this); }; PolylineGeometryUpdater.prototype._onEntityPropertyChanged = function ( entity, propertyName, newValue, oldValue ) { if (!(propertyName === "availability" || propertyName === "polyline")) { return; } var polyline = this._entity.polyline; if (!defined(polyline)) { if (this._fillEnabled) { this._fillEnabled = false; this._geometryChanged.raiseEvent(this); } return; } var positionsProperty = polyline.positions; var show = polyline.show; if ( (defined(show) && show.isConstant && !show.getValue(Iso8601.MINIMUM_VALUE)) || // !defined(positionsProperty) ) { if (this._fillEnabled) { this._fillEnabled = false; this._geometryChanged.raiseEvent(this); } return; } var zIndex = polyline.zIndex; var material = defaultValue(polyline.material, defaultMaterial); var isColorMaterial = material instanceof ColorMaterialProperty; this._materialProperty = material; this._depthFailMaterialProperty = polyline.depthFailMaterial; this._showProperty = defaultValue(show, defaultShow); this._shadowsProperty = defaultValue(polyline.shadows, defaultShadows); this._distanceDisplayConditionProperty = defaultValue( polyline.distanceDisplayCondition, defaultDistanceDisplayCondition$1 ); this._classificationTypeProperty = defaultValue( polyline.classificationType, defaultClassificationType ); this._fillEnabled = true; this._zIndex = defaultValue(zIndex, defaultZIndex); var width = polyline.width; var arcType = polyline.arcType; var clampToGround = polyline.clampToGround; var granularity = polyline.granularity; if ( !positionsProperty.isConstant || !Property.isConstant(width) || !Property.isConstant(arcType) || !Property.isConstant(granularity) || !Property.isConstant(clampToGround) || !Property.isConstant(zIndex) ) { if (!this._dynamic) { this._dynamic = true; this._geometryChanged.raiseEvent(this); } } else { var geometryOptions = this._geometryOptions; var positions = positionsProperty.getValue( Iso8601.MINIMUM_VALUE, geometryOptions.positions ); //Because of the way we currently handle reference properties, //we can't automatically assume the positions are always valid. if (!defined(positions) || positions.length < 2) { if (this._fillEnabled) { this._fillEnabled = false; this._geometryChanged.raiseEvent(this); } return; } var vertexFormat; if ( isColorMaterial && (!defined(this._depthFailMaterialProperty) || this._depthFailMaterialProperty instanceof ColorMaterialProperty) ) { vertexFormat = PolylineColorAppearance.VERTEX_FORMAT; } else { vertexFormat = PolylineMaterialAppearance.VERTEX_FORMAT; } geometryOptions.vertexFormat = vertexFormat; geometryOptions.positions = positions; geometryOptions.width = defined(width) ? width.getValue(Iso8601.MINIMUM_VALUE) : undefined; geometryOptions.arcType = defined(arcType) ? arcType.getValue(Iso8601.MINIMUM_VALUE) : undefined; geometryOptions.granularity = defined(granularity) ? granularity.getValue(Iso8601.MINIMUM_VALUE) : undefined; var groundGeometryOptions = this._groundGeometryOptions; groundGeometryOptions.positions = positions; groundGeometryOptions.width = geometryOptions.width; groundGeometryOptions.arcType = geometryOptions.arcType; groundGeometryOptions.granularity = geometryOptions.granularity; this._clampToGround = defined(clampToGround) ? clampToGround.getValue(Iso8601.MINIMUM_VALUE) : false; if (!this._clampToGround && defined(zIndex)) { oneTimeWarning( "Entity polylines must have clampToGround: true when using zIndex. zIndex will be ignored." ); } this._dynamic = false; this._geometryChanged.raiseEvent(this); } }; /** * Creates the dynamic updater to be used when GeometryUpdater#isDynamic is true. * * @param {PrimitiveCollection} primitives The primitive collection to use. * @param {PrimitiveCollection|OrderedGroundPrimitiveCollection} groundPrimitives The primitive collection to use for ordered ground primitives. * @returns {DynamicGeometryUpdater} The dynamic updater used to update the geometry each frame. * * @exception {DeveloperError} This instance does not represent dynamic geometry. * @private */ PolylineGeometryUpdater.prototype.createDynamicUpdater = function ( primitives, groundPrimitives ) { //>>includeStart('debug', pragmas.debug); Check.defined("primitives", primitives); Check.defined("groundPrimitives", groundPrimitives); if (!this._dynamic) { throw new DeveloperError( "This instance does not represent dynamic geometry." ); } //>>includeEnd('debug'); return new DynamicGeometryUpdater(primitives, groundPrimitives, this); }; /** * @private */ var generateCartesianArcOptions = { positions: undefined, granularity: undefined, height: undefined, ellipsoid: undefined, }; function DynamicGeometryUpdater(primitives, groundPrimitives, geometryUpdater) { this._line = undefined; this._primitives = primitives; this._groundPrimitives = groundPrimitives; this._groundPolylinePrimitive = undefined; this._material = undefined; this._geometryUpdater = geometryUpdater; this._positions = []; } function getLine(dynamicGeometryUpdater) { if (defined(dynamicGeometryUpdater._line)) { return dynamicGeometryUpdater._line; } var sceneId = dynamicGeometryUpdater._geometryUpdater._scene.id; var polylineCollection = polylineCollections[sceneId]; var primitives = dynamicGeometryUpdater._primitives; if (!defined(polylineCollection) || polylineCollection.isDestroyed()) { polylineCollection = new PolylineCollection(); polylineCollections[sceneId] = polylineCollection; primitives.add(polylineCollection); } else if (!primitives.contains(polylineCollection)) { primitives.add(polylineCollection); } var line = polylineCollection.add(); line.id = dynamicGeometryUpdater._geometryUpdater._entity; dynamicGeometryUpdater._line = line; return line; } DynamicGeometryUpdater.prototype.update = function (time) { var geometryUpdater = this._geometryUpdater; var entity = geometryUpdater._entity; var polyline = entity.polyline; var positionsProperty = polyline.positions; var positions = Property.getValueOrUndefined( positionsProperty, time, this._positions ); // Synchronize with geometryUpdater for GroundPolylinePrimitive geometryUpdater._clampToGround = Property.getValueOrDefault( polyline._clampToGround, time, false ); geometryUpdater._groundGeometryOptions.positions = positions; geometryUpdater._groundGeometryOptions.width = Property.getValueOrDefault( polyline._width, time, 1 ); geometryUpdater._groundGeometryOptions.arcType = Property.getValueOrDefault( polyline._arcType, time, ArcType$1.GEODESIC ); geometryUpdater._groundGeometryOptions.granularity = Property.getValueOrDefault( polyline._granularity, time, 9999 ); var groundPrimitives = this._groundPrimitives; if (defined(this._groundPolylinePrimitive)) { groundPrimitives.remove(this._groundPolylinePrimitive); // destroys by default this._groundPolylinePrimitive = undefined; } if (geometryUpdater.clampToGround) { if ( !entity.isShowing || !entity.isAvailable(time) || !Property.getValueOrDefault(polyline._show, time, true) ) { return; } if (!defined(positions) || positions.length < 2) { return; } var fillMaterialProperty = geometryUpdater.fillMaterialProperty; var appearance; if (fillMaterialProperty instanceof ColorMaterialProperty) { appearance = new PolylineColorAppearance(); } else { var material = MaterialProperty.getValue( time, fillMaterialProperty, this._material ); appearance = new PolylineMaterialAppearance({ material: material, translucent: material.isTranslucent(), }); this._material = material; } this._groundPolylinePrimitive = groundPrimitives.add( new GroundPolylinePrimitive({ geometryInstances: geometryUpdater.createFillGeometryInstance(time), appearance: appearance, classificationType: geometryUpdater.classificationTypeProperty.getValue( time ), asynchronous: false, }), Property.getValueOrUndefined(geometryUpdater.zIndex, time) ); // Hide the polyline in the collection, if any if (defined(this._line)) { this._line.show = false; } return; } var line = getLine(this); if ( !entity.isShowing || !entity.isAvailable(time) || !Property.getValueOrDefault(polyline._show, time, true) ) { line.show = false; return; } if (!defined(positions) || positions.length < 2) { line.show = false; return; } var arcType = ArcType$1.GEODESIC; arcType = Property.getValueOrDefault(polyline._arcType, time, arcType); var globe = geometryUpdater._scene.globe; if (arcType !== ArcType$1.NONE && defined(globe)) { generateCartesianArcOptions.ellipsoid = globe.ellipsoid; generateCartesianArcOptions.positions = positions; generateCartesianArcOptions.granularity = Property.getValueOrUndefined( polyline._granularity, time ); generateCartesianArcOptions.height = PolylinePipeline.extractHeights( positions, globe.ellipsoid ); if (arcType === ArcType$1.GEODESIC) { positions = PolylinePipeline.generateCartesianArc( generateCartesianArcOptions ); } else { positions = PolylinePipeline.generateCartesianRhumbArc( generateCartesianArcOptions ); } } line.show = true; line.positions = positions.slice(); line.material = MaterialProperty.getValue( time, geometryUpdater.fillMaterialProperty, line.material ); line.width = Property.getValueOrDefault(polyline._width, time, 1); line.distanceDisplayCondition = Property.getValueOrUndefined( polyline._distanceDisplayCondition, time, line.distanceDisplayCondition ); }; DynamicGeometryUpdater.prototype.getBoundingSphere = function (result) { //>>includeStart('debug', pragmas.debug); Check.defined("result", result); //>>includeEnd('debug'); if (!this._geometryUpdater.clampToGround) { var line = getLine(this); if (line.show && line.positions.length > 0) { BoundingSphere.fromPoints(line.positions, result); return BoundingSphereState$1.DONE; } } else { var groundPolylinePrimitive = this._groundPolylinePrimitive; if ( defined(groundPolylinePrimitive) && groundPolylinePrimitive.show && groundPolylinePrimitive.ready ) { var attributes = groundPolylinePrimitive.getGeometryInstanceAttributes( this._geometryUpdater._entity ); if (defined(attributes) && defined(attributes.boundingSphere)) { BoundingSphere.clone(attributes.boundingSphere, result); return BoundingSphereState$1.DONE; } } if (defined(groundPolylinePrimitive) && !groundPolylinePrimitive.ready) { return BoundingSphereState$1.PENDING; } return BoundingSphereState$1.DONE; } return BoundingSphereState$1.FAILED; }; DynamicGeometryUpdater.prototype.isDestroyed = function () { return false; }; DynamicGeometryUpdater.prototype.destroy = function () { var geometryUpdater = this._geometryUpdater; var sceneId = geometryUpdater._scene.id; var polylineCollection = polylineCollections[sceneId]; if (defined(polylineCollection)) { polylineCollection.remove(this._line); if (polylineCollection.length === 0) { this._primitives.removeAndDestroy(polylineCollection); delete polylineCollections[sceneId]; } } if (defined(this._groundPolylinePrimitive)) { this._groundPrimitives.remove(this._groundPolylinePrimitive); } destroyObject(this); }; var scratchColor$3 = new Color(); var distanceDisplayConditionScratch = new DistanceDisplayCondition(); var defaultDistanceDisplayCondition = new DistanceDisplayCondition(); // Encapsulates a Primitive and all the entities that it represents. function Batch( orderedGroundPrimitives, classificationType, materialProperty, zIndex, asynchronous ) { var appearanceType; if (materialProperty instanceof ColorMaterialProperty) { appearanceType = PolylineColorAppearance; } else { appearanceType = PolylineMaterialAppearance; } this.orderedGroundPrimitives = orderedGroundPrimitives; // scene level primitive collection this.classificationType = classificationType; this.appearanceType = appearanceType; this.materialProperty = materialProperty; this.updaters = new AssociativeArray(); this.createPrimitive = true; this.primitive = undefined; // a GroundPolylinePrimitive encapsulating all the entities this.oldPrimitive = undefined; this.geometry = new AssociativeArray(); this.material = undefined; this.updatersWithAttributes = new AssociativeArray(); this.attributes = new AssociativeArray(); this.invalidated = false; this.removeMaterialSubscription = materialProperty.definitionChanged.addEventListener( Batch.prototype.onMaterialChanged, this ); this.subscriptions = new AssociativeArray(); this.showsUpdated = new AssociativeArray(); this.zIndex = zIndex; this._asynchronous = asynchronous; } Batch.prototype.onMaterialChanged = function () { this.invalidated = true; }; // Check if the given updater's material is compatible with this batch Batch.prototype.isMaterial = function (updater) { var material = this.materialProperty; var updaterMaterial = updater.fillMaterialProperty; if ( updaterMaterial === material || (updaterMaterial instanceof ColorMaterialProperty && material instanceof ColorMaterialProperty) ) { return true; } return defined(material) && material.equals(updaterMaterial); }; Batch.prototype.add = function (time, updater, geometryInstance) { var id = updater.id; this.updaters.set(id, updater); this.geometry.set(id, geometryInstance); // Updaters with dynamic attributes must be tracked separately, may exit the batch if ( !updater.hasConstantFill || !updater.fillMaterialProperty.isConstant || !Property.isConstant(updater.distanceDisplayConditionProperty) ) { this.updatersWithAttributes.set(id, updater); } else { var that = this; // Listen for show changes. These will be synchronized in updateShows. this.subscriptions.set( id, updater.entity.definitionChanged.addEventListener(function ( entity, propertyName, newValue, oldValue ) { if (propertyName === "isShowing") { that.showsUpdated.set(updater.id, updater); } }) ); } this.createPrimitive = true; }; Batch.prototype.remove = function (updater) { var id = updater.id; this.createPrimitive = this.geometry.remove(id) || this.createPrimitive; if (this.updaters.remove(id)) { this.updatersWithAttributes.remove(id); var unsubscribe = this.subscriptions.get(id); if (defined(unsubscribe)) { unsubscribe(); this.subscriptions.remove(id); } return true; } return false; }; Batch.prototype.update = function (time) { var isUpdated = true; var primitive = this.primitive; var orderedGroundPrimitives = this.orderedGroundPrimitives; var geometries = this.geometry.values; var i; if (this.createPrimitive) { var geometriesLength = geometries.length; if (geometriesLength > 0) { if (defined(primitive)) { // Keep a handle to the old primitive so it can be removed when the updated version is ready. if (!defined(this.oldPrimitive)) { this.oldPrimitive = primitive; } else { // For if the new primitive changes again before it is ready. orderedGroundPrimitives.remove(primitive); } } primitive = new GroundPolylinePrimitive({ show: false, asynchronous: this._asynchronous, geometryInstances: geometries.slice(), appearance: new this.appearanceType(), classificationType: this.classificationType, }); if (this.appearanceType === PolylineMaterialAppearance) { this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); primitive.appearance.material = this.material; } orderedGroundPrimitives.add(primitive, this.zIndex); isUpdated = false; } else { if (defined(primitive)) { orderedGroundPrimitives.remove(primitive); primitive = undefined; } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { orderedGroundPrimitives.remove(oldPrimitive); this.oldPrimitive = undefined; } } this.attributes.removeAll(); this.primitive = primitive; this.createPrimitive = false; } else if (defined(primitive) && primitive.ready) { primitive.show = true; if (defined(this.oldPrimitive)) { orderedGroundPrimitives.remove(this.oldPrimitive); this.oldPrimitive = undefined; } if (this.appearanceType === PolylineMaterialAppearance) { this.material = MaterialProperty.getValue( time, this.materialProperty, this.material ); this.primitive.appearance.material = this.material; } var updatersWithAttributes = this.updatersWithAttributes.values; var length = updatersWithAttributes.length; for (i = 0; i < length; i++) { var updater = updatersWithAttributes[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } if (!updater.fillMaterialProperty.isConstant) { var colorProperty = updater.fillMaterialProperty.color; var resultColor = Property.getValueOrDefault( colorProperty, time, Color.WHITE, scratchColor$3 ); if (!Color.equals(attributes._lastColor, resultColor)) { attributes._lastColor = Color.clone( resultColor, attributes._lastColor ); attributes.color = ColorGeometryInstanceAttribute.toValue( resultColor, attributes.color ); } } var show = entity.isShowing && (updater.hasConstantFill || updater.isFilled(time)); var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); } var distanceDisplayConditionProperty = updater.distanceDisplayConditionProperty; if (!Property.isConstant(distanceDisplayConditionProperty)) { var distanceDisplayCondition = Property.getValueOrDefault( distanceDisplayConditionProperty, time, defaultDistanceDisplayCondition, distanceDisplayConditionScratch ); if ( !DistanceDisplayCondition.equals( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ) ) { attributes._lastDistanceDisplayCondition = DistanceDisplayCondition.clone( distanceDisplayCondition, attributes._lastDistanceDisplayCondition ); attributes.distanceDisplayCondition = DistanceDisplayConditionGeometryInstanceAttribute.toValue( distanceDisplayCondition, attributes.distanceDisplayCondition ); } } } this.updateShows(primitive); } else if (defined(primitive) && !primitive.ready) { isUpdated = false; } return isUpdated; }; Batch.prototype.updateShows = function (primitive) { var showsUpdated = this.showsUpdated.values; var length = showsUpdated.length; for (var i = 0; i < length; i++) { var updater = showsUpdated[i]; var entity = updater.entity; var instance = this.geometry.get(updater.id); var attributes = this.attributes.get(instance.id.id); if (!defined(attributes)) { attributes = primitive.getGeometryInstanceAttributes(instance.id); this.attributes.set(instance.id.id, attributes); } var show = entity.isShowing; var currentShow = attributes.show[0] === 1; if (show !== currentShow) { attributes.show = ShowGeometryInstanceAttribute.toValue( show, attributes.show ); instance.attributes.show.value[0] = attributes.show[0]; } } this.showsUpdated.removeAll(); }; Batch.prototype.contains = function (updater) { return this.updaters.contains(updater.id); }; Batch.prototype.getBoundingSphere = function (updater, result) { var primitive = this.primitive; if (!primitive.ready) { return BoundingSphereState$1.PENDING; } var attributes = primitive.getGeometryInstanceAttributes(updater.entity); if ( !defined(attributes) || !defined(attributes.boundingSphere) || (defined(attributes.show) && attributes.show[0] === 0) ) { return BoundingSphereState$1.FAILED; } attributes.boundingSphere.clone(result); return BoundingSphereState$1.DONE; }; Batch.prototype.destroy = function () { var primitive = this.primitive; var orderedGroundPrimitives = this.orderedGroundPrimitives; if (defined(primitive)) { orderedGroundPrimitives.remove(primitive); } var oldPrimitive = this.oldPrimitive; if (defined(oldPrimitive)) { orderedGroundPrimitives.remove(oldPrimitive); } this.removeMaterialSubscription(); }; /** * @private */ function StaticGroundPolylinePerMaterialBatch( orderedGroundPrimitives, classificationType, asynchronous ) { this._items = []; this._orderedGroundPrimitives = orderedGroundPrimitives; this._classificationType = classificationType; this._asynchronous = defaultValue(asynchronous, true); } StaticGroundPolylinePerMaterialBatch.prototype.add = function (time, updater) { var items = this._items; var length = items.length; var geometryInstance = updater.createFillGeometryInstance(time); var zIndex = Property.getValueOrDefault(updater.zIndex, 0); // Check if the Entity represented by the updater has the same material or a material representable with per-instance color. for (var i = 0; i < length; ++i) { var item = items[i]; if (item.isMaterial(updater) && item.zIndex === zIndex) { item.add(time, updater, geometryInstance); return; } } // If a compatible batch wasn't found, create a new batch. var batch = new Batch( this._orderedGroundPrimitives, this._classificationType, updater.fillMaterialProperty, zIndex, this._asynchronous ); batch.add(time, updater, geometryInstance); items.push(batch); }; StaticGroundPolylinePerMaterialBatch.prototype.remove = function (updater) { var items = this._items; var length = items.length; for (var i = length - 1; i >= 0; i--) { var item = items[i]; if (item.remove(updater)) { if (item.updaters.length === 0) { items.splice(i, 1); item.destroy(); } break; } } }; StaticGroundPolylinePerMaterialBatch.prototype.update = function (time) { var i; var items = this._items; var length = items.length; for (i = length - 1; i >= 0; i--) { var item = items[i]; if (item.invalidated) { items.splice(i, 1); var updaters = item.updaters.values; var updatersLength = updaters.length; for (var h = 0; h < updatersLength; h++) { this.add(time, updaters[h]); } item.destroy(); } } var isUpdated = true; for (i = 0; i < items.length; i++) { isUpdated = items[i].update(time) && isUpdated; } return isUpdated; }; StaticGroundPolylinePerMaterialBatch.prototype.getBoundingSphere = function ( updater, result ) { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { var item = items[i]; if (item.contains(updater)) { return item.getBoundingSphere(updater, result); } } return BoundingSphereState$1.FAILED; }; StaticGroundPolylinePerMaterialBatch.prototype.removeAllPrimitives = function () { var items = this._items; var length = items.length; for (var i = 0; i < length; i++) { items[i].destroy(); } this._items.length = 0; }; var emptyArray = []; function removeUpdater(that, updater) { //We don't keep track of which batch an updater is in, so just remove it from all of them. var batches = that._batches; var length = batches.length; for (var i = 0; i < length; i++) { batches[i].remove(updater); } } function insertUpdaterIntoBatch(that, time, updater) { if (updater.isDynamic) { that._dynamicBatch.add(time, updater); return; } if (updater.clampToGround && updater.fillEnabled) { // Also checks for support var classificationType = updater.classificationTypeProperty.getValue(time); that._groundBatches[classificationType].add(time, updater); return; } var shadows; if (updater.fillEnabled) { shadows = updater.shadowsProperty.getValue(time); } var multiplier = 0; if (defined(updater.depthFailMaterialProperty)) { multiplier = updater.depthFailMaterialProperty instanceof ColorMaterialProperty ? 1 : 2; } var index; if (defined(shadows)) { index = shadows + multiplier * ShadowMode$1.NUMBER_OF_SHADOW_MODES; } if (updater.fillEnabled) { if (updater.fillMaterialProperty instanceof ColorMaterialProperty) { that._colorBatches[index].add(time, updater); } else { that._materialBatches[index].add(time, updater); } } } /** * A visualizer for polylines represented by {@link Primitive} instances. * @alias PolylineVisualizer * @constructor * * @param {Scene} scene The scene the primitives will be rendered in. * @param {EntityCollection} entityCollection The entityCollection to visualize. * @param {PrimitiveCollection} [primitives=scene.primitives] A collection to add primitives related to the entities * @param {PrimitiveCollection} [groundPrimitives=scene.groundPrimitives] A collection to add ground primitives related to the entities */ function PolylineVisualizer( scene, entityCollection, primitives, groundPrimitives ) { //>>includeStart('debug', pragmas.debug); Check.defined("scene", scene); Check.defined("entityCollection", entityCollection); //>>includeEnd('debug'); groundPrimitives = defaultValue(groundPrimitives, scene.groundPrimitives); primitives = defaultValue(primitives, scene.primitives); this._scene = scene; this._primitives = primitives; this._entityCollection = undefined; this._addedObjects = new AssociativeArray(); this._removedObjects = new AssociativeArray(); this._changedObjects = new AssociativeArray(); var i; var numberOfShadowModes = ShadowMode$1.NUMBER_OF_SHADOW_MODES; this._colorBatches = new Array(numberOfShadowModes * 3); this._materialBatches = new Array(numberOfShadowModes * 3); for (i = 0; i < numberOfShadowModes; ++i) { this._colorBatches[i] = new StaticGeometryColorBatch( primitives, PolylineColorAppearance, undefined, false, i ); // no depth fail appearance this._materialBatches[i] = new StaticGeometryPerMaterialBatch( primitives, PolylineMaterialAppearance, undefined, false, i ); this._colorBatches[i + numberOfShadowModes] = new StaticGeometryColorBatch( primitives, PolylineColorAppearance, PolylineColorAppearance, false, i ); //depth fail appearance variations this._materialBatches[ i + numberOfShadowModes ] = new StaticGeometryPerMaterialBatch( primitives, PolylineMaterialAppearance, PolylineColorAppearance, false, i ); this._colorBatches[ i + numberOfShadowModes * 2 ] = new StaticGeometryColorBatch( primitives, PolylineColorAppearance, PolylineMaterialAppearance, false, i ); this._materialBatches[ i + numberOfShadowModes * 2 ] = new StaticGeometryPerMaterialBatch( primitives, PolylineMaterialAppearance, PolylineMaterialAppearance, false, i ); } this._dynamicBatch = new DynamicGeometryBatch(primitives, groundPrimitives); var numberOfClassificationTypes = ClassificationType$1.NUMBER_OF_CLASSIFICATION_TYPES; this._groundBatches = new Array(numberOfClassificationTypes); for (i = 0; i < numberOfClassificationTypes; ++i) { this._groundBatches[i] = new StaticGroundPolylinePerMaterialBatch( groundPrimitives, i ); } this._batches = this._colorBatches.concat( this._materialBatches, this._dynamicBatch, this._groundBatches ); this._subscriptions = new AssociativeArray(); this._updaters = new AssociativeArray(); this._entityCollection = entityCollection; entityCollection.collectionChanged.addEventListener( PolylineVisualizer.prototype._onCollectionChanged, this ); this._onCollectionChanged( entityCollection, entityCollection.values, emptyArray ); } /** * Updates all of the primitives created by this visualizer to match their * Entity counterpart at the given time. * * @param {JulianDate} time The time to update to. * @returns {Boolean} True if the visualizer successfully updated to the provided time, * false if the visualizer is waiting for asynchronous primitives to be created. */ PolylineVisualizer.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var addedObjects = this._addedObjects; var added = addedObjects.values; var removedObjects = this._removedObjects; var removed = removedObjects.values; var changedObjects = this._changedObjects; var changed = changedObjects.values; var i; var entity; var id; var updater; for (i = changed.length - 1; i > -1; i--) { entity = changed[i]; id = entity.id; updater = this._updaters.get(id); //If in a single update, an entity gets removed and a new instance //re-added with the same id, the updater no longer tracks the //correct entity, we need to both remove the old one and //add the new one, which is done by pushing the entity //onto the removed/added lists. if (updater.entity === entity) { removeUpdater(this, updater); insertUpdaterIntoBatch(this, time, updater); } else { removed.push(entity); added.push(entity); } } for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; updater = this._updaters.get(id); removeUpdater(this, updater); updater.destroy(); this._updaters.remove(id); this._subscriptions.get(id)(); this._subscriptions.remove(id); } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; updater = new PolylineGeometryUpdater(entity, this._scene); this._updaters.set(id, updater); insertUpdaterIntoBatch(this, time, updater); this._subscriptions.set( id, updater.geometryChanged.addEventListener( PolylineVisualizer._onGeometryChanged, this ) ); } addedObjects.removeAll(); removedObjects.removeAll(); changedObjects.removeAll(); var isUpdated = true; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { isUpdated = batches[i].update(time) && isUpdated; } return isUpdated; }; var getBoundingSphereArrayScratch$1 = []; var getBoundingSphereBoundingSphereScratch$1 = new BoundingSphere(); /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ PolylineVisualizer.prototype.getBoundingSphere = function (entity, result) { //>>includeStart('debug', pragmas.debug); Check.defined("entity", entity); Check.defined("result", result); //>>includeEnd('debug'); var boundingSpheres = getBoundingSphereArrayScratch$1; var tmp = getBoundingSphereBoundingSphereScratch$1; var count = 0; var state = BoundingSphereState$1.DONE; var batches = this._batches; var batchesLength = batches.length; var updater = this._updaters.get(entity.id); for (var i = 0; i < batchesLength; i++) { state = batches[i].getBoundingSphere(updater, tmp); if (state === BoundingSphereState$1.PENDING) { return BoundingSphereState$1.PENDING; } else if (state === BoundingSphereState$1.DONE) { boundingSpheres[count] = BoundingSphere.clone( tmp, boundingSpheres[count] ); count++; } } if (count === 0) { return BoundingSphereState$1.FAILED; } boundingSpheres.length = count; BoundingSphere.fromBoundingSpheres(boundingSpheres, result); return BoundingSphereState$1.DONE; }; /** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */ PolylineVisualizer.prototype.isDestroyed = function () { return false; }; /** * Removes and destroys all primitives created by this instance. */ PolylineVisualizer.prototype.destroy = function () { this._entityCollection.collectionChanged.removeEventListener( PolylineVisualizer.prototype._onCollectionChanged, this ); this._addedObjects.removeAll(); this._removedObjects.removeAll(); var i; var batches = this._batches; var length = batches.length; for (i = 0; i < length; i++) { batches[i].removeAllPrimitives(); } var subscriptions = this._subscriptions.values; length = subscriptions.length; for (i = 0; i < length; i++) { subscriptions[i](); } this._subscriptions.removeAll(); return destroyObject(this); }; /** * @private */ PolylineVisualizer._onGeometryChanged = function (updater) { var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var entity = updater.entity; var id = entity.id; if (!defined(removedObjects.get(id)) && !defined(changedObjects.get(id))) { changedObjects.set(id, entity); } }; /** * @private */ PolylineVisualizer.prototype._onCollectionChanged = function ( entityCollection, added, removed ) { var addedObjects = this._addedObjects; var removedObjects = this._removedObjects; var changedObjects = this._changedObjects; var i; var id; var entity; for (i = removed.length - 1; i > -1; i--) { entity = removed[i]; id = entity.id; if (!addedObjects.remove(id)) { removedObjects.set(id, entity); changedObjects.remove(id); } } for (i = added.length - 1; i > -1; i--) { entity = added[i]; id = entity.id; if (removedObjects.remove(id)) { changedObjects.set(id, entity); } else { addedObjects.set(id, entity); } } }; /** * Visualizes a collection of {@link DataSource} instances. * @alias DataSourceDisplay * @constructor * * @param {Object} options Object with the following properties: * @param {Scene} options.scene The scene in which to display the data. * @param {DataSourceCollection} options.dataSourceCollection The data sources to display. * @param {DataSourceDisplay.VisualizersCallback} [options.visualizersCallback=DataSourceDisplay.defaultVisualizersCallback] * A function which creates an array of visualizers used for visualization. * If undefined, all standard visualizers are used. */ function DataSourceDisplay(options) { //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options", options); Check.typeOf.object("options.scene", options.scene); Check.typeOf.object( "options.dataSourceCollection", options.dataSourceCollection ); //>>includeEnd('debug'); GroundPrimitive.initializeTerrainHeights(); GroundPolylinePrimitive.initializeTerrainHeights(); var scene = options.scene; var dataSourceCollection = options.dataSourceCollection; this._eventHelper = new EventHelper(); this._eventHelper.add( dataSourceCollection.dataSourceAdded, this._onDataSourceAdded, this ); this._eventHelper.add( dataSourceCollection.dataSourceRemoved, this._onDataSourceRemoved, this ); this._eventHelper.add( dataSourceCollection.dataSourceMoved, this._onDataSourceMoved, this ); this._eventHelper.add(scene.postRender, this._postRender, this); this._dataSourceCollection = dataSourceCollection; this._scene = scene; this._visualizersCallback = defaultValue( options.visualizersCallback, DataSourceDisplay.defaultVisualizersCallback ); var primitivesAdded = false; var primitives = new PrimitiveCollection(); var groundPrimitives = new PrimitiveCollection(); if (dataSourceCollection.length > 0) { scene.primitives.add(primitives); scene.groundPrimitives.add(groundPrimitives); primitivesAdded = true; } this._primitives = primitives; this._groundPrimitives = groundPrimitives; for (var i = 0, len = dataSourceCollection.length; i < len; i++) { this._onDataSourceAdded(dataSourceCollection, dataSourceCollection.get(i)); } var defaultDataSource = new CustomDataSource(); this._onDataSourceAdded(undefined, defaultDataSource); this._defaultDataSource = defaultDataSource; var removeDefaultDataSourceListener; var removeDataSourceCollectionListener; if (!primitivesAdded) { var that = this; var addPrimitives = function () { scene.primitives.add(primitives); scene.groundPrimitives.add(groundPrimitives); removeDefaultDataSourceListener(); removeDataSourceCollectionListener(); that._removeDefaultDataSourceListener = undefined; that._removeDataSourceCollectionListener = undefined; }; removeDefaultDataSourceListener = defaultDataSource.entities.collectionChanged.addEventListener( addPrimitives ); removeDataSourceCollectionListener = dataSourceCollection.dataSourceAdded.addEventListener( addPrimitives ); } this._removeDefaultDataSourceListener = removeDefaultDataSourceListener; this._removeDataSourceCollectionListener = removeDataSourceCollectionListener; this._ready = false; } /** * Gets or sets the default function which creates an array of visualizers used for visualization. * By default, this function uses all standard visualizers. * * @type {DataSourceDisplay.VisualizersCallback} */ DataSourceDisplay.defaultVisualizersCallback = function ( scene, entityCluster, dataSource ) { var entities = dataSource.entities; return [ new BillboardVisualizer(entityCluster, entities), new GeometryVisualizer( scene, entities, dataSource._primitives, dataSource._groundPrimitives ), new LabelVisualizer(entityCluster, entities), new ModelVisualizer(scene, entities), new Cesium3DTilesetVisualizer(scene, entities), new PointVisualizer(entityCluster, entities), new PathVisualizer(scene, entities), new PolylineVisualizer( scene, entities, dataSource._primitives, dataSource._groundPrimitives ), ]; }; Object.defineProperties(DataSourceDisplay.prototype, { /** * Gets the scene associated with this display. * @memberof DataSourceDisplay.prototype * @type {Scene} */ scene: { get: function () { return this._scene; }, }, /** * Gets the collection of data sources to display. * @memberof DataSourceDisplay.prototype * @type {DataSourceCollection} */ dataSources: { get: function () { return this._dataSourceCollection; }, }, /** * Gets the default data source instance which can be used to * manually create and visualize entities not tied to * a specific data source. This instance is always available * and does not appear in the list dataSources collection. * @memberof DataSourceDisplay.prototype * @type {CustomDataSource} */ defaultDataSource: { get: function () { return this._defaultDataSource; }, }, /** * Gets a value indicating whether or not all entities in the data source are ready * @memberof DataSourceDisplay.prototype * @type {Boolean} * @readonly */ ready: { get: function () { return this._ready; }, }, }); /** * Returns true if this object was destroyed; otherwise, false. *

* If this object was destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. * * @returns {Boolean} True if this object was destroyed; otherwise, false. * * @see DataSourceDisplay#destroy */ DataSourceDisplay.prototype.isDestroyed = function () { return false; }; /** * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic * release of WebGL resources, instead of relying on the garbage collector to destroy this object. *

* Once an object is destroyed, it should not be used; calling any function other than * isDestroyed will result in a {@link DeveloperError} exception. Therefore, * assign the return value (undefined) to the object as done in the example. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. * * * @example * dataSourceDisplay = dataSourceDisplay.destroy(); * * @see DataSourceDisplay#isDestroyed */ DataSourceDisplay.prototype.destroy = function () { this._eventHelper.removeAll(); var dataSourceCollection = this._dataSourceCollection; for (var i = 0, length = dataSourceCollection.length; i < length; ++i) { this._onDataSourceRemoved( this._dataSourceCollection, dataSourceCollection.get(i) ); } this._onDataSourceRemoved(undefined, this._defaultDataSource); if (defined(this._removeDefaultDataSourceListener)) { this._removeDefaultDataSourceListener(); this._removeDataSourceCollectionListener(); } else { this._scene.primitives.remove(this._primitives); this._scene.groundPrimitives.remove(this._groundPrimitives); } return destroyObject(this); }; /** * Updates the display to the provided time. * * @param {JulianDate} time The simulation time. * @returns {Boolean} True if all data sources are ready to be displayed, false otherwise. */ DataSourceDisplay.prototype.update = function (time) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); if (!ApproximateTerrainHeights.initialized) { this._ready = false; return false; } var result = true; var i; var x; var visualizers; var vLength; var dataSources = this._dataSourceCollection; var length = dataSources.length; for (i = 0; i < length; i++) { var dataSource = dataSources.get(i); if (defined(dataSource.update)) { result = dataSource.update(time) && result; } visualizers = dataSource._visualizers; vLength = visualizers.length; for (x = 0; x < vLength; x++) { result = visualizers[x].update(time) && result; } } visualizers = this._defaultDataSource._visualizers; vLength = visualizers.length; for (x = 0; x < vLength; x++) { result = visualizers[x].update(time) && result; } this._ready = result; return result; }; DataSourceDisplay.prototype._postRender = function () { // Adds credits for all datasources var frameState = this._scene.frameState; var dataSources = this._dataSourceCollection; var length = dataSources.length; for (var i = 0; i < length; i++) { var dataSource = dataSources.get(i); var credit = dataSource.credit; if (defined(credit)) { frameState.creditDisplay.addCredit(credit); } // Credits from the resource that the user can't remove var credits = dataSource._resourceCredits; if (defined(credits)) { var creditCount = credits.length; for (var c = 0; c < creditCount; c++) { frameState.creditDisplay.addCredit(credits[c]); } } } }; var getBoundingSphereArrayScratch = []; var getBoundingSphereBoundingSphereScratch = new BoundingSphere(); /** * Computes a bounding sphere which encloses the visualization produced for the specified entity. * The bounding sphere is in the fixed frame of the scene's globe. * * @param {Entity} entity The entity whose bounding sphere to compute. * @param {Boolean} allowPartial If true, pending bounding spheres are ignored and an answer will be returned from the currently available data. * If false, the the function will halt and return pending if any of the bounding spheres are pending. * @param {BoundingSphere} result The bounding sphere onto which to store the result. * @returns {BoundingSphereState} BoundingSphereState.DONE if the result contains the bounding sphere, * BoundingSphereState.PENDING if the result is still being computed, or * BoundingSphereState.FAILED if the entity has no visualization in the current scene. * @private */ DataSourceDisplay.prototype.getBoundingSphere = function ( entity, allowPartial, result ) { //>>includeStart('debug', pragmas.debug); Check.defined("entity", entity); Check.typeOf.bool("allowPartial", allowPartial); Check.defined("result", result); //>>includeEnd('debug'); if (!this._ready) { return BoundingSphereState$1.PENDING; } var i; var length; var dataSource = this._defaultDataSource; if (!dataSource.entities.contains(entity)) { dataSource = undefined; var dataSources = this._dataSourceCollection; length = dataSources.length; for (i = 0; i < length; i++) { var d = dataSources.get(i); if (d.entities.contains(entity)) { dataSource = d; break; } } } if (!defined(dataSource)) { return BoundingSphereState$1.FAILED; } var boundingSpheres = getBoundingSphereArrayScratch; var tmp = getBoundingSphereBoundingSphereScratch; var count = 0; var state = BoundingSphereState$1.DONE; var visualizers = dataSource._visualizers; var visualizersLength = visualizers.length; for (i = 0; i < visualizersLength; i++) { var visualizer = visualizers[i]; if (defined(visualizer.getBoundingSphere)) { state = visualizers[i].getBoundingSphere(entity, tmp); if (!allowPartial && state === BoundingSphereState$1.PENDING) { return BoundingSphereState$1.PENDING; } else if (state === BoundingSphereState$1.DONE) { boundingSpheres[count] = BoundingSphere.clone( tmp, boundingSpheres[count] ); count++; } } } if (count === 0) { return BoundingSphereState$1.FAILED; } boundingSpheres.length = count; BoundingSphere.fromBoundingSpheres(boundingSpheres, result); return BoundingSphereState$1.DONE; }; DataSourceDisplay.prototype._onDataSourceAdded = function ( dataSourceCollection, dataSource ) { var scene = this._scene; var displayPrimitives = this._primitives; var displayGroundPrimitives = this._groundPrimitives; var primitives = displayPrimitives.add(new PrimitiveCollection()); var groundPrimitives = displayGroundPrimitives.add( new OrderedGroundPrimitiveCollection() ); dataSource._primitives = primitives; dataSource._groundPrimitives = groundPrimitives; var entityCluster = dataSource.clustering; entityCluster._initialize(scene); primitives.add(entityCluster); dataSource._visualizers = this._visualizersCallback( scene, entityCluster, dataSource ); }; DataSourceDisplay.prototype._onDataSourceRemoved = function ( dataSourceCollection, dataSource ) { var displayPrimitives = this._primitives; var displayGroundPrimitives = this._groundPrimitives; var primitives = dataSource._primitives; var groundPrimitives = dataSource._groundPrimitives; var entityCluster = dataSource.clustering; primitives.remove(entityCluster); var visualizers = dataSource._visualizers; var length = visualizers.length; for (var i = 0; i < length; i++) { visualizers[i].destroy(); } displayPrimitives.remove(primitives); displayGroundPrimitives.remove(groundPrimitives); dataSource._visualizers = undefined; }; DataSourceDisplay.prototype._onDataSourceMoved = function ( dataSource, newIndex, oldIndex ) { var displayPrimitives = this._primitives; var displayGroundPrimitives = this._groundPrimitives; var primitives = dataSource._primitives; var groundPrimitives = dataSource._groundPrimitives; if (newIndex === oldIndex + 1) { displayPrimitives.raise(primitives); displayGroundPrimitives.raise(groundPrimitives); } else if (newIndex === oldIndex - 1) { displayPrimitives.lower(primitives); displayGroundPrimitives.lower(groundPrimitives); } else if (newIndex === 0) { displayPrimitives.lowerToBottom(primitives); displayGroundPrimitives.lowerToBottom(groundPrimitives); displayPrimitives.raise(primitives); // keep defaultDataSource primitives at index 0 since it's not in the collection displayGroundPrimitives.raise(groundPrimitives); } else { displayPrimitives.raiseToTop(primitives); displayGroundPrimitives.raiseToTop(groundPrimitives); } }; var updateTransformMatrix3Scratch1 = new Matrix3(); var updateTransformMatrix3Scratch2 = new Matrix3(); var updateTransformMatrix3Scratch3 = new Matrix3(); var updateTransformMatrix4Scratch = new Matrix4(); var updateTransformCartesian3Scratch1 = new Cartesian3(); var updateTransformCartesian3Scratch2 = new Cartesian3(); var updateTransformCartesian3Scratch3 = new Cartesian3(); var updateTransformCartesian3Scratch4 = new Cartesian3(); var updateTransformCartesian3Scratch5 = new Cartesian3(); var updateTransformCartesian3Scratch6 = new Cartesian3(); var deltaTime = new JulianDate(); var northUpAxisFactor = 1.25; // times ellipsoid's maximum radius function updateTransform( that, camera, updateLookAt, saveCamera, positionProperty, time, ellipsoid ) { var mode = that.scene.mode; var cartesian = positionProperty.getValue(time, that._lastCartesian); if (defined(cartesian)) { var hasBasis = false; var invertVelocity = false; var xBasis; var yBasis; var zBasis; if (mode === SceneMode$1.SCENE3D) { // The time delta was determined based on how fast satellites move compared to vehicles near the surface. // Slower moving vehicles will most likely default to east-north-up, while faster ones will be VVLH. JulianDate.addSeconds(time, 0.001, deltaTime); var deltaCartesian = positionProperty.getValue( deltaTime, updateTransformCartesian3Scratch1 ); // If no valid position at (time + 0.001), sample at (time - 0.001) and invert the vector if (!defined(deltaCartesian)) { JulianDate.addSeconds(time, -0.001, deltaTime); deltaCartesian = positionProperty.getValue( deltaTime, updateTransformCartesian3Scratch1 ); invertVelocity = true; } if (defined(deltaCartesian)) { var toInertial = Transforms.computeFixedToIcrfMatrix( time, updateTransformMatrix3Scratch1 ); var toInertialDelta = Transforms.computeFixedToIcrfMatrix( deltaTime, updateTransformMatrix3Scratch2 ); var toFixed; if (!defined(toInertial) || !defined(toInertialDelta)) { toFixed = Transforms.computeTemeToPseudoFixedMatrix( time, updateTransformMatrix3Scratch3 ); toInertial = Matrix3.transpose( toFixed, updateTransformMatrix3Scratch1 ); toInertialDelta = Transforms.computeTemeToPseudoFixedMatrix( deltaTime, updateTransformMatrix3Scratch2 ); Matrix3.transpose(toInertialDelta, toInertialDelta); } else { toFixed = Matrix3.transpose( toInertial, updateTransformMatrix3Scratch3 ); } var inertialCartesian = Matrix3.multiplyByVector( toInertial, cartesian, updateTransformCartesian3Scratch5 ); var inertialDeltaCartesian = Matrix3.multiplyByVector( toInertialDelta, deltaCartesian, updateTransformCartesian3Scratch6 ); Cartesian3.subtract( inertialCartesian, inertialDeltaCartesian, updateTransformCartesian3Scratch4 ); var inertialVelocity = Cartesian3.magnitude(updateTransformCartesian3Scratch4) * 1000.0; // meters/sec var mu = CesiumMath.GRAVITATIONALPARAMETER; // m^3 / sec^2 var semiMajorAxis = -mu / (inertialVelocity * inertialVelocity - (2 * mu) / Cartesian3.magnitude(inertialCartesian)); if ( semiMajorAxis < 0 || semiMajorAxis > northUpAxisFactor * ellipsoid.maximumRadius ) { // North-up viewing from deep space. // X along the nadir xBasis = updateTransformCartesian3Scratch2; Cartesian3.normalize(cartesian, xBasis); Cartesian3.negate(xBasis, xBasis); // Z is North zBasis = Cartesian3.clone( Cartesian3.UNIT_Z, updateTransformCartesian3Scratch3 ); // Y is along the cross of z and x (right handed basis / in the direction of motion) yBasis = Cartesian3.cross( zBasis, xBasis, updateTransformCartesian3Scratch1 ); if (Cartesian3.magnitude(yBasis) > CesiumMath.EPSILON7) { Cartesian3.normalize(xBasis, xBasis); Cartesian3.normalize(yBasis, yBasis); zBasis = Cartesian3.cross( xBasis, yBasis, updateTransformCartesian3Scratch3 ); Cartesian3.normalize(zBasis, zBasis); hasBasis = true; } } else if ( !Cartesian3.equalsEpsilon( cartesian, deltaCartesian, CesiumMath.EPSILON7 ) ) { // Approximation of VVLH (Vehicle Velocity Local Horizontal) with the Z-axis flipped. // Z along the position zBasis = updateTransformCartesian3Scratch2; Cartesian3.normalize(inertialCartesian, zBasis); Cartesian3.normalize(inertialDeltaCartesian, inertialDeltaCartesian); // Y is along the angular momentum vector (e.g. "orbit normal") yBasis = Cartesian3.cross( zBasis, inertialDeltaCartesian, updateTransformCartesian3Scratch3 ); if (invertVelocity) { yBasis = Cartesian3.multiplyByScalar(yBasis, -1, yBasis); } if ( !Cartesian3.equalsEpsilon( yBasis, Cartesian3.ZERO, CesiumMath.EPSILON7 ) ) { // X is along the cross of y and z (right handed basis / in the direction of motion) xBasis = Cartesian3.cross( yBasis, zBasis, updateTransformCartesian3Scratch1 ); Matrix3.multiplyByVector(toFixed, xBasis, xBasis); Matrix3.multiplyByVector(toFixed, yBasis, yBasis); Matrix3.multiplyByVector(toFixed, zBasis, zBasis); Cartesian3.normalize(xBasis, xBasis); Cartesian3.normalize(yBasis, yBasis); Cartesian3.normalize(zBasis, zBasis); hasBasis = true; } } } } if (defined(that.boundingSphere)) { cartesian = that.boundingSphere.center; } var position; var direction; var up; if (saveCamera) { position = Cartesian3.clone( camera.position, updateTransformCartesian3Scratch4 ); direction = Cartesian3.clone( camera.direction, updateTransformCartesian3Scratch5 ); up = Cartesian3.clone(camera.up, updateTransformCartesian3Scratch6); } var transform = updateTransformMatrix4Scratch; if (hasBasis) { transform[0] = xBasis.x; transform[1] = xBasis.y; transform[2] = xBasis.z; transform[3] = 0.0; transform[4] = yBasis.x; transform[5] = yBasis.y; transform[6] = yBasis.z; transform[7] = 0.0; transform[8] = zBasis.x; transform[9] = zBasis.y; transform[10] = zBasis.z; transform[11] = 0.0; transform[12] = cartesian.x; transform[13] = cartesian.y; transform[14] = cartesian.z; transform[15] = 0.0; } else { // Stationary or slow-moving, low-altitude objects use East-North-Up. Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform); } camera._setTransform(transform); if (saveCamera) { Cartesian3.clone(position, camera.position); Cartesian3.clone(direction, camera.direction); Cartesian3.clone(up, camera.up); Cartesian3.cross(direction, up, camera.right); } } if (updateLookAt) { var offset = mode === SceneMode$1.SCENE2D || Cartesian3.equals(that._offset3D, Cartesian3.ZERO) ? undefined : that._offset3D; camera.lookAtTransform(camera.transform, offset); } } /** * A utility object for tracking an entity with the camera. * @alias EntityView * @constructor * * @param {Entity} entity The entity to track with the camera. * @param {Scene} scene The scene to use. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use for orienting the camera. */ function EntityView(entity, scene, ellipsoid) { //>>includeStart('debug', pragmas.debug); Check.defined("entity", entity); Check.defined("scene", scene); //>>includeEnd('debug'); /** * The entity to track with the camera. * @type {Entity} */ this.entity = entity; /** * The scene in which to track the object. * @type {Scene} */ this.scene = scene; /** * The ellipsoid to use for orienting the camera. * @type {Ellipsoid} */ this.ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); /** * The bounding sphere of the object. * @type {BoundingSphere} */ this.boundingSphere = undefined; // Shadow copies of the objects so we can detect changes. this._lastEntity = undefined; this._mode = undefined; this._lastCartesian = new Cartesian3(); this._defaultOffset3D = undefined; this._offset3D = new Cartesian3(); } // STATIC properties defined here, not per-instance. Object.defineProperties(EntityView, { /** * Gets or sets a camera offset that will be used to * initialize subsequent EntityViews. * @memberof EntityView * @type {Cartesian3} */ defaultOffset3D: { get: function () { return this._defaultOffset3D; }, set: function (vector) { this._defaultOffset3D = Cartesian3.clone(vector, new Cartesian3()); }, }, }); // Initialize the static property. EntityView.defaultOffset3D = new Cartesian3(-14000, 3500, 3500); var scratchHeadingPitchRange = new HeadingPitchRange(); var scratchCartesian$2 = new Cartesian3(); /** * Should be called each animation frame to update the camera * to the latest settings. * @param {JulianDate} time The current animation time. * @param {BoundingSphere} [boundingSphere] bounding sphere of the object. */ EntityView.prototype.update = function (time, boundingSphere) { //>>includeStart('debug', pragmas.debug); Check.defined("time", time); //>>includeEnd('debug'); var scene = this.scene; var ellipsoid = this.ellipsoid; var sceneMode = scene.mode; if (sceneMode === SceneMode$1.MORPHING) { return; } var entity = this.entity; var positionProperty = entity.position; if (!defined(positionProperty)) { return; } var objectChanged = entity !== this._lastEntity; var sceneModeChanged = sceneMode !== this._mode; var camera = scene.camera; var updateLookAt = objectChanged || sceneModeChanged; var saveCamera = true; if (objectChanged) { var viewFromProperty = entity.viewFrom; var hasViewFrom = defined(viewFromProperty); if (!hasViewFrom && defined(boundingSphere)) { // The default HPR is not ideal for high altitude objects so // we scale the pitch as we get further from the earth for a more // downward view. scratchHeadingPitchRange.pitch = -CesiumMath.PI_OVER_FOUR; scratchHeadingPitchRange.range = 0; var position = positionProperty.getValue(time, scratchCartesian$2); if (defined(position)) { var factor = 2 - 1 / Math.max( 1, Cartesian3.magnitude(position) / ellipsoid.maximumRadius ); scratchHeadingPitchRange.pitch *= factor; } camera.viewBoundingSphere(boundingSphere, scratchHeadingPitchRange); this.boundingSphere = boundingSphere; updateLookAt = false; saveCamera = false; } else if ( !hasViewFrom || !defined(viewFromProperty.getValue(time, this._offset3D)) ) { Cartesian3.clone(EntityView._defaultOffset3D, this._offset3D); } } else if (!sceneModeChanged && this._mode !== SceneMode$1.SCENE2D) { Cartesian3.clone(camera.position, this._offset3D); } this._lastEntity = entity; this._mode = sceneMode; updateTransform( this, camera, updateLookAt, saveCamera, positionProperty, time, ellipsoid ); }; /** @license topojson - https://github.com/topojson/topojson Copyright (c) 2012-2016, Michael Bostock All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * The name Michael Bostock 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 MICHAEL BOSTOCK 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. **/ var tmp$4 = {}; // https://github.com/topojson/topojson Version 3.0.2. Copyright 2017 Mike Bostock. (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : (factory((global.topojson = global.topojson || {}))); }(tmp$4, (function (exports) { var identity = function(x) { return x; }; var transform = function(transform) { if (transform == null) return identity; var x0, y0, kx = transform.scale[0], ky = transform.scale[1], dx = transform.translate[0], dy = transform.translate[1]; return function(input, i) { if (!i) x0 = y0 = 0; var j = 2, n = input.length, output = new Array(n); output[0] = (x0 += input[0]) * kx + dx; output[1] = (y0 += input[1]) * ky + dy; while (j < n) output[j] = input[j], ++j; return output; }; }; var bbox = function(topology) { var t = transform(topology.transform), key, x0 = Infinity, y0 = x0, x1 = -x0, y1 = -x0; function bboxPoint(p) { p = t(p); if (p[0] < x0) x0 = p[0]; if (p[0] > x1) x1 = p[0]; if (p[1] < y0) y0 = p[1]; if (p[1] > y1) y1 = p[1]; } function bboxGeometry(o) { switch (o.type) { case "GeometryCollection": o.geometries.forEach(bboxGeometry); break; case "Point": bboxPoint(o.coordinates); break; case "MultiPoint": o.coordinates.forEach(bboxPoint); break; } } topology.arcs.forEach(function(arc) { var i = -1, n = arc.length, p; while (++i < n) { p = t(arc[i], i); if (p[0] < x0) x0 = p[0]; if (p[0] > x1) x1 = p[0]; if (p[1] < y0) y0 = p[1]; if (p[1] > y1) y1 = p[1]; } }); for (key in topology.objects) { bboxGeometry(topology.objects[key]); } return [x0, y0, x1, y1]; }; var reverse = function(array, n) { var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t; }; var feature = function(topology, o) { return o.type === "GeometryCollection" ? {type: "FeatureCollection", features: o.geometries.map(function(o) { return feature$1(topology, o); })} : feature$1(topology, o); }; function feature$1(topology, o) { var id = o.id, bbox = o.bbox, properties = o.properties == null ? {} : o.properties, geometry = object(topology, o); return id == null && bbox == null ? {type: "Feature", properties: properties, geometry: geometry} : bbox == null ? {type: "Feature", id: id, properties: properties, geometry: geometry} : {type: "Feature", id: id, bbox: bbox, properties: properties, geometry: geometry}; } function object(topology, o) { var transformPoint = transform(topology.transform), arcs = topology.arcs; function arc(i, points) { if (points.length) points.pop(); for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length; k < n; ++k) { points.push(transformPoint(a[k], k)); } if (i < 0) reverse(points, n); } function point(p) { return transformPoint(p); } function line(arcs) { var points = []; for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points); if (points.length < 2) points.push(points[0]); // This should never happen per the specification. return points; } function ring(arcs) { var points = line(arcs); while (points.length < 4) points.push(points[0]); // This may happen if an arc has only two points. return points; } function polygon(arcs) { return arcs.map(ring); } function geometry(o) { var type = o.type, coordinates; switch (type) { case "GeometryCollection": return {type: type, geometries: o.geometries.map(geometry)}; case "Point": coordinates = point(o.coordinates); break; case "MultiPoint": coordinates = o.coordinates.map(point); break; case "LineString": coordinates = line(o.arcs); break; case "MultiLineString": coordinates = o.arcs.map(line); break; case "Polygon": coordinates = polygon(o.arcs); break; case "MultiPolygon": coordinates = o.arcs.map(polygon); break; default: return null; } return {type: type, coordinates: coordinates}; } return geometry(o); } var stitch = function(topology, arcs) { var stitchedArcs = {}, fragmentByStart = {}, fragmentByEnd = {}, fragments = [], emptyIndex = -1; // Stitch empty arcs first, since they may be subsumed by other arcs. arcs.forEach(function(i, j) { var arc = topology.arcs[i < 0 ? ~i : i], t; if (arc.length < 3 && !arc[1][0] && !arc[1][1]) { t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t; } }); arcs.forEach(function(i) { var e = ends(i), start = e[0], end = e[1], f, g; if (f = fragmentByEnd[start]) { delete fragmentByEnd[f.end]; f.push(i); f.end = end; if (g = fragmentByStart[end]) { delete fragmentByStart[g.start]; var fg = g === f ? f : f.concat(g); fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg; } else { fragmentByStart[f.start] = fragmentByEnd[f.end] = f; } } else if (f = fragmentByStart[end]) { delete fragmentByStart[f.start]; f.unshift(i); f.start = start; if (g = fragmentByEnd[start]) { delete fragmentByEnd[g.end]; var gf = g === f ? f : g.concat(f); fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf; } else { fragmentByStart[f.start] = fragmentByEnd[f.end] = f; } } else { f = [i]; fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f; } }); function ends(i) { var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1; if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; }); else p1 = arc[arc.length - 1]; return i < 0 ? [p1, p0] : [p0, p1]; } function flush(fragmentByEnd, fragmentByStart) { for (var k in fragmentByEnd) { var f = fragmentByEnd[k]; delete fragmentByStart[f.start]; delete f.start; delete f.end; f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; }); fragments.push(f); } } flush(fragmentByEnd, fragmentByStart); flush(fragmentByStart, fragmentByEnd); arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); }); return fragments; }; var mesh = function(topology) { return object(topology, meshArcs.apply(this, arguments)); }; function meshArcs(topology, object$$1, filter) { var arcs, i, n; if (arguments.length > 1) arcs = extractArcs(topology, object$$1, filter); else for (i = 0, arcs = new Array(n = topology.arcs.length); i < n; ++i) arcs[i] = i; return {type: "MultiLineString", arcs: stitch(topology, arcs)}; } function extractArcs(topology, object$$1, filter) { var arcs = [], geomsByArc = [], geom; function extract0(i) { var j = i < 0 ? ~i : i; (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom}); } function extract1(arcs) { arcs.forEach(extract0); } function extract2(arcs) { arcs.forEach(extract1); } function extract3(arcs) { arcs.forEach(extract2); } function geometry(o) { switch (geom = o, o.type) { case "GeometryCollection": o.geometries.forEach(geometry); break; case "LineString": extract1(o.arcs); break; case "MultiLineString": case "Polygon": extract2(o.arcs); break; case "MultiPolygon": extract3(o.arcs); break; } } geometry(object$$1); geomsByArc.forEach(filter == null ? function(geoms) { arcs.push(geoms[0].i); } : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); }); return arcs; } function planarRingArea(ring) { var i = -1, n = ring.length, a, b = ring[n - 1], area = 0; while (++i < n) a = b, b = ring[i], area += a[0] * b[1] - a[1] * b[0]; return Math.abs(area); // Note: doubled area! } var merge = function(topology) { return object(topology, mergeArcs.apply(this, arguments)); }; function mergeArcs(topology, objects) { var polygonsByArc = {}, polygons = [], groups = []; objects.forEach(geometry); function geometry(o) { switch (o.type) { case "GeometryCollection": o.geometries.forEach(geometry); break; case "Polygon": extract(o.arcs); break; case "MultiPolygon": o.arcs.forEach(extract); break; } } function extract(polygon) { polygon.forEach(function(ring) { ring.forEach(function(arc) { (polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon); }); }); polygons.push(polygon); } function area(ring) { return planarRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]); } polygons.forEach(function(polygon) { if (!polygon._) { var group = [], neighbors = [polygon]; polygon._ = 1; groups.push(group); while (polygon = neighbors.pop()) { group.push(polygon); polygon.forEach(function(ring) { ring.forEach(function(arc) { polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) { if (!polygon._) { polygon._ = 1; neighbors.push(polygon); } }); }); }); } } }); polygons.forEach(function(polygon) { delete polygon._; }); return { type: "MultiPolygon", arcs: groups.map(function(polygons) { var arcs = [], n; // Extract the exterior (unique) arcs. polygons.forEach(function(polygon) { polygon.forEach(function(ring) { ring.forEach(function(arc) { if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) { arcs.push(arc); } }); }); }); // Stitch the arcs into one or more rings. arcs = stitch(topology, arcs); // If more than one ring is returned, // at most one of these rings can be the exterior; // choose the one with the greatest absolute area. if ((n = arcs.length) > 1) { for (var i = 1, k = area(arcs[0]), ki, t; i < n; ++i) { if ((ki = area(arcs[i])) > k) { t = arcs[0], arcs[0] = arcs[i], arcs[i] = t, k = ki; } } } return arcs; }) }; } var bisect = function(a, x) { var lo = 0, hi = a.length; while (lo < hi) { var mid = lo + hi >>> 1; if (a[mid] < x) lo = mid + 1; else hi = mid; } return lo; }; var neighbors = function(objects) { var indexesByArc = {}, // arc index -> array of object indexes neighbors = objects.map(function() { return []; }); function line(arcs, i) { arcs.forEach(function(a) { if (a < 0) a = ~a; var o = indexesByArc[a]; if (o) o.push(i); else indexesByArc[a] = [i]; }); } function polygon(arcs, i) { arcs.forEach(function(arc) { line(arc, i); }); } function geometry(o, i) { if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); }); else if (o.type in geometryType) geometryType[o.type](o.arcs, i); } var geometryType = { LineString: line, MultiLineString: polygon, Polygon: polygon, MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); } }; objects.forEach(geometry); for (var i in indexesByArc) { for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) { for (var k = j + 1; k < m; ++k) { var ij = indexes[j], ik = indexes[k], n; if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik); if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij); } } } return neighbors; }; var untransform = function(transform) { if (transform == null) return identity; var x0, y0, kx = transform.scale[0], ky = transform.scale[1], dx = transform.translate[0], dy = transform.translate[1]; return function(input, i) { if (!i) x0 = y0 = 0; var j = 2, n = input.length, output = new Array(n), x1 = Math.round((input[0] - dx) / kx), y1 = Math.round((input[1] - dy) / ky); output[0] = x1 - x0, x0 = x1; output[1] = y1 - y0, y0 = y1; while (j < n) output[j] = input[j], ++j; return output; }; }; var quantize = function(topology, transform) { if (topology.transform) throw new Error("already quantized"); if (!transform || !transform.scale) { if (!((n = Math.floor(transform)) >= 2)) throw new Error("n must be \u22652"); box = topology.bbox || bbox(topology); var x0 = box[0], y0 = box[1], x1 = box[2], y1 = box[3], n; transform = {scale: [x1 - x0 ? (x1 - x0) / (n - 1) : 1, y1 - y0 ? (y1 - y0) / (n - 1) : 1], translate: [x0, y0]}; } else { box = topology.bbox; } var t = untransform(transform), box, key, inputs = topology.objects, outputs = {}; function quantizePoint(point) { return t(point); } function quantizeGeometry(input) { var output; switch (input.type) { case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(quantizeGeometry)}; break; case "Point": output = {type: "Point", coordinates: quantizePoint(input.coordinates)}; break; case "MultiPoint": output = {type: "MultiPoint", coordinates: input.coordinates.map(quantizePoint)}; break; default: return input; } if (input.id != null) output.id = input.id; if (input.bbox != null) output.bbox = input.bbox; if (input.properties != null) output.properties = input.properties; return output; } function quantizeArc(input) { var i = 0, j = 1, n = input.length, p, output = new Array(n); // pessimistic output[0] = t(input[0], 0); while (++i < n) if ((p = t(input[i], i))[0] || p[1]) output[j++] = p; // non-coincident points if (j === 1) output[j++] = [0, 0]; // an arc must have at least two points output.length = j; return output; } for (key in inputs) outputs[key] = quantizeGeometry(inputs[key]); return { type: "Topology", bbox: box, transform: transform, objects: outputs, arcs: topology.arcs.map(quantizeArc) }; }; // Computes the bounding box of the specified hash of GeoJSON objects. var bounds = function(objects) { var x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity; function boundGeometry(geometry) { if (geometry != null && boundGeometryType.hasOwnProperty(geometry.type)) boundGeometryType[geometry.type](geometry); } var boundGeometryType = { GeometryCollection: function(o) { o.geometries.forEach(boundGeometry); }, Point: function(o) { boundPoint(o.coordinates); }, MultiPoint: function(o) { o.coordinates.forEach(boundPoint); }, LineString: function(o) { boundLine(o.arcs); }, MultiLineString: function(o) { o.arcs.forEach(boundLine); }, Polygon: function(o) { o.arcs.forEach(boundLine); }, MultiPolygon: function(o) { o.arcs.forEach(boundMultiLine); } }; function boundPoint(coordinates) { var x = coordinates[0], y = coordinates[1]; if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y; } function boundLine(coordinates) { coordinates.forEach(boundPoint); } function boundMultiLine(coordinates) { coordinates.forEach(boundLine); } for (var key in objects) { boundGeometry(objects[key]); } return x1 >= x0 && y1 >= y0 ? [x0, y0, x1, y1] : undefined; }; var hashset = function(size, hash, equal, type, empty) { if (arguments.length === 3) { type = Array; empty = null; } var store = new type(size = 1 << Math.max(4, Math.ceil(Math.log(size) / Math.LN2))), mask = size - 1; for (var i = 0; i < size; ++i) { store[i] = empty; } function add(value) { var index = hash(value) & mask, match = store[index], collisions = 0; while (match != empty) { if (equal(match, value)) return true; if (++collisions >= size) throw new Error("full hashset"); match = store[index = (index + 1) & mask]; } store[index] = value; return true; } function has(value) { var index = hash(value) & mask, match = store[index], collisions = 0; while (match != empty) { if (equal(match, value)) return true; if (++collisions >= size) break; match = store[index = (index + 1) & mask]; } return false; } function values() { var values = []; for (var i = 0, n = store.length; i < n; ++i) { var match = store[i]; if (match != empty) values.push(match); } return values; } return { add: add, has: has, values: values }; }; var hashmap = function(size, hash, equal, keyType, keyEmpty, valueType) { if (arguments.length === 3) { keyType = valueType = Array; keyEmpty = null; } var keystore = new keyType(size = 1 << Math.max(4, Math.ceil(Math.log(size) / Math.LN2))), valstore = new valueType(size), mask = size - 1; for (var i = 0; i < size; ++i) { keystore[i] = keyEmpty; } function set(key, value) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index] = value; if (++collisions >= size) throw new Error("full hashmap"); matchKey = keystore[index = (index + 1) & mask]; } keystore[index] = key; valstore[index] = value; return value; } function maybeSet(key, value) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index]; if (++collisions >= size) throw new Error("full hashmap"); matchKey = keystore[index = (index + 1) & mask]; } keystore[index] = key; valstore[index] = value; return value; } function get(key, missingValue) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index]; if (++collisions >= size) break; matchKey = keystore[index = (index + 1) & mask]; } return missingValue; } function keys() { var keys = []; for (var i = 0, n = keystore.length; i < n; ++i) { var matchKey = keystore[i]; if (matchKey != keyEmpty) keys.push(matchKey); } return keys; } return { set: set, maybeSet: maybeSet, // set if unset get: get, keys: keys }; }; var equalPoint = function(pointA, pointB) { return pointA[0] === pointB[0] && pointA[1] === pointB[1]; }; // TODO if quantized, use simpler Int32 hashing? var buffer = new ArrayBuffer(16); var uints = new Uint32Array(buffer); var hashPoint = function(point) { var hash = uints[0] ^ uints[1]; hash = hash << 5 ^ hash >> 7 ^ uints[2] ^ uints[3]; return hash & 0x7fffffff; }; // Given an extracted (pre-)topology, identifies all of the junctions. These are // the points at which arcs (lines or rings) will need to be cut so that each // arc is represented uniquely. // // A junction is a point where at least one arc deviates from another arc going // through the same point. For example, consider the point B. If there is a arc // through ABC and another arc through CBA, then B is not a junction because in // both cases the adjacent point pairs are {A,C}. However, if there is an // additional arc ABD, then {A,D} != {A,C}, and thus B becomes a junction. // // For a closed ring ABCA, the first point A’s adjacent points are the second // and last point {B,C}. For a line, the first and last point are always // considered junctions, even if the line is closed; this ensures that a closed // line is never rotated. var join = function(topology) { var coordinates = topology.coordinates, lines = topology.lines, rings = topology.rings, indexes = index(), visitedByIndex = new Int32Array(coordinates.length), leftByIndex = new Int32Array(coordinates.length), rightByIndex = new Int32Array(coordinates.length), junctionByIndex = new Int8Array(coordinates.length), junctionCount = 0, // upper bound on number of junctions i, n, previousIndex, currentIndex, nextIndex; for (i = 0, n = coordinates.length; i < n; ++i) { visitedByIndex[i] = leftByIndex[i] = rightByIndex[i] = -1; } for (i = 0, n = lines.length; i < n; ++i) { var line = lines[i], lineStart = line[0], lineEnd = line[1]; currentIndex = indexes[lineStart]; nextIndex = indexes[++lineStart]; ++junctionCount, junctionByIndex[currentIndex] = 1; // start while (++lineStart <= lineEnd) { sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[lineStart]); } ++junctionCount, junctionByIndex[nextIndex] = 1; // end } for (i = 0, n = coordinates.length; i < n; ++i) { visitedByIndex[i] = -1; } for (i = 0, n = rings.length; i < n; ++i) { var ring = rings[i], ringStart = ring[0] + 1, ringEnd = ring[1]; previousIndex = indexes[ringEnd - 1]; currentIndex = indexes[ringStart - 1]; nextIndex = indexes[ringStart]; sequence(i, previousIndex, currentIndex, nextIndex); while (++ringStart <= ringEnd) { sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[ringStart]); } } function sequence(i, previousIndex, currentIndex, nextIndex) { if (visitedByIndex[currentIndex] === i) return; // ignore self-intersection visitedByIndex[currentIndex] = i; var leftIndex = leftByIndex[currentIndex]; if (leftIndex >= 0) { var rightIndex = rightByIndex[currentIndex]; if ((leftIndex !== previousIndex || rightIndex !== nextIndex) && (leftIndex !== nextIndex || rightIndex !== previousIndex)) { ++junctionCount, junctionByIndex[currentIndex] = 1; } } else { leftByIndex[currentIndex] = previousIndex; rightByIndex[currentIndex] = nextIndex; } } function index() { var indexByPoint = hashmap(coordinates.length * 1.4, hashIndex, equalIndex, Int32Array, -1, Int32Array), indexes = new Int32Array(coordinates.length); for (var i = 0, n = coordinates.length; i < n; ++i) { indexes[i] = indexByPoint.maybeSet(i, i); } return indexes; } function hashIndex(i) { return hashPoint(coordinates[i]); } function equalIndex(i, j) { return equalPoint(coordinates[i], coordinates[j]); } visitedByIndex = leftByIndex = rightByIndex = null; var junctionByPoint = hashset(junctionCount * 1.4, hashPoint, equalPoint), j; // Convert back to a standard hashset by point for caller convenience. for (i = 0, n = coordinates.length; i < n; ++i) { if (junctionByIndex[j = indexes[i]]) { junctionByPoint.add(coordinates[j]); } } return junctionByPoint; }; // Given an extracted (pre-)topology, cuts (or rotates) arcs so that all shared // point sequences are identified. The topology can then be subsequently deduped // to remove exact duplicate arcs. var cut = function(topology) { var junctions = join(topology), coordinates = topology.coordinates, lines = topology.lines, rings = topology.rings, next, i, n; for (i = 0, n = lines.length; i < n; ++i) { var line = lines[i], lineMid = line[0], lineEnd = line[1]; while (++lineMid < lineEnd) { if (junctions.has(coordinates[lineMid])) { next = {0: lineMid, 1: line[1]}; line[1] = lineMid; line = line.next = next; } } } for (i = 0, n = rings.length; i < n; ++i) { var ring = rings[i], ringStart = ring[0], ringMid = ringStart, ringEnd = ring[1], ringFixed = junctions.has(coordinates[ringStart]); while (++ringMid < ringEnd) { if (junctions.has(coordinates[ringMid])) { if (ringFixed) { next = {0: ringMid, 1: ring[1]}; ring[1] = ringMid; ring = ring.next = next; } else { // For the first junction, we can rotate rather than cut. rotateArray(coordinates, ringStart, ringEnd, ringEnd - ringMid); coordinates[ringEnd] = coordinates[ringStart]; ringFixed = true; ringMid = ringStart; // restart; we may have skipped junctions } } } } return topology; }; function rotateArray(array, start, end, offset) { reverse$1(array, start, end); reverse$1(array, start, start + offset); reverse$1(array, start + offset, end); } function reverse$1(array, start, end) { for (var mid = start + ((end-- - start) >> 1), t; start < mid; ++start, --end) { t = array[start], array[start] = array[end], array[end] = t; } } // Given a cut topology, combines duplicate arcs. var dedup = function(topology) { var coordinates = topology.coordinates, lines = topology.lines, line, rings = topology.rings, ring, arcCount = lines.length + rings.length, i, n; delete topology.lines; delete topology.rings; // Count the number of (non-unique) arcs to initialize the hashmap safely. for (i = 0, n = lines.length; i < n; ++i) { line = lines[i]; while (line = line.next) ++arcCount; } for (i = 0, n = rings.length; i < n; ++i) { ring = rings[i]; while (ring = ring.next) ++arcCount; } var arcsByEnd = hashmap(arcCount * 2 * 1.4, hashPoint, equalPoint), arcs = topology.arcs = []; for (i = 0, n = lines.length; i < n; ++i) { line = lines[i]; do { dedupLine(line); } while (line = line.next); } for (i = 0, n = rings.length; i < n; ++i) { ring = rings[i]; if (ring.next) { // arc is no longer closed do { dedupLine(ring); } while (ring = ring.next); } else { dedupRing(ring); } } function dedupLine(arc) { var startPoint, endPoint, startArcs, startArc, endArcs, endArc, i, n; // Does this arc match an existing arc in order? if (startArcs = arcsByEnd.get(startPoint = coordinates[arc[0]])) { for (i = 0, n = startArcs.length; i < n; ++i) { startArc = startArcs[i]; if (equalLine(startArc, arc)) { arc[0] = startArc[0]; arc[1] = startArc[1]; return; } } } // Does this arc match an existing arc in reverse order? if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[1]])) { for (i = 0, n = endArcs.length; i < n; ++i) { endArc = endArcs[i]; if (reverseEqualLine(endArc, arc)) { arc[1] = endArc[0]; arc[0] = endArc[1]; return; } } } if (startArcs) startArcs.push(arc); else arcsByEnd.set(startPoint, [arc]); if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]); arcs.push(arc); } function dedupRing(arc) { var endPoint, endArcs, endArc, i, n; // Does this arc match an existing line in order, or reverse order? // Rings are closed, so their start point and end point is the same. if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0]])) { for (i = 0, n = endArcs.length; i < n; ++i) { endArc = endArcs[i]; if (equalRing(endArc, arc)) { arc[0] = endArc[0]; arc[1] = endArc[1]; return; } if (reverseEqualRing(endArc, arc)) { arc[0] = endArc[1]; arc[1] = endArc[0]; return; } } } // Otherwise, does this arc match an existing ring in order, or reverse order? if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0] + findMinimumOffset(arc)])) { for (i = 0, n = endArcs.length; i < n; ++i) { endArc = endArcs[i]; if (equalRing(endArc, arc)) { arc[0] = endArc[0]; arc[1] = endArc[1]; return; } if (reverseEqualRing(endArc, arc)) { arc[0] = endArc[1]; arc[1] = endArc[0]; return; } } } if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]); arcs.push(arc); } function equalLine(arcA, arcB) { var ia = arcA[0], ib = arcB[0], ja = arcA[1], jb = arcB[1]; if (ia - ja !== ib - jb) return false; for (; ia <= ja; ++ia, ++ib) if (!equalPoint(coordinates[ia], coordinates[ib])) return false; return true; } function reverseEqualLine(arcA, arcB) { var ia = arcA[0], ib = arcB[0], ja = arcA[1], jb = arcB[1]; if (ia - ja !== ib - jb) return false; for (; ia <= ja; ++ia, --jb) if (!equalPoint(coordinates[ia], coordinates[jb])) return false; return true; } function equalRing(arcA, arcB) { var ia = arcA[0], ib = arcB[0], ja = arcA[1], jb = arcB[1], n = ja - ia; if (n !== jb - ib) return false; var ka = findMinimumOffset(arcA), kb = findMinimumOffset(arcB); for (var i = 0; i < n; ++i) { if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[ib + (i + kb) % n])) return false; } return true; } function reverseEqualRing(arcA, arcB) { var ia = arcA[0], ib = arcB[0], ja = arcA[1], jb = arcB[1], n = ja - ia; if (n !== jb - ib) return false; var ka = findMinimumOffset(arcA), kb = n - findMinimumOffset(arcB); for (var i = 0; i < n; ++i) { if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[jb - (i + kb) % n])) return false; } return true; } // Rings are rotated to a consistent, but arbitrary, start point. // This is necessary to detect when a ring and a rotated copy are dupes. function findMinimumOffset(arc) { var start = arc[0], end = arc[1], mid = start, minimum = mid, minimumPoint = coordinates[mid]; while (++mid < end) { var point = coordinates[mid]; if (point[0] < minimumPoint[0] || point[0] === minimumPoint[0] && point[1] < minimumPoint[1]) { minimum = mid; minimumPoint = point; } } return minimum - start; } return topology; }; // Given an array of arcs in absolute (but already quantized!) coordinates, // converts to fixed-point delta encoding. // This is a destructive operation that modifies the given arcs! var delta = function(arcs) { var i = -1, n = arcs.length; while (++i < n) { var arc = arcs[i], j = 0, k = 1, m = arc.length, point = arc[0], x0 = point[0], y0 = point[1], x1, y1; while (++j < m) { point = arc[j], x1 = point[0], y1 = point[1]; if (x1 !== x0 || y1 !== y0) arc[k++] = [x1 - x0, y1 - y0], x0 = x1, y0 = y1; } if (k === 1) arc[k++] = [0, 0]; // Each arc must be an array of two or more positions. arc.length = k; } return arcs; }; // Extracts the lines and rings from the specified hash of geometry objects. // // Returns an object with three properties: // // * coordinates - shared buffer of [x, y] coordinates // * lines - lines extracted from the hash, of the form [start, end] // * rings - rings extracted from the hash, of the form [start, end] // // For each ring or line, start and end represent inclusive indexes into the // coordinates buffer. For rings (and closed lines), coordinates[start] equals // coordinates[end]. // // For each line or polygon geometry in the input hash, including nested // geometries as in geometry collections, the `coordinates` array is replaced // with an equivalent `arcs` array that, for each line (for line string // geometries) or ring (for polygon geometries), points to one of the above // lines or rings. var extract = function(objects) { var index = -1, lines = [], rings = [], coordinates = []; function extractGeometry(geometry) { if (geometry && extractGeometryType.hasOwnProperty(geometry.type)) extractGeometryType[geometry.type](geometry); } var extractGeometryType = { GeometryCollection: function(o) { o.geometries.forEach(extractGeometry); }, LineString: function(o) { o.arcs = extractLine(o.arcs); }, MultiLineString: function(o) { o.arcs = o.arcs.map(extractLine); }, Polygon: function(o) { o.arcs = o.arcs.map(extractRing); }, MultiPolygon: function(o) { o.arcs = o.arcs.map(extractMultiRing); } }; function extractLine(line) { for (var i = 0, n = line.length; i < n; ++i) coordinates[++index] = line[i]; var arc = {0: index - n + 1, 1: index}; lines.push(arc); return arc; } function extractRing(ring) { for (var i = 0, n = ring.length; i < n; ++i) coordinates[++index] = ring[i]; var arc = {0: index - n + 1, 1: index}; rings.push(arc); return arc; } function extractMultiRing(rings) { return rings.map(extractRing); } for (var key in objects) { extractGeometry(objects[key]); } return { type: "Topology", coordinates: coordinates, lines: lines, rings: rings, objects: objects }; }; // Given a hash of GeoJSON objects, returns a hash of GeoJSON geometry objects. // Any null input geometry objects are represented as {type: null} in the output. // Any feature.{id,properties,bbox} are transferred to the output geometry object. // Each output geometry object is a shallow copy of the input (e.g., properties, coordinates)! var geometry = function(inputs) { var outputs = {}, key; for (key in inputs) outputs[key] = geomifyObject(inputs[key]); return outputs; }; function geomifyObject(input) { return input == null ? {type: null} : (input.type === "FeatureCollection" ? geomifyFeatureCollection : input.type === "Feature" ? geomifyFeature : geomifyGeometry)(input); } function geomifyFeatureCollection(input) { var output = {type: "GeometryCollection", geometries: input.features.map(geomifyFeature)}; if (input.bbox != null) output.bbox = input.bbox; return output; } function geomifyFeature(input) { var output = geomifyGeometry(input.geometry), key; // eslint-disable-line no-unused-vars if (input.id != null) output.id = input.id; if (input.bbox != null) output.bbox = input.bbox; for (key in input.properties) { output.properties = input.properties; break; } return output; } function geomifyGeometry(input) { if (input == null) return {type: null}; var output = input.type === "GeometryCollection" ? {type: "GeometryCollection", geometries: input.geometries.map(geomifyGeometry)} : input.type === "Point" || input.type === "MultiPoint" ? {type: input.type, coordinates: input.coordinates} : {type: input.type, arcs: input.coordinates}; // TODO Check for unknown types? if (input.bbox != null) output.bbox = input.bbox; return output; } var prequantize = function(objects, bbox, n) { var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3], kx = x1 - x0 ? (n - 1) / (x1 - x0) : 1, ky = y1 - y0 ? (n - 1) / (y1 - y0) : 1; function quantizePoint(input) { return [Math.round((input[0] - x0) * kx), Math.round((input[1] - y0) * ky)]; } function quantizePoints(input, m) { var i = -1, j = 0, n = input.length, output = new Array(n), // pessimistic pi, px, py, x, y; while (++i < n) { pi = input[i]; x = Math.round((pi[0] - x0) * kx); y = Math.round((pi[1] - y0) * ky); if (x !== px || y !== py) output[j++] = [px = x, py = y]; // non-coincident points } output.length = j; while (j < m) j = output.push([output[0][0], output[0][1]]); return output; } function quantizeLine(input) { return quantizePoints(input, 2); } function quantizeRing(input) { return quantizePoints(input, 4); } function quantizePolygon(input) { return input.map(quantizeRing); } function quantizeGeometry(o) { if (o != null && quantizeGeometryType.hasOwnProperty(o.type)) quantizeGeometryType[o.type](o); } var quantizeGeometryType = { GeometryCollection: function(o) { o.geometries.forEach(quantizeGeometry); }, Point: function(o) { o.coordinates = quantizePoint(o.coordinates); }, MultiPoint: function(o) { o.coordinates = o.coordinates.map(quantizePoint); }, LineString: function(o) { o.arcs = quantizeLine(o.arcs); }, MultiLineString: function(o) { o.arcs = o.arcs.map(quantizeLine); }, Polygon: function(o) { o.arcs = quantizePolygon(o.arcs); }, MultiPolygon: function(o) { o.arcs = o.arcs.map(quantizePolygon); } }; for (var key in objects) { quantizeGeometry(objects[key]); } return { scale: [1 / kx, 1 / ky], translate: [x0, y0] }; }; // Constructs the TopoJSON Topology for the specified hash of features. // Each object in the specified hash must be a GeoJSON object, // meaning FeatureCollection, a Feature or a geometry object. var topology = function(objects, quantization) { var bbox = bounds(objects = geometry(objects)), transform = quantization > 0 && bbox && prequantize(objects, bbox, quantization), topology = dedup(cut(extract(objects))), coordinates = topology.coordinates, indexByArc = hashmap(topology.arcs.length * 1.4, hashArc, equalArc); objects = topology.objects; // for garbage collection topology.bbox = bbox; topology.arcs = topology.arcs.map(function(arc, i) { indexByArc.set(arc, i); return coordinates.slice(arc[0], arc[1] + 1); }); delete topology.coordinates; coordinates = null; function indexGeometry(geometry$$1) { if (geometry$$1 && indexGeometryType.hasOwnProperty(geometry$$1.type)) indexGeometryType[geometry$$1.type](geometry$$1); } var indexGeometryType = { GeometryCollection: function(o) { o.geometries.forEach(indexGeometry); }, LineString: function(o) { o.arcs = indexArcs(o.arcs); }, MultiLineString: function(o) { o.arcs = o.arcs.map(indexArcs); }, Polygon: function(o) { o.arcs = o.arcs.map(indexArcs); }, MultiPolygon: function(o) { o.arcs = o.arcs.map(indexMultiArcs); } }; function indexArcs(arc) { var indexes = []; do { var index = indexByArc.get(arc); indexes.push(arc[0] < arc[1] ? index : ~index); } while (arc = arc.next); return indexes; } function indexMultiArcs(arcs) { return arcs.map(indexArcs); } for (var key in objects) { indexGeometry(objects[key]); } if (transform) { topology.transform = transform; topology.arcs = delta(topology.arcs); } return topology; }; function hashArc(arc) { var i = arc[0], j = arc[1], t; if (j < i) t = i, i = j, j = t; return i + 31 * j; } function equalArc(arcA, arcB) { var ia = arcA[0], ja = arcA[1], ib = arcB[0], jb = arcB[1], t; if (ja < ia) t = ia, ia = ja, ja = t; if (jb < ib) t = ib, ib = jb, jb = t; return ia === ib && ja === jb; } var prune = function(topology) { var oldObjects = topology.objects, newObjects = {}, oldArcs = topology.arcs, oldArcsLength = oldArcs.length, oldIndex = -1, newIndexByOldIndex = new Array(oldArcsLength), newArcsLength = 0, newArcs, newIndex = -1, key; function scanGeometry(input) { switch (input.type) { case "GeometryCollection": input.geometries.forEach(scanGeometry); break; case "LineString": scanArcs(input.arcs); break; case "MultiLineString": input.arcs.forEach(scanArcs); break; case "Polygon": input.arcs.forEach(scanArcs); break; case "MultiPolygon": input.arcs.forEach(scanMultiArcs); break; } } function scanArc(index) { if (index < 0) index = ~index; if (!newIndexByOldIndex[index]) newIndexByOldIndex[index] = 1, ++newArcsLength; } function scanArcs(arcs) { arcs.forEach(scanArc); } function scanMultiArcs(arcs) { arcs.forEach(scanArcs); } function reindexGeometry(input) { var output; switch (input.type) { case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(reindexGeometry)}; break; case "LineString": output = {type: "LineString", arcs: reindexArcs(input.arcs)}; break; case "MultiLineString": output = {type: "MultiLineString", arcs: input.arcs.map(reindexArcs)}; break; case "Polygon": output = {type: "Polygon", arcs: input.arcs.map(reindexArcs)}; break; case "MultiPolygon": output = {type: "MultiPolygon", arcs: input.arcs.map(reindexMultiArcs)}; break; default: return input; } if (input.id != null) output.id = input.id; if (input.bbox != null) output.bbox = input.bbox; if (input.properties != null) output.properties = input.properties; return output; } function reindexArc(oldIndex) { return oldIndex < 0 ? ~newIndexByOldIndex[~oldIndex] : newIndexByOldIndex[oldIndex]; } function reindexArcs(arcs) { return arcs.map(reindexArc); } function reindexMultiArcs(arcs) { return arcs.map(reindexArcs); } for (key in oldObjects) { scanGeometry(oldObjects[key]); } newArcs = new Array(newArcsLength); while (++oldIndex < oldArcsLength) { if (newIndexByOldIndex[oldIndex]) { newIndexByOldIndex[oldIndex] = ++newIndex; newArcs[newIndex] = oldArcs[oldIndex]; } } for (key in oldObjects) { newObjects[key] = reindexGeometry(oldObjects[key]); } return { type: "Topology", bbox: topology.bbox, transform: topology.transform, objects: newObjects, arcs: newArcs }; }; var filter = function(topology, filter) { var oldObjects = topology.objects, newObjects = {}, key; if (filter == null) filter = filterTrue; function filterGeometry(input) { var output, arcs; switch (input.type) { case "Polygon": { arcs = filterRings(input.arcs); output = arcs ? {type: "Polygon", arcs: arcs} : {type: null}; break; } case "MultiPolygon": { arcs = input.arcs.map(filterRings).filter(filterIdentity); output = arcs.length ? {type: "MultiPolygon", arcs: arcs} : {type: null}; break; } case "GeometryCollection": { arcs = input.geometries.map(filterGeometry).filter(filterNotNull); output = arcs.length ? {type: "GeometryCollection", geometries: arcs} : {type: null}; break; } default: return input; } if (input.id != null) output.id = input.id; if (input.bbox != null) output.bbox = input.bbox; if (input.properties != null) output.properties = input.properties; return output; } function filterRings(arcs) { return arcs.length && filterExteriorRing(arcs[0]) // if the exterior is small, ignore any holes ? [arcs[0]].concat(arcs.slice(1).filter(filterInteriorRing)) : null; } function filterExteriorRing(ring) { return filter(ring, false); } function filterInteriorRing(ring) { return filter(ring, true); } for (key in oldObjects) { newObjects[key] = filterGeometry(oldObjects[key]); } return prune({ type: "Topology", bbox: topology.bbox, transform: topology.transform, objects: newObjects, arcs: topology.arcs }); }; function filterTrue() { return true; } function filterIdentity(x) { return x; } function filterNotNull(geometry) { return geometry.type != null; } var filterAttached = function(topology) { var ownerByArc = new Array(topology.arcs.length), // arc index -> index of unique associated ring, or -1 if used by multiple rings ownerIndex = 0, key; function testGeometry(o) { switch (o.type) { case "GeometryCollection": o.geometries.forEach(testGeometry); break; case "Polygon": testArcs(o.arcs); break; case "MultiPolygon": o.arcs.forEach(testArcs); break; } } function testArcs(arcs) { for (var i = 0, n = arcs.length; i < n; ++i, ++ownerIndex) { for (var ring = arcs[i], j = 0, m = ring.length; j < m; ++j) { var arc = ring[j]; if (arc < 0) arc = ~arc; var owner = ownerByArc[arc]; if (owner == null) ownerByArc[arc] = ownerIndex; else if (owner !== ownerIndex) ownerByArc[arc] = -1; } } } for (key in topology.objects) { testGeometry(topology.objects[key]); } return function(ring) { for (var j = 0, m = ring.length, arc; j < m; ++j) { if (ownerByArc[(arc = ring[j]) < 0 ? ~arc : arc] === -1) { return true; } } return false; }; }; function planarTriangleArea(triangle) { var a = triangle[0], b = triangle[1], c = triangle[2]; return Math.abs((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])) / 2; } function planarRingArea$1(ring) { var i = -1, n = ring.length, a, b = ring[n - 1], area = 0; while (++i < n) a = b, b = ring[i], area += a[0] * b[1] - a[1] * b[0]; return Math.abs(area) / 2; } var filterWeight = function(topology, minWeight, weight) { minWeight = minWeight == null ? Number.MIN_VALUE : +minWeight; if (weight == null) weight = planarRingArea$1; return function(ring, interior) { return weight(feature(topology, {type: "Polygon", arcs: [ring]}).geometry.coordinates[0], interior) >= minWeight; }; }; var filterAttachedWeight = function(topology, minWeight, weight) { var a = filterAttached(topology), w = filterWeight(topology, minWeight, weight); return function(ring, interior) { return a(ring, interior) || w(ring, interior); }; }; function compare(a, b) { return a[1][2] - b[1][2]; } var newHeap = function() { var heap = {}, array = [], size = 0; heap.push = function(object) { up(array[object._ = size] = object, size++); return size; }; heap.pop = function() { if (size <= 0) return; var removed = array[0], object; if (--size > 0) object = array[size], down(array[object._ = 0] = object, 0); return removed; }; heap.remove = function(removed) { var i = removed._, object; if (array[i] !== removed) return; // invalid request if (i !== --size) object = array[size], (compare(object, removed) < 0 ? up : down)(array[object._ = i] = object, i); return i; }; function up(object, i) { while (i > 0) { var j = ((i + 1) >> 1) - 1, parent = array[j]; if (compare(object, parent) >= 0) break; array[parent._ = i] = parent; array[object._ = i = j] = object; } } function down(object, i) { while (true) { var r = (i + 1) << 1, l = r - 1, j = i, child = array[j]; if (l < size && compare(array[l], child) < 0) child = array[j = l]; if (r < size && compare(array[r], child) < 0) child = array[j = r]; if (j === i) break; array[child._ = i] = child; array[object._ = i = j] = object; } } return heap; }; function copy(point) { return [point[0], point[1], 0]; } var presimplify = function(topology, weight) { var point = topology.transform ? transform(topology.transform) : copy, heap = newHeap(); if (weight == null) weight = planarTriangleArea; var arcs = topology.arcs.map(function(arc) { var triangles = [], maxWeight = 0, triangle, i, n; arc = arc.map(point); for (i = 1, n = arc.length - 1; i < n; ++i) { triangle = [arc[i - 1], arc[i], arc[i + 1]]; triangle[1][2] = weight(triangle); triangles.push(triangle); heap.push(triangle); } // Always keep the arc endpoints! arc[0][2] = arc[n][2] = Infinity; for (i = 0, n = triangles.length; i < n; ++i) { triangle = triangles[i]; triangle.previous = triangles[i - 1]; triangle.next = triangles[i + 1]; } while (triangle = heap.pop()) { var previous = triangle.previous, next = triangle.next; // If the weight of the current point is less than that of the previous // point to be eliminated, use the latter’s weight instead. This ensures // that the current point cannot be eliminated without eliminating // previously- eliminated points. if (triangle[1][2] < maxWeight) triangle[1][2] = maxWeight; else maxWeight = triangle[1][2]; if (previous) { previous.next = next; previous[2] = triangle[2]; update(previous); } if (next) { next.previous = previous; next[0] = triangle[0]; update(next); } } return arc; }); function update(triangle) { heap.remove(triangle); triangle[1][2] = weight(triangle); heap.push(triangle); } return { type: "Topology", bbox: topology.bbox, objects: topology.objects, arcs: arcs }; }; var quantile = function(topology, p) { var array = []; topology.arcs.forEach(function(arc) { arc.forEach(function(point) { if (isFinite(point[2])) { // Ignore endpoints, whose weight is Infinity. array.push(point[2]); } }); }); return array.length && quantile$1(array.sort(descending), p); }; function quantile$1(array, p) { if (!(n = array.length)) return; if ((p = +p) <= 0 || n < 2) return array[0]; if (p >= 1) return array[n - 1]; var n, h = (n - 1) * p, i = Math.floor(h), a = array[i], b = array[i + 1]; return a + (b - a) * (h - i); } function descending(a, b) { return b - a; } var simplify = function(topology, minWeight) { minWeight = minWeight == null ? Number.MIN_VALUE : +minWeight; // Remove points whose weight is less than the minimum weight. var arcs = topology.arcs.map(function(input) { var i = -1, j = 0, n = input.length, output = new Array(n), // pessimistic point; while (++i < n) { if ((point = input[i])[2] >= minWeight) { output[j++] = [point[0], point[1]]; } } output.length = j; return output; }); return { type: "Topology", transform: topology.transform, bbox: topology.bbox, objects: topology.objects, arcs: arcs }; }; var pi = Math.PI; var tau = 2 * pi; var quarterPi = pi / 4; var radians = pi / 180; var abs = Math.abs; var atan2 = Math.atan2; var cos = Math.cos; var sin = Math.sin; function halfArea(ring, closed) { var i = 0, n = ring.length, sum = 0, point = ring[closed ? i++ : n - 1], lambda0, lambda1 = point[0] * radians, phi1 = (point[1] * radians) / 2 + quarterPi, cosPhi0, cosPhi1 = cos(phi1), sinPhi0, sinPhi1 = sin(phi1); for (; i < n; ++i) { point = ring[i]; lambda0 = lambda1, lambda1 = point[0] * radians; phi1 = (point[1] * radians) / 2 + quarterPi; cosPhi0 = cosPhi1, cosPhi1 = cos(phi1); sinPhi0 = sinPhi1, sinPhi1 = sin(phi1); // Spherical excess E for a spherical triangle with vertices: south pole, // previous point, current point. Uses a formula derived from Cagnoli’s // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). // See https://github.com/d3/d3-geo/blob/master/README.md#geoArea var dLambda = lambda1 - lambda0, sdLambda = dLambda >= 0 ? 1 : -1, adLambda = sdLambda * dLambda, k = sinPhi0 * sinPhi1, u = cosPhi0 * cosPhi1 + k * cos(adLambda), v = k * sdLambda * sin(adLambda); sum += atan2(v, u); } return sum; } function sphericalRingArea(ring, interior) { var sum = halfArea(ring, true); if (interior) sum *= -1; return (sum < 0 ? tau + sum : sum) * 2; } function sphericalTriangleArea(t) { return abs(halfArea(t, false)) * 2; } exports.bbox = bbox; exports.feature = feature; exports.mesh = mesh; exports.meshArcs = meshArcs; exports.merge = merge; exports.mergeArcs = mergeArcs; exports.neighbors = neighbors; exports.quantize = quantize; exports.transform = transform; exports.untransform = untransform; exports.topology = topology; exports.filter = filter; exports.filterAttached = filterAttached; exports.filterAttachedWeight = filterAttachedWeight; exports.filterWeight = filterWeight; exports.planarRingArea = planarRingArea$1; exports.planarTriangleArea = planarTriangleArea; exports.presimplify = presimplify; exports.quantile = quantile; exports.simplify = simplify; exports.sphericalRingArea = sphericalRingArea; exports.sphericalTriangleArea = sphericalTriangleArea; Object.defineProperty(exports, '__esModule', { value: true }); }))); var topojson = tmp$4.topojson; function defaultCrsFunction(coordinates) { return Cartesian3.fromDegrees(coordinates[0], coordinates[1], coordinates[2]); } var crsNames = { "urn:ogc:def:crs:OGC:1.3:CRS84": defaultCrsFunction, "EPSG:4326": defaultCrsFunction, "urn:ogc:def:crs:EPSG::4326": defaultCrsFunction, }; var crsLinkHrefs = {}; var crsLinkTypes = {}; var defaultMarkerSize = 48; var defaultMarkerSymbol; var defaultMarkerColor = Color.ROYALBLUE; var defaultStroke = Color.YELLOW; var defaultStrokeWidth = 2; var defaultFill = Color.fromBytes(255, 255, 0, 100); var defaultClampToGround = false; var sizes = { small: 24, medium: 48, large: 64, }; var simpleStyleIdentifiers = [ "title", "description", // "marker-size", "marker-symbol", "marker-color", "stroke", // "stroke-opacity", "stroke-width", "fill", "fill-opacity", ]; function defaultDescribe(properties, nameProperty) { var html = ""; for (var key in properties) { if (properties.hasOwnProperty(key)) { if (key === nameProperty || simpleStyleIdentifiers.indexOf(key) !== -1) { continue; } var value = properties[key]; if (defined(value)) { if (typeof value === "object") { html += "" + key + "" + defaultDescribe(value) + ""; } else { html += "" + key + "" + value + ""; } } } } if (html.length > 0) { html = '' + html + "
"; } return html; } function createDescriptionCallback(describe, properties, nameProperty) { var description; return function (time, result) { if (!defined(description)) { description = describe(properties, nameProperty); } return description; }; } function defaultDescribeProperty(properties, nameProperty) { return new CallbackProperty( createDescriptionCallback(defaultDescribe, properties, nameProperty), true ); } //GeoJSON specifies only the Feature object has a usable id property //But since "multi" geometries create multiple entity, //we can't use it for them either. function createObject(geoJson, entityCollection, describe) { var id = geoJson.id; if (!defined(id) || geoJson.type !== "Feature") { id = createGuid(); } else { var i = 2; var finalId = id; while (defined(entityCollection.getById(finalId))) { finalId = id + "_" + i; i++; } id = finalId; } var entity = entityCollection.getOrCreateEntity(id); var properties = geoJson.properties; if (defined(properties)) { entity.properties = properties; var nameProperty; //Check for the simplestyle specified name first. var name = properties.title; if (defined(name)) { entity.name = name; nameProperty = "title"; } else { //Else, find the name by selecting an appropriate property. //The name will be obtained based on this order: //1) The first case-insensitive property with the name 'title', //2) The first case-insensitive property with the name 'name', //3) The first property containing the word 'title'. //4) The first property containing the word 'name', var namePropertyPrecedence = Number.MAX_VALUE; for (var key in properties) { if (properties.hasOwnProperty(key) && properties[key]) { var lowerKey = key.toLowerCase(); if (namePropertyPrecedence > 1 && lowerKey === "title") { namePropertyPrecedence = 1; nameProperty = key; break; } else if (namePropertyPrecedence > 2 && lowerKey === "name") { namePropertyPrecedence = 2; nameProperty = key; } else if (namePropertyPrecedence > 3 && /title/i.test(key)) { namePropertyPrecedence = 3; nameProperty = key; } else if (namePropertyPrecedence > 4 && /name/i.test(key)) { namePropertyPrecedence = 4; nameProperty = key; } } } if (defined(nameProperty)) { entity.name = properties[nameProperty]; } } var description = properties.description; if (description !== null) { entity.description = !defined(description) ? describe(properties, nameProperty) : new ConstantProperty(description); } } return entity; } function coordinatesArrayToCartesianArray(coordinates, crsFunction) { var positions = new Array(coordinates.length); for (var i = 0; i < coordinates.length; i++) { positions[i] = crsFunction(coordinates[i]); } return positions; } var geoJsonObjectTypes = { Feature: processFeature$1, FeatureCollection: processFeatureCollection, GeometryCollection: processGeometryCollection, LineString: processLineString, MultiLineString: processMultiLineString, MultiPoint: processMultiPoint, MultiPolygon: processMultiPolygon, Point: processPoint$1, Polygon: processPolygon$1, Topology: processTopology, }; var geometryTypes$1 = { GeometryCollection: processGeometryCollection, LineString: processLineString, MultiLineString: processMultiLineString, MultiPoint: processMultiPoint, MultiPolygon: processMultiPolygon, Point: processPoint$1, Polygon: processPolygon$1, Topology: processTopology, }; // GeoJSON processing functions function processFeature$1(dataSource, feature, notUsed, crsFunction, options) { if (feature.geometry === null) { //Null geometry is allowed, so just create an empty entity instance for it. createObject(feature, dataSource._entityCollection, options.describe); return; } if (!defined(feature.geometry)) { throw new RuntimeError("feature.geometry is required."); } var geometryType = feature.geometry.type; var geometryHandler = geometryTypes$1[geometryType]; if (!defined(geometryHandler)) { throw new RuntimeError("Unknown geometry type: " + geometryType); } geometryHandler(dataSource, feature, feature.geometry, crsFunction, options); } function processFeatureCollection( dataSource, featureCollection, notUsed, crsFunction, options ) { var features = featureCollection.features; for (var i = 0, len = features.length; i < len; i++) { processFeature$1(dataSource, features[i], undefined, crsFunction, options); } } function processGeometryCollection( dataSource, geoJson, geometryCollection, crsFunction, options ) { var geometries = geometryCollection.geometries; for (var i = 0, len = geometries.length; i < len; i++) { var geometry = geometries[i]; var geometryType = geometry.type; var geometryHandler = geometryTypes$1[geometryType]; if (!defined(geometryHandler)) { throw new RuntimeError("Unknown geometry type: " + geometryType); } geometryHandler(dataSource, geoJson, geometry, crsFunction, options); } } function createPoint$1(dataSource, geoJson, crsFunction, coordinates, options) { var symbol = options.markerSymbol; var color = options.markerColor; var size = options.markerSize; var properties = geoJson.properties; if (defined(properties)) { var cssColor = properties["marker-color"]; if (defined(cssColor)) { color = Color.fromCssColorString(cssColor); } size = defaultValue(sizes[properties["marker-size"]], size); var markerSymbol = properties["marker-symbol"]; if (defined(markerSymbol)) { symbol = markerSymbol; } } var canvasOrPromise; if (defined(symbol)) { if (symbol.length === 1) { canvasOrPromise = dataSource._pinBuilder.fromText( symbol.toUpperCase(), color, size ); } else { canvasOrPromise = dataSource._pinBuilder.fromMakiIconId( symbol, color, size ); } } else { canvasOrPromise = dataSource._pinBuilder.fromColor(color, size); } var billboard = new BillboardGraphics(); billboard.verticalOrigin = new ConstantProperty(VerticalOrigin$1.BOTTOM); // Clamp to ground if there isn't a height specified if (coordinates.length === 2 && options.clampToGround) { billboard.heightReference = HeightReference$1.CLAMP_TO_GROUND; } var entity = createObject( geoJson, dataSource._entityCollection, options.describe ); entity.billboard = billboard; entity.position = new ConstantPositionProperty(crsFunction(coordinates)); var promise = when(canvasOrPromise) .then(function (image) { billboard.image = new ConstantProperty(image); }) .otherwise(function () { billboard.image = new ConstantProperty( dataSource._pinBuilder.fromColor(color, size) ); }); dataSource._promises.push(promise); } function processPoint$1(dataSource, geoJson, geometry, crsFunction, options) { createPoint$1(dataSource, geoJson, crsFunction, geometry.coordinates, options); } function processMultiPoint( dataSource, geoJson, geometry, crsFunction, options ) { var coordinates = geometry.coordinates; for (var i = 0; i < coordinates.length; i++) { createPoint$1(dataSource, geoJson, crsFunction, coordinates[i], options); } } function createLineString$1( dataSource, geoJson, crsFunction, coordinates, options ) { var material = options.strokeMaterialProperty; var widthProperty = options.strokeWidthProperty; var properties = geoJson.properties; if (defined(properties)) { var width = properties["stroke-width"]; if (defined(width)) { widthProperty = new ConstantProperty(width); } var color; var stroke = properties.stroke; if (defined(stroke)) { color = Color.fromCssColorString(stroke); } var opacity = properties["stroke-opacity"]; if (defined(opacity) && opacity !== 1.0) { if (!defined(color)) { color = material.color.clone(); } color.alpha = opacity; } if (defined(color)) { material = new ColorMaterialProperty(color); } } var entity = createObject( geoJson, dataSource._entityCollection, options.describe ); var polylineGraphics = new PolylineGraphics(); entity.polyline = polylineGraphics; polylineGraphics.clampToGround = options.clampToGround; polylineGraphics.material = material; polylineGraphics.width = widthProperty; polylineGraphics.positions = new ConstantProperty( coordinatesArrayToCartesianArray(coordinates, crsFunction) ); polylineGraphics.arcType = ArcType$1.RHUMB; } function processLineString( dataSource, geoJson, geometry, crsFunction, options ) { createLineString$1( dataSource, geoJson, crsFunction, geometry.coordinates, options ); } function processMultiLineString( dataSource, geoJson, geometry, crsFunction, options ) { var lineStrings = geometry.coordinates; for (var i = 0; i < lineStrings.length; i++) { createLineString$1(dataSource, geoJson, crsFunction, lineStrings[i], options); } } function createPolygon$1(dataSource, geoJson, crsFunction, coordinates, options) { if (coordinates.length === 0 || coordinates[0].length === 0) { return; } var outlineColorProperty = options.strokeMaterialProperty.color; var material = options.fillMaterialProperty; var widthProperty = options.strokeWidthProperty; var properties = geoJson.properties; if (defined(properties)) { var width = properties["stroke-width"]; if (defined(width)) { widthProperty = new ConstantProperty(width); } var color; var stroke = properties.stroke; if (defined(stroke)) { color = Color.fromCssColorString(stroke); } var opacity = properties["stroke-opacity"]; if (defined(opacity) && opacity !== 1.0) { if (!defined(color)) { color = options.strokeMaterialProperty.color.clone(); } color.alpha = opacity; } if (defined(color)) { outlineColorProperty = new ConstantProperty(color); } var fillColor; var fill = properties.fill; if (defined(fill)) { fillColor = Color.fromCssColorString(fill); fillColor.alpha = material.color.alpha; } opacity = properties["fill-opacity"]; if (defined(opacity) && opacity !== material.color.alpha) { if (!defined(fillColor)) { fillColor = material.color.clone(); } fillColor.alpha = opacity; } if (defined(fillColor)) { material = new ColorMaterialProperty(fillColor); } } var polygon = new PolygonGraphics(); polygon.outline = new ConstantProperty(true); polygon.outlineColor = outlineColorProperty; polygon.outlineWidth = widthProperty; polygon.material = material; polygon.arcType = ArcType$1.RHUMB; var holes = []; for (var i = 1, len = coordinates.length; i < len; i++) { holes.push( new PolygonHierarchy( coordinatesArrayToCartesianArray(coordinates[i], crsFunction) ) ); } var positions = coordinates[0]; polygon.hierarchy = new ConstantProperty( new PolygonHierarchy( coordinatesArrayToCartesianArray(positions, crsFunction), holes ) ); if (positions[0].length > 2) { polygon.perPositionHeight = new ConstantProperty(true); } else if (!options.clampToGround) { polygon.height = 0; } var entity = createObject( geoJson, dataSource._entityCollection, options.describe ); entity.polygon = polygon; } function processPolygon$1(dataSource, geoJson, geometry, crsFunction, options) { createPolygon$1( dataSource, geoJson, crsFunction, geometry.coordinates, options ); } function processMultiPolygon( dataSource, geoJson, geometry, crsFunction, options ) { var polygons = geometry.coordinates; for (var i = 0; i < polygons.length; i++) { createPolygon$1(dataSource, geoJson, crsFunction, polygons[i], options); } } function processTopology(dataSource, geoJson, geometry, crsFunction, options) { for (var property in geometry.objects) { if (geometry.objects.hasOwnProperty(property)) { var feature = topojson.feature(geometry, geometry.objects[property]); var typeHandler = geoJsonObjectTypes[feature.type]; typeHandler(dataSource, feature, feature, crsFunction, options); } } } /** * @typedef {Object} GeoJsonDataSource.LoadOptions * * Initialization options for the `load` method. * * @property {String} [sourceUri] Overrides the url to use for resolving relative links. * @property {Number} [markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels. * @property {String} [markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point. * @property {Color} [markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point. * @property {Color} [stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines. * @property {Number} [strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines. * @property {Color} [fill=GeoJsonDataSource.fill] The default color for polygon interiors. * @property {Boolean} [clampToGround=GeoJsonDataSource.clampToGround] true if we want the geometry features (polygons or linestrings) clamped to the ground. * @property {Credit|String} [credit] A credit for the data source, which is displayed on the canvas. */ /** * A {@link DataSource} which processes both * {@link http://www.geojson.org/|GeoJSON} and {@link https://github.com/mbostock/topojson|TopoJSON} data. * {@link https://github.com/mapbox/simplestyle-spec|simplestyle-spec} properties will also be used if they * are present. * * @alias GeoJsonDataSource * @constructor * * @param {String} [name] The name of this data source. If undefined, a name will be taken from * the name of the GeoJSON file. * * @demo {@link https://sandcastle.cesium.com/index.html?src=GeoJSON%20and%20TopoJSON.html|Cesium Sandcastle GeoJSON and TopoJSON Demo} * @demo {@link https://sandcastle.cesium.com/index.html?src=GeoJSON%20simplestyle.html|Cesium Sandcastle GeoJSON simplestyle Demo} * * @example * var viewer = new Cesium.Viewer('cesiumContainer'); * viewer.dataSources.add(Cesium.GeoJsonDataSource.load('../../SampleData/ne_10m_us_states.topojson', { * stroke: Cesium.Color.HOTPINK, * fill: Cesium.Color.PINK, * strokeWidth: 3, * markerSymbol: '?' * })); */ function GeoJsonDataSource(name) { this._name = name; this._changed = new Event(); this._error = new Event(); this._isLoading = false; this._loading = new Event(); this._entityCollection = new EntityCollection(this); this._promises = []; this._pinBuilder = new PinBuilder(); this._entityCluster = new EntityCluster(); this._credit = undefined; this._resourceCredits = []; } /** * Creates a Promise to a new instance loaded with the provided GeoJSON or TopoJSON data. * * @param {Resource|String|Object} data A url, GeoJSON object, or TopoJSON object to be loaded. * @param {GeoJsonDataSource.LoadOptions} [options] An object specifying configuration options * * @returns {Promise.} A promise that will resolve when the data is loaded. */ GeoJsonDataSource.load = function (data, options) { return new GeoJsonDataSource().load(data, options); }; Object.defineProperties(GeoJsonDataSource, { /** * Gets or sets the default size of the map pin created for each point, in pixels. * @memberof GeoJsonDataSource * @type {Number} * @default 48 */ markerSize: { get: function () { return defaultMarkerSize; }, set: function (value) { defaultMarkerSize = value; }, }, /** * Gets or sets the default symbol of the map pin created for each point. * This can be any valid {@link http://mapbox.com/maki/|Maki} identifier, any single character, * or blank if no symbol is to be used. * @memberof GeoJsonDataSource * @type {String} */ markerSymbol: { get: function () { return defaultMarkerSymbol; }, set: function (value) { defaultMarkerSymbol = value; }, }, /** * Gets or sets the default color of the map pin created for each point. * @memberof GeoJsonDataSource * @type {Color} * @default Color.ROYALBLUE */ markerColor: { get: function () { return defaultMarkerColor; }, set: function (value) { defaultMarkerColor = value; }, }, /** * Gets or sets the default color of polylines and polygon outlines. * @memberof GeoJsonDataSource * @type {Color} * @default Color.BLACK */ stroke: { get: function () { return defaultStroke; }, set: function (value) { defaultStroke = value; }, }, /** * Gets or sets the default width of polylines and polygon outlines. * @memberof GeoJsonDataSource * @type {Number} * @default 2.0 */ strokeWidth: { get: function () { return defaultStrokeWidth; }, set: function (value) { defaultStrokeWidth = value; }, }, /** * Gets or sets default color for polygon interiors. * @memberof GeoJsonDataSource * @type {Color} * @default Color.YELLOW */ fill: { get: function () { return defaultFill; }, set: function (value) { defaultFill = value; }, }, /** * Gets or sets default of whether to clamp to the ground. * @memberof GeoJsonDataSource * @type {Boolean} * @default false */ clampToGround: { get: function () { return defaultClampToGround; }, set: function (value) { defaultClampToGround = value; }, }, /** * Gets an object that maps the name of a crs to a callback function which takes a GeoJSON coordinate * and transforms it into a WGS84 Earth-fixed Cartesian. Older versions of GeoJSON which * supported the EPSG type can be added to this list as well, by specifying the complete EPSG name, * for example 'EPSG:4326'. * @memberof GeoJsonDataSource * @type {Object} */ crsNames: { get: function () { return crsNames; }, }, /** * Gets an object that maps the href property of a crs link to a callback function * which takes the crs properties object and returns a Promise that resolves * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian. * Items in this object take precedence over those defined in crsLinkHrefs, assuming * the link has a type specified. * @memberof GeoJsonDataSource * @type {Object} */ crsLinkHrefs: { get: function () { return crsLinkHrefs; }, }, /** * Gets an object that maps the type property of a crs link to a callback function * which takes the crs properties object and returns a Promise that resolves * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian. * Items in crsLinkHrefs take precedence over this object. * @memberof GeoJsonDataSource * @type {Object} */ crsLinkTypes: { get: function () { return crsLinkTypes; }, }, }); Object.defineProperties(GeoJsonDataSource.prototype, { /** * Gets or sets a human-readable name for this instance. * @memberof GeoJsonDataSource.prototype * @type {String} */ name: { get: function () { return this._name; }, set: function (value) { if (this._name !== value) { this._name = value; this._changed.raiseEvent(this); } }, }, /** * This DataSource only defines static data, therefore this property is always undefined. * @memberof GeoJsonDataSource.prototype * @type {DataSourceClock} */ clock: { value: undefined, writable: false, }, /** * Gets the collection of {@link Entity} instances. * @memberof GeoJsonDataSource.prototype * @type {EntityCollection} */ entities: { get: function () { return this._entityCollection; }, }, /** * Gets a value indicating if the data source is currently loading data. * @memberof GeoJsonDataSource.prototype * @type {Boolean} */ isLoading: { get: function () { return this._isLoading; }, }, /** * Gets an event that will be raised when the underlying data changes. * @memberof GeoJsonDataSource.prototype * @type {Event} */ changedEvent: { get: function () { return this._changed; }, }, /** * Gets an event that will be raised if an error is encountered during processing. * @memberof GeoJsonDataSource.prototype * @type {Event} */ errorEvent: { get: function () { return this._error; }, }, /** * Gets an event that will be raised when the data source either starts or stops loading. * @memberof GeoJsonDataSource.prototype * @type {Event} */ loadingEvent: { get: function () { return this._loading; }, }, /** * Gets whether or not this data source should be displayed. * @memberof GeoJsonDataSource.prototype * @type {Boolean} */ show: { get: function () { return this._entityCollection.show; }, set: function (value) { this._entityCollection.show = value; }, }, /** * Gets or sets the clustering options for this data source. This object can be shared between multiple data sources. * * @memberof GeoJsonDataSource.prototype * @type {EntityCluster} */ clustering: { get: function () { return this._entityCluster; }, set: function (value) { //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value must be defined."); } //>>includeEnd('debug'); this._entityCluster = value; }, }, /** * Gets the credit that will be displayed for the data source * @memberof GeoJsonDataSource.prototype * @type {Credit} */ credit: { get: function () { return this._credit; }, }, }); /** * Asynchronously loads the provided GeoJSON or TopoJSON data, replacing any existing data. * * @param {Resource|String|Object} data A url, GeoJSON object, or TopoJSON object to be loaded. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] Overrides the url to use for resolving relative links. * @param {GeoJsonDataSource.describe} [options.describe=GeoJsonDataSource.defaultDescribeProperty] A function which returns a Property object (or just a string), * which converts the properties into an html description. * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors. * @param {Boolean} [options.clampToGround=GeoJsonDataSource.clampToGround] true if we want the features clamped to the ground. * @param {Credit|String} [options.credit] A credit for the data source, which is displayed on the canvas. * * @returns {Promise.} a promise that will resolve when the GeoJSON is loaded. */ GeoJsonDataSource.prototype.load = function (data, options) { //>>includeStart('debug', pragmas.debug); if (!defined(data)) { throw new DeveloperError("data is required."); } //>>includeEnd('debug'); DataSource.setLoading(this, true); options = defaultValue(options, defaultValue.EMPTY_OBJECT); // User specified credit var credit = options.credit; if (typeof credit === "string") { credit = new Credit(credit); } this._credit = credit; var promise = data; var sourceUri = options.sourceUri; if (typeof data === "string" || data instanceof Resource) { data = Resource.createIfNeeded(data); promise = data.fetchJson(); sourceUri = defaultValue(sourceUri, data.getUrlComponent()); // Add resource credits to our list of credits to display var resourceCredits = this._resourceCredits; var credits = data.credits; if (defined(credits)) { var length = credits.length; for (var i = 0; i < length; i++) { resourceCredits.push(credits[i]); } } } options = { describe: defaultValue(options.describe, defaultDescribeProperty), markerSize: defaultValue(options.markerSize, defaultMarkerSize), markerSymbol: defaultValue(options.markerSymbol, defaultMarkerSymbol), markerColor: defaultValue(options.markerColor, defaultMarkerColor), strokeWidthProperty: new ConstantProperty( defaultValue(options.strokeWidth, defaultStrokeWidth) ), strokeMaterialProperty: new ColorMaterialProperty( defaultValue(options.stroke, defaultStroke) ), fillMaterialProperty: new ColorMaterialProperty( defaultValue(options.fill, defaultFill) ), clampToGround: defaultValue(options.clampToGround, defaultClampToGround), }; var that = this; return when(promise, function (geoJson) { return load$1(that, geoJson, options, sourceUri); }).otherwise(function (error) { DataSource.setLoading(that, false); that._error.raiseEvent(that, error); console.log(error); return when.reject(error); }); }; /** * Updates the data source to the provided time. This function is optional and * is not required to be implemented. It is provided for data sources which * retrieve data based on the current animation time or scene state. * If implemented, update will be called by {@link DataSourceDisplay} once a frame. * * @param {JulianDate} time The simulation time. * @returns {Boolean} True if this data source is ready to be displayed at the provided time, false otherwise. */ GeoJsonDataSource.prototype.update = function (time) { return true; }; function load$1(that, geoJson, options, sourceUri) { var name; if (defined(sourceUri)) { name = getFilenameFromUri(sourceUri); } if (defined(name) && that._name !== name) { that._name = name; that._changed.raiseEvent(that); } var typeHandler = geoJsonObjectTypes[geoJson.type]; if (!defined(typeHandler)) { throw new RuntimeError("Unsupported GeoJSON object type: " + geoJson.type); } //Check for a Coordinate Reference System. var crs = geoJson.crs; var crsFunction = crs !== null ? defaultCrsFunction : null; if (defined(crs)) { if (!defined(crs.properties)) { throw new RuntimeError("crs.properties is undefined."); } var properties = crs.properties; if (crs.type === "name") { crsFunction = crsNames[properties.name]; if (!defined(crsFunction)) { throw new RuntimeError("Unknown crs name: " + properties.name); } } else if (crs.type === "link") { var handler = crsLinkHrefs[properties.href]; if (!defined(handler)) { handler = crsLinkTypes[properties.type]; } if (!defined(handler)) { throw new RuntimeError( "Unable to resolve crs link: " + JSON.stringify(properties) ); } crsFunction = handler(properties); } else if (crs.type === "EPSG") { crsFunction = crsNames["EPSG:" + properties.code]; if (!defined(crsFunction)) { throw new RuntimeError("Unknown crs EPSG code: " + properties.code); } } else { throw new RuntimeError("Unknown crs type: " + crs.type); } } return when(crsFunction, function (crsFunction) { that._entityCollection.removeAll(); // null is a valid value for the crs, but means the entire load process becomes a no-op // because we can't assume anything about the coordinates. if (crsFunction !== null) { typeHandler(that, geoJson, geoJson, crsFunction, options); } return when.all(that._promises, function () { that._promises.length = 0; DataSource.setLoading(that, false); return that; }); }); } /** * Representation of from KML * @alias KmlCamera * @constructor * * @param {Cartesian3} position camera position * @param {HeadingPitchRoll} headingPitchRoll camera orientation */ function KmlCamera(position, headingPitchRoll) { this.position = position; this.headingPitchRoll = headingPitchRoll; } var tmp$3 = {}; /*! * Autolinker.js * 3.11.0 * * Copyright(c) 2019 Gregory Jacobs * MIT License * * https://github.com/gregjacobs/Autolinker.js */ (function (global, factory) { global.Autolinker = factory(); }(tmp$3, function () { /** * Assigns (shallow copies) the properties of `src` onto `dest`, if the * corresponding property on `dest` === `undefined`. * * @param {Object} dest The destination object. * @param {Object} src The source object. * @return {Object} The destination object (`dest`) */ function defaults(dest, src) { for (var prop in src) { if (src.hasOwnProperty(prop) && dest[prop] === undefined) { dest[prop] = src[prop]; } } return dest; } /** * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the * end of the string (by default, two periods: '..'). If the `str` length does not exceed * `len`, the string will be returned unchanged. * * @param {String} str The string to truncate and add an ellipsis to. * @param {Number} truncateLen The length to truncate the string at. * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str` * when truncated. Defaults to '...' */ function ellipsis(str, truncateLen, ellipsisChars) { var ellipsisLength; if (str.length > truncateLen) { if (ellipsisChars == null) { ellipsisChars = '…'; ellipsisLength = 3; } else { ellipsisLength = ellipsisChars.length; } str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars; } return str; } /** * Supports `Array.prototype.indexOf()` functionality for old IE (IE8 and below). * * @param {Array} arr The array to find an element of. * @param {*} element The element to find in the array, and return the index of. * @return {Number} The index of the `element`, or -1 if it was not found. */ function indexOf(arr, element) { if (Array.prototype.indexOf) { return arr.indexOf(element); } else { for (var i = 0, len = arr.length; i < len; i++) { if (arr[i] === element) return i; } return -1; } } /** * Removes array elements based on a filtering function. Mutates the input * array. * * Using this instead of the ES5 Array.prototype.filter() function, to allow * Autolinker compatibility with IE8, and also to prevent creating many new * arrays in memory for filtering. * * @param {Array} arr The array to remove elements from. This array is * mutated. * @param {Function} fn A function which should return `true` to * remove an element. * @return {Array} The mutated input `arr`. */ function remove(arr, fn) { for (var i = arr.length - 1; i >= 0; i--) { if (fn(arr[i]) === true) { arr.splice(i, 1); } } } /** * Performs the functionality of what modern browsers do when `String.prototype.split()` is called * with a regular expression that contains capturing parenthesis. * * For example: * * // Modern browsers: * "a,b,c".split( /(,)/ ); // --> [ 'a', ',', 'b', ',', 'c' ] * * // Old IE (including IE8): * "a,b,c".split( /(,)/ ); // --> [ 'a', 'b', 'c' ] * * This method emulates the functionality of modern browsers for the old IE case. * * @param {String} str The string to split. * @param {RegExp} splitRegex The regular expression to split the input `str` on. The splitting * character(s) will be spliced into the array, as in the "modern browsers" example in the * description of this method. * Note #1: the supplied regular expression **must** have the 'g' flag specified. * Note #2: for simplicity's sake, the regular expression does not need * to contain capturing parenthesis - it will be assumed that any match has them. * @return {String[]} The split array of strings, with the splitting character(s) included. */ function splitAndCapture(str, splitRegex) { if (!splitRegex.global) throw new Error("`splitRegex` must have the 'g' flag set"); var result = [], lastIdx = 0, match; while (match = splitRegex.exec(str)) { result.push(str.substring(lastIdx, match.index)); result.push(match[0]); // push the splitting char(s) lastIdx = match.index + match[0].length; } result.push(str.substring(lastIdx)); return result; } /** * Function that should never be called but is used to check that every * enum value is handled using TypeScript's 'never' type. */ function throwUnhandledCaseError(theValue) { throw new Error("Unhandled case for value: '" + theValue + "'"); } /** * @class Autolinker.HtmlTag * @extends Object * * Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically. * * Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use * this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}. * * ## Examples * * Example instantiation: * * var tag = new Autolinker.HtmlTag( { * tagName : 'a', * attrs : { 'href': 'http://google.com', 'class': 'external-link' }, * innerHtml : 'Google' * } ); * * tag.toAnchorString(); // Google * * // Individual accessor methods * tag.getTagName(); // 'a' * tag.getAttr( 'href' ); // 'http://google.com' * tag.hasClass( 'external-link' ); // true * * * Using mutator methods (which may be used in combination with instantiation config properties): * * var tag = new Autolinker.HtmlTag(); * tag.setTagName( 'a' ); * tag.setAttr( 'href', 'http://google.com' ); * tag.addClass( 'external-link' ); * tag.setInnerHtml( 'Google' ); * * tag.getTagName(); // 'a' * tag.getAttr( 'href' ); // 'http://google.com' * tag.hasClass( 'external-link' ); // true * * tag.toAnchorString(); // Google * * * ## Example use within a {@link Autolinker#replaceFn replaceFn} * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance, configured with the Match's href and anchor text * tag.setAttr( 'rel', 'nofollow' ); * * return tag; * } * } ); * * // generated html: * // Test google.com * * * ## Example use with a new tag for the replacement * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = new Autolinker.HtmlTag( { * tagName : 'button', * attrs : { 'title': 'Load URL: ' + match.getAnchorHref() }, * innerHtml : 'Load URL: ' + match.getAnchorText() * } ); * * return tag; * } * } ); * * // generated html: * // Test */ var HtmlTag = /** @class */ (function () { /** * @method constructor * @param {Object} [cfg] The configuration properties for this class, in an Object (map) */ function HtmlTag(cfg) { if (cfg === void 0) { cfg = {}; } /** * @cfg {String} tagName * * The tag name. Ex: 'a', 'button', etc. * * Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString} * is executed. */ this.tagName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Object.} attrs * * An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the * values are the attribute values. */ this.attrs = {}; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} innerHTML * * The inner HTML for the tag. */ this.innerHTML = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @protected * @property {RegExp} whitespaceRegex * * Regular expression used to match whitespace in a string of CSS classes. */ this.whitespaceRegex = /\s+/; // default value just to get the above doc comment in the ES5 output and documentation generator this.tagName = cfg.tagName || ''; this.attrs = cfg.attrs || {}; this.innerHTML = cfg.innerHtml || cfg.innerHTML || ''; // accept either the camelCased form or the fully capitalized acronym as in the DOM } /** * Sets the tag name that will be used to generate the tag with. * * @param {String} tagName * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setTagName = function (tagName) { this.tagName = tagName; return this; }; /** * Retrieves the tag name. * * @return {String} */ HtmlTag.prototype.getTagName = function () { return this.tagName || ''; }; /** * Sets an attribute on the HtmlTag. * * @param {String} attrName The attribute name to set. * @param {String} attrValue The attribute value to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setAttr = function (attrName, attrValue) { var tagAttrs = this.getAttrs(); tagAttrs[attrName] = attrValue; return this; }; /** * Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`. * * @param {String} attrName The attribute name to retrieve. * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag. */ HtmlTag.prototype.getAttr = function (attrName) { return this.getAttrs()[attrName]; }; /** * Sets one or more attributes on the HtmlTag. * * @param {Object.} attrs A key/value Object (map) of the attributes to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setAttrs = function (attrs) { Object.assign(this.getAttrs(), attrs); return this; }; /** * Retrieves the attributes Object (map) for the HtmlTag. * * @return {Object.} A key/value object of the attributes for the HtmlTag. */ HtmlTag.prototype.getAttrs = function () { return this.attrs || (this.attrs = {}); }; /** * Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag. * * @param {String} cssClass One or more space-separated CSS classes to set (overwrite). * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setClass = function (cssClass) { return this.setAttr('class', cssClass); }; /** * Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes. * * @param {String} cssClass One or more space-separated CSS classes to add. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.addClass = function (cssClass) { var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = (!classAttr) ? [] : classAttr.split(whitespaceRegex), newClasses = cssClass.split(whitespaceRegex), newClass; while (newClass = newClasses.shift()) { if (indexOf(classes, newClass) === -1) { classes.push(newClass); } } this.getAttrs()['class'] = classes.join(" "); return this; }; /** * Convenience method to remove one or more CSS classes from the HtmlTag. * * @param {String} cssClass One or more space-separated CSS classes to remove. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.removeClass = function (cssClass) { var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = (!classAttr) ? [] : classAttr.split(whitespaceRegex), removeClasses = cssClass.split(whitespaceRegex), removeClass; while (classes.length && (removeClass = removeClasses.shift())) { var idx = indexOf(classes, removeClass); if (idx !== -1) { classes.splice(idx, 1); } } this.getAttrs()['class'] = classes.join(" "); return this; }; /** * Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when * there are multiple. * * @return {String} */ HtmlTag.prototype.getClass = function () { return this.getAttrs()['class'] || ""; }; /** * Convenience method to check if the tag has a CSS class or not. * * @param {String} cssClass The CSS class to check for. * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise. */ HtmlTag.prototype.hasClass = function (cssClass) { return (' ' + this.getClass() + ' ').indexOf(' ' + cssClass + ' ') !== -1; }; /** * Sets the inner HTML for the tag. * * @param {String} html The inner HTML to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setInnerHTML = function (html) { this.innerHTML = html; return this; }; /** * Backwards compatibility method name. * * @param {String} html The inner HTML to set. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained. */ HtmlTag.prototype.setInnerHtml = function (html) { return this.setInnerHTML(html); }; /** * Retrieves the inner HTML for the tag. * * @return {String} */ HtmlTag.prototype.getInnerHTML = function () { return this.innerHTML || ""; }; /** * Backward compatibility method name. * * @return {String} */ HtmlTag.prototype.getInnerHtml = function () { return this.getInnerHTML(); }; /** * Override of superclass method used to generate the HTML string for the tag. * * @return {String} */ HtmlTag.prototype.toAnchorString = function () { var tagName = this.getTagName(), attrsStr = this.buildAttrsStr(); attrsStr = (attrsStr) ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes return ['<', tagName, attrsStr, '>', this.getInnerHtml(), ''].join(""); }; /** * Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate * the stringified HtmlTag. * * @protected * @return {String} Example return: `attr1="value1" attr2="value2"` */ HtmlTag.prototype.buildAttrsStr = function () { if (!this.attrs) return ""; // no `attrs` Object (map) has been set, return empty string var attrs = this.getAttrs(), attrsArr = []; for (var prop in attrs) { if (attrs.hasOwnProperty(prop)) { attrsArr.push(prop + '="' + attrs[prop] + '"'); } } return attrsArr.join(" "); }; return HtmlTag; }()); /** * Date: 2015-10-05 * Author: Kasper Søfren (https://github.com/kafoso) * * A truncation feature, where the ellipsis will be placed at a section within * the URL making it still somewhat human readable. * * @param {String} url A URL. * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "...". * @return {String} The truncated URL. */ function truncateSmart(url, truncateLen, ellipsisChars) { var ellipsisLengthBeforeParsing; var ellipsisLength; if (ellipsisChars == null) { ellipsisChars = '…'; ellipsisLength = 3; ellipsisLengthBeforeParsing = 8; } else { ellipsisLength = ellipsisChars.length; ellipsisLengthBeforeParsing = ellipsisChars.length; } var parse_url = function (url) { var urlObj = {}; var urlSub = url; var match = urlSub.match(/^([a-z]+):\/\//i); if (match) { urlObj.scheme = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i); if (match) { urlObj.host = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i); if (match) { urlObj.path = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^\?(.*?)(?=(#|$))/i); if (match) { urlObj.query = match[1]; urlSub = urlSub.substr(match[0].length); } match = urlSub.match(/^#(.*?)$/i); if (match) { urlObj.fragment = match[1]; //urlSub = urlSub.substr(match[0].length); -- not used. Uncomment if adding another block. } return urlObj; }; var buildUrl = function (urlObj) { var url = ""; if (urlObj.scheme && urlObj.host) { url += urlObj.scheme + "://"; } if (urlObj.host) { url += urlObj.host; } if (urlObj.path) { url += "/" + urlObj.path; } if (urlObj.query) { url += "?" + urlObj.query; } if (urlObj.fragment) { url += "#" + urlObj.fragment; } return url; }; var buildSegment = function (segment, remainingAvailableLength) { var remainingAvailableLengthHalf = remainingAvailableLength / 2, startOffset = Math.ceil(remainingAvailableLengthHalf), endOffset = (-1) * Math.floor(remainingAvailableLengthHalf), end = ""; if (endOffset < 0) { end = segment.substr(endOffset); } return segment.substr(0, startOffset) + ellipsisChars + end; }; if (url.length <= truncateLen) { return url; } var availableLength = truncateLen - ellipsisLength; var urlObj = parse_url(url); // Clean up the URL if (urlObj.query) { var matchQuery = urlObj.query.match(/^(.*?)(?=(\?|\#))(.*?)$/i); if (matchQuery) { // Malformed URL; two or more "?". Removed any content behind the 2nd. urlObj.query = urlObj.query.substr(0, matchQuery[1].length); url = buildUrl(urlObj); } } if (url.length <= truncateLen) { return url; } if (urlObj.host) { urlObj.host = urlObj.host.replace(/^www\./, ""); url = buildUrl(urlObj); } if (url.length <= truncateLen) { return url; } // Process and build the URL var str = ""; if (urlObj.host) { str += urlObj.host; } if (str.length >= availableLength) { if (urlObj.host.length == truncateLen) { return (urlObj.host.substr(0, (truncateLen - ellipsisLength)) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing); } return buildSegment(str, availableLength).substr(0, availableLength + ellipsisLengthBeforeParsing); } var pathAndQuery = ""; if (urlObj.path) { pathAndQuery += "/" + urlObj.path; } if (urlObj.query) { pathAndQuery += "?" + urlObj.query; } if (pathAndQuery) { if ((str + pathAndQuery).length >= availableLength) { if ((str + pathAndQuery).length == truncateLen) { return (str + pathAndQuery).substr(0, truncateLen); } var remainingAvailableLength = availableLength - str.length; return (str + buildSegment(pathAndQuery, remainingAvailableLength)).substr(0, availableLength + ellipsisLengthBeforeParsing); } else { str += pathAndQuery; } } if (urlObj.fragment) { var fragment = "#" + urlObj.fragment; if ((str + fragment).length >= availableLength) { if ((str + fragment).length == truncateLen) { return (str + fragment).substr(0, truncateLen); } var remainingAvailableLength2 = availableLength - str.length; return (str + buildSegment(fragment, remainingAvailableLength2)).substr(0, availableLength + ellipsisLengthBeforeParsing); } else { str += fragment; } } if (urlObj.scheme && urlObj.host) { var scheme = urlObj.scheme + "://"; if ((str + scheme).length < availableLength) { return (scheme + str).substr(0, truncateLen); } } if (str.length <= truncateLen) { return str; } var end = ""; if (availableLength > 0) { end = str.substr((-1) * Math.floor(availableLength / 2)); } return (str.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing); } /** * Date: 2015-10-05 * Author: Kasper Søfren (https://github.com/kafoso) * * A truncation feature, where the ellipsis will be placed in the dead-center of the URL. * * @param {String} url A URL. * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "..". * @return {String} The truncated URL. */ function truncateMiddle(url, truncateLen, ellipsisChars) { if (url.length <= truncateLen) { return url; } var ellipsisLengthBeforeParsing; var ellipsisLength; if (ellipsisChars == null) { ellipsisChars = '…'; ellipsisLengthBeforeParsing = 8; ellipsisLength = 3; } else { ellipsisLengthBeforeParsing = ellipsisChars.length; ellipsisLength = ellipsisChars.length; } var availableLength = truncateLen - ellipsisLength; var end = ""; if (availableLength > 0) { end = url.substr((-1) * Math.floor(availableLength / 2)); } return (url.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing); } /** * A truncation feature where the ellipsis will be placed at the end of the URL. * * @param {String} anchorText * @param {Number} truncateLen The maximum length of the truncated output URL string. * @param {String} ellipsisChars The characters to place within the url, e.g. "..". * @return {String} The truncated URL. */ function truncateEnd(anchorText, truncateLen, ellipsisChars) { return ellipsis(anchorText, truncateLen, ellipsisChars); } /** * @protected * @class Autolinker.AnchorTagBuilder * @extends Object * * Builds anchor (<a>) tags for the Autolinker utility when a match is * found. * * Normally this class is instantiated, configured, and used internally by an * {@link Autolinker} instance, but may actually be used indirectly in a * {@link Autolinker#replaceFn replaceFn} to create {@link Autolinker.HtmlTag HtmlTag} * instances which may be modified before returning from the * {@link Autolinker#replaceFn replaceFn}. For example: * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance * tag.setAttr( 'rel', 'nofollow' ); * * return tag; * } * } ); * * // generated html: * // Test google.com */ var AnchorTagBuilder = /** @class */ (function () { /** * @method constructor * @param {Object} [cfg] The configuration options for the AnchorTagBuilder instance, specified in an Object (map). */ function AnchorTagBuilder(cfg) { if (cfg === void 0) { cfg = {}; } /** * @cfg {Boolean} newWindow * @inheritdoc Autolinker#newWindow */ this.newWindow = false; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Object} truncate * @inheritdoc Autolinker#truncate */ this.truncate = {}; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} className * @inheritdoc Autolinker#className */ this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator this.newWindow = cfg.newWindow || false; this.truncate = cfg.truncate || {}; this.className = cfg.className || ''; } /** * Generates the actual anchor (<a>) tag to use in place of the * matched text, via its `match` object. * * @param {Autolinker.match.Match} match The Match instance to generate an * anchor tag from. * @return {Autolinker.HtmlTag} The HtmlTag instance for the anchor tag. */ AnchorTagBuilder.prototype.build = function (match) { return new HtmlTag({ tagName: 'a', attrs: this.createAttrs(match), innerHtml: this.processAnchorText(match.getAnchorText()) }); }; /** * Creates the Object (map) of the HTML attributes for the anchor (<a>) * tag being generated. * * @protected * @param {Autolinker.match.Match} match The Match instance to generate an * anchor tag from. * @return {Object} A key/value Object (map) of the anchor tag's attributes. */ AnchorTagBuilder.prototype.createAttrs = function (match) { var attrs = { 'href': match.getAnchorHref() // we'll always have the `href` attribute }; var cssClass = this.createCssClass(match); if (cssClass) { attrs['class'] = cssClass; } if (this.newWindow) { attrs['target'] = "_blank"; attrs['rel'] = "noopener noreferrer"; // Issue #149. See https://mathiasbynens.github.io/rel-noopener/ } if (this.truncate) { if (this.truncate.length && this.truncate.length < match.getAnchorText().length) { attrs['title'] = match.getAnchorHref(); } } return attrs; }; /** * Creates the CSS class that will be used for a given anchor tag, based on * the `matchType` and the {@link #className} config. * * Example returns: * * - "" // no {@link #className} * - "myLink myLink-url" // url match * - "myLink myLink-email" // email match * - "myLink myLink-phone" // phone match * - "myLink myLink-hashtag" // hashtag match * - "myLink myLink-mention myLink-twitter" // mention match with Twitter service * * @protected * @param {Autolinker.match.Match} match The Match instance to generate an * anchor tag from. * @return {String} The CSS class string for the link. Example return: * "myLink myLink-url". If no {@link #className} was configured, returns * an empty string. */ AnchorTagBuilder.prototype.createCssClass = function (match) { var className = this.className; if (!className) { return ""; } else { var returnClasses = [className], cssClassSuffixes = match.getCssClassSuffixes(); for (var i = 0, len = cssClassSuffixes.length; i < len; i++) { returnClasses.push(className + '-' + cssClassSuffixes[i]); } return returnClasses.join(' '); } }; /** * Processes the `anchorText` by truncating the text according to the * {@link #truncate} config. * * @private * @param {String} anchorText The anchor tag's text (i.e. what will be * displayed). * @return {String} The processed `anchorText`. */ AnchorTagBuilder.prototype.processAnchorText = function (anchorText) { anchorText = this.doTruncate(anchorText); return anchorText; }; /** * Performs the truncation of the `anchorText` based on the {@link #truncate} * option. If the `anchorText` is longer than the length specified by the * {@link #truncate} option, the truncation is performed based on the * `location` property. See {@link #truncate} for details. * * @private * @param {String} anchorText The anchor tag's text (i.e. what will be * displayed). * @return {String} The truncated anchor text. */ AnchorTagBuilder.prototype.doTruncate = function (anchorText) { var truncate = this.truncate; if (!truncate || !truncate.length) return anchorText; var truncateLength = truncate.length, truncateLocation = truncate.location; if (truncateLocation === 'smart') { return truncateSmart(anchorText, truncateLength); } else if (truncateLocation === 'middle') { return truncateMiddle(anchorText, truncateLength); } else { return truncateEnd(anchorText, truncateLength); } }; return AnchorTagBuilder; }()); /** * @abstract * @class Autolinker.match.Match * * Represents a match found in an input string which should be Autolinked. A Match object is what is provided in a * {@link Autolinker#replaceFn replaceFn}, and may be used to query for details about the match. * * For example: * * var input = "..."; // string with URLs, Email Addresses, and Mentions (Twitter, Instagram, Soundcloud) * * var linkedText = Autolinker.link( input, { * replaceFn : function( match ) { * console.log( "href = ", match.getAnchorHref() ); * console.log( "text = ", match.getAnchorText() ); * * switch( match.getType() ) { * case 'url' : * console.log( "url: ", match.getUrl() ); * * case 'email' : * console.log( "email: ", match.getEmail() ); * * case 'mention' : * console.log( "mention: ", match.getMention() ); * } * } * } ); * * See the {@link Autolinker} class for more details on using the {@link Autolinker#replaceFn replaceFn}. */ var Match = /** @class */ (function () { /** * @member Autolinker.match.Match * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function Match(cfg) { /** * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required) * * Reference to the AnchorTagBuilder instance to use to generate an anchor * tag for the Match. */ this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator /** * @cfg {String} matchedText (required) * * The original text that was matched by the {@link Autolinker.matcher.Matcher}. */ this.matchedText = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Number} offset (required) * * The offset of where the match was made in the input string. */ this.offset = 0; // default value just to get the above doc comment in the ES5 output and documentation generator this.tagBuilder = cfg.tagBuilder; this.matchedText = cfg.matchedText; this.offset = cfg.offset; } /** * Returns the original text that was matched. * * @return {String} */ Match.prototype.getMatchedText = function () { return this.matchedText; }; /** * Sets the {@link #offset} of where the match was made in the input string. * * A {@link Autolinker.matcher.Matcher} will be fed only HTML text nodes, * and will therefore set an original offset that is relative to the HTML * text node itself. However, we want this offset to be relative to the full * HTML input string, and thus if using {@link Autolinker#parse} (rather * than calling a {@link Autolinker.matcher.Matcher} directly), then this * offset is corrected after the Matcher itself has done its job. * * @param {Number} offset */ Match.prototype.setOffset = function (offset) { this.offset = offset; }; /** * Returns the offset of where the match was made in the input string. This * is the 0-based index of the match. * * @return {Number} */ Match.prototype.getOffset = function () { return this.offset; }; /** * Returns the CSS class suffix(es) for this match. * * A CSS class suffix is appended to the {@link Autolinker#className} in * the {@link Autolinker.AnchorTagBuilder} when a match is translated into * an anchor tag. * * For example, if {@link Autolinker#className} was configured as 'myLink', * and this method returns `[ 'url' ]`, the final class name of the element * will become: 'myLink myLink-url'. * * The match may provide multiple CSS class suffixes to be appended to the * {@link Autolinker#className} in order to facilitate better styling * options for different match criteria. See {@link Autolinker.match.Mention} * for an example. * * By default, this method returns a single array with the match's * {@link #getType type} name, but may be overridden by subclasses. * * @return {String[]} */ Match.prototype.getCssClassSuffixes = function () { return [this.getType()]; }; /** * Builds and returns an {@link Autolinker.HtmlTag} instance based on the * Match. * * This can be used to easily generate anchor tags from matches, and either * return their HTML string, or modify them before doing so. * * Example Usage: * * var tag = match.buildTag(); * tag.addClass( 'cordova-link' ); * tag.setAttr( 'target', '_system' ); * * tag.toAnchorString(); // Google * * Example Usage in {@link Autolinker#replaceFn}: * * var html = Autolinker.link( "Test google.com", { * replaceFn : function( match ) { * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance * tag.setAttr( 'rel', 'nofollow' ); * * return tag; * } * } ); * * // generated html: * // Test google.com */ Match.prototype.buildTag = function () { return this.tagBuilder.build(this); }; return Match; }()); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. 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 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; /** * @class Autolinker.match.Email * @extends Autolinker.match.Match * * Represents a Email match found in an input string which should be Autolinked. * * See this class's superclass ({@link Autolinker.match.Match}) for more details. */ var EmailMatch = /** @class */ (function (_super) { __extends(EmailMatch, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function EmailMatch(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {String} email (required) * * The email address that was matched. */ _this.email = ''; // default value just to get the above doc comment in the ES5 output and documentation generator _this.email = cfg.email; return _this; } /** * Returns a string name for the type of match that this class represents. * For the case of EmailMatch, returns 'email'. * * @return {String} */ EmailMatch.prototype.getType = function () { return 'email'; }; /** * Returns the email address that was matched. * * @return {String} */ EmailMatch.prototype.getEmail = function () { return this.email; }; /** * Returns the anchor href that should be generated for the match. * * @return {String} */ EmailMatch.prototype.getAnchorHref = function () { return 'mailto:' + this.email; }; /** * Returns the anchor text that should be generated for the match. * * @return {String} */ EmailMatch.prototype.getAnchorText = function () { return this.email; }; return EmailMatch; }(Match)); /** * @class Autolinker.match.Hashtag * @extends Autolinker.match.Match * * Represents a Hashtag match found in an input string which should be * Autolinked. * * See this class's superclass ({@link Autolinker.match.Match}) for more * details. */ var HashtagMatch = /** @class */ (function (_super) { __extends(HashtagMatch, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function HashtagMatch(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {String} serviceName * * The service to point hashtag matches to. See {@link Autolinker#hashtag} * for available values. */ _this.serviceName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} hashtag (required) * * The HashtagMatch that was matched, without the '#'. */ _this.hashtag = ''; // default value just to get the above doc comment in the ES5 output and documentation generator _this.serviceName = cfg.serviceName; _this.hashtag = cfg.hashtag; return _this; } /** * Returns a string name for the type of match that this class represents. * For the case of HashtagMatch, returns 'hashtag'. * * @return {String} */ HashtagMatch.prototype.getType = function () { return 'hashtag'; }; /** * Returns the configured {@link #serviceName} to point the HashtagMatch to. * Ex: 'facebook', 'twitter'. * * @return {String} */ HashtagMatch.prototype.getServiceName = function () { return this.serviceName; }; /** * Returns the matched hashtag, without the '#' character. * * @return {String} */ HashtagMatch.prototype.getHashtag = function () { return this.hashtag; }; /** * Returns the anchor href that should be generated for the match. * * @return {String} */ HashtagMatch.prototype.getAnchorHref = function () { var serviceName = this.serviceName, hashtag = this.hashtag; switch (serviceName) { case 'twitter': return 'https://twitter.com/hashtag/' + hashtag; case 'facebook': return 'https://www.facebook.com/hashtag/' + hashtag; case 'instagram': return 'https://instagram.com/explore/tags/' + hashtag; default: // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case. throw new Error('Unknown service name to point hashtag to: ' + serviceName); } }; /** * Returns the anchor text that should be generated for the match. * * @return {String} */ HashtagMatch.prototype.getAnchorText = function () { return '#' + this.hashtag; }; return HashtagMatch; }(Match)); /** * @class Autolinker.match.Mention * @extends Autolinker.match.Match * * Represents a Mention match found in an input string which should be Autolinked. * * See this class's superclass ({@link Autolinker.match.Match}) for more details. */ var MentionMatch = /** @class */ (function (_super) { __extends(MentionMatch, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function MentionMatch(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {String} serviceName * * The service to point mention matches to. See {@link Autolinker#mention} * for available values. */ _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {String} mention (required) * * The Mention that was matched, without the '@' character. */ _this.mention = ''; // default value just to get the above doc comment in the ES5 output and documentation generator _this.mention = cfg.mention; _this.serviceName = cfg.serviceName; return _this; } /** * Returns a string name for the type of match that this class represents. * For the case of MentionMatch, returns 'mention'. * * @return {String} */ MentionMatch.prototype.getType = function () { return 'mention'; }; /** * Returns the mention, without the '@' character. * * @return {String} */ MentionMatch.prototype.getMention = function () { return this.mention; }; /** * Returns the configured {@link #serviceName} to point the mention to. * Ex: 'instagram', 'twitter', 'soundcloud'. * * @return {String} */ MentionMatch.prototype.getServiceName = function () { return this.serviceName; }; /** * Returns the anchor href that should be generated for the match. * * @return {String} */ MentionMatch.prototype.getAnchorHref = function () { switch (this.serviceName) { case 'twitter': return 'https://twitter.com/' + this.mention; case 'instagram': return 'https://instagram.com/' + this.mention; case 'soundcloud': return 'https://soundcloud.com/' + this.mention; default: // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case. throw new Error('Unknown service name to point mention to: ' + this.serviceName); } }; /** * Returns the anchor text that should be generated for the match. * * @return {String} */ MentionMatch.prototype.getAnchorText = function () { return '@' + this.mention; }; /** * Returns the CSS class suffixes that should be used on a tag built with * the match. See {@link Autolinker.match.Match#getCssClassSuffixes} for * details. * * @return {String[]} */ MentionMatch.prototype.getCssClassSuffixes = function () { var cssClassSuffixes = _super.prototype.getCssClassSuffixes.call(this), serviceName = this.getServiceName(); if (serviceName) { cssClassSuffixes.push(serviceName); } return cssClassSuffixes; }; return MentionMatch; }(Match)); /** * @class Autolinker.match.Phone * @extends Autolinker.match.Match * * Represents a Phone number match found in an input string which should be * Autolinked. * * See this class's superclass ({@link Autolinker.match.Match}) for more * details. */ var PhoneMatch = /** @class */ (function (_super) { __extends(PhoneMatch, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function PhoneMatch(cfg) { var _this = _super.call(this, cfg) || this; /** * @protected * @property {String} number (required) * * The phone number that was matched, without any delimiter characters. * * Note: This is a string to allow for prefixed 0's. */ _this.number = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @protected * @property {Boolean} plusSign (required) * * `true` if the matched phone number started with a '+' sign. We'll include * it in the `tel:` URL if so, as this is needed for international numbers. * * Ex: '+1 (123) 456 7879' */ _this.plusSign = false; // default value just to get the above doc comment in the ES5 output and documentation generator _this.number = cfg.number; _this.plusSign = cfg.plusSign; return _this; } /** * Returns a string name for the type of match that this class represents. * For the case of PhoneMatch, returns 'phone'. * * @return {String} */ PhoneMatch.prototype.getType = function () { return 'phone'; }; /** * Returns the phone number that was matched as a string, without any * delimiter characters. * * Note: This is a string to allow for prefixed 0's. * * @return {String} */ PhoneMatch.prototype.getPhoneNumber = function () { return this.number; }; /** * Alias of {@link #getPhoneNumber}, returns the phone number that was * matched as a string, without any delimiter characters. * * Note: This is a string to allow for prefixed 0's. * * @return {String} */ PhoneMatch.prototype.getNumber = function () { return this.getPhoneNumber(); }; /** * Returns the anchor href that should be generated for the match. * * @return {String} */ PhoneMatch.prototype.getAnchorHref = function () { return 'tel:' + (this.plusSign ? '+' : '') + this.number; }; /** * Returns the anchor text that should be generated for the match. * * @return {String} */ PhoneMatch.prototype.getAnchorText = function () { return this.matchedText; }; return PhoneMatch; }(Match)); /** * @class Autolinker.match.Url * @extends Autolinker.match.Match * * Represents a Url match found in an input string which should be Autolinked. * * See this class's superclass ({@link Autolinker.match.Match}) for more details. */ var UrlMatch = /** @class */ (function (_super) { __extends(UrlMatch, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match * instance, specified in an Object (map). */ function UrlMatch(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {String} url (required) * * The url that was matched. */ _this.url = ''; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {"scheme"/"www"/"tld"} urlMatchType (required) * * The type of URL match that this class represents. This helps to determine * if the match was made in the original text with a prefixed scheme (ex: * 'http://www.google.com'), a prefixed 'www' (ex: 'www.google.com'), or * was matched by a known top-level domain (ex: 'google.com'). */ _this.urlMatchType = 'scheme'; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} protocolUrlMatch (required) * * `true` if the URL is a match which already has a protocol (i.e. * 'http://'), `false` if the match was from a 'www' or known TLD match. */ _this.protocolUrlMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} protocolRelativeMatch (required) * * `true` if the URL is a protocol-relative match. A protocol-relative match * is a URL that starts with '//', and will be either http:// or https:// * based on the protocol that the site is loaded under. */ _this.protocolRelativeMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Object} stripPrefix (required) * * The Object form of {@link Autolinker#cfg-stripPrefix}. */ _this.stripPrefix = { scheme: true, www: true }; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} stripTrailingSlash (required) * @inheritdoc Autolinker#cfg-stripTrailingSlash */ _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} decodePercentEncoding (required) * @inheritdoc Autolinker#cfg-decodePercentEncoding */ _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @private * @property {RegExp} schemePrefixRegex * * A regular expression used to remove the 'http://' or 'https://' from * URLs. */ _this.schemePrefixRegex = /^(https?:\/\/)?/i; /** * @private * @property {RegExp} wwwPrefixRegex * * A regular expression used to remove the 'www.' from URLs. */ _this.wwwPrefixRegex = /^(https?:\/\/)?(www\.)?/i; /** * @private * @property {RegExp} protocolRelativeRegex * * The regular expression used to remove the protocol-relative '//' from the {@link #url} string, for purposes * of {@link #getAnchorText}. A protocol-relative URL is, for example, "//yahoo.com" */ _this.protocolRelativeRegex = /^\/\//; /** * @private * @property {Boolean} protocolPrepended * * Will be set to `true` if the 'http://' protocol has been prepended to the {@link #url} (because the * {@link #url} did not have a protocol) */ _this.protocolPrepended = false; _this.urlMatchType = cfg.urlMatchType; _this.url = cfg.url; _this.protocolUrlMatch = cfg.protocolUrlMatch; _this.protocolRelativeMatch = cfg.protocolRelativeMatch; _this.stripPrefix = cfg.stripPrefix; _this.stripTrailingSlash = cfg.stripTrailingSlash; _this.decodePercentEncoding = cfg.decodePercentEncoding; return _this; } /** * Returns a string name for the type of match that this class represents. * For the case of UrlMatch, returns 'url'. * * @return {String} */ UrlMatch.prototype.getType = function () { return 'url'; }; /** * Returns a string name for the type of URL match that this class * represents. * * This helps to determine if the match was made in the original text with a * prefixed scheme (ex: 'http://www.google.com'), a prefixed 'www' (ex: * 'www.google.com'), or was matched by a known top-level domain (ex: * 'google.com'). * * @return {"scheme"/"www"/"tld"} */ UrlMatch.prototype.getUrlMatchType = function () { return this.urlMatchType; }; /** * Returns the url that was matched, assuming the protocol to be 'http://' if the original * match was missing a protocol. * * @return {String} */ UrlMatch.prototype.getUrl = function () { var url = this.url; // if the url string doesn't begin with a protocol, assume 'http://' if (!this.protocolRelativeMatch && !this.protocolUrlMatch && !this.protocolPrepended) { url = this.url = 'http://' + url; this.protocolPrepended = true; } return url; }; /** * Returns the anchor href that should be generated for the match. * * @return {String} */ UrlMatch.prototype.getAnchorHref = function () { var url = this.getUrl(); return url.replace(/&/g, '&'); // any &'s in the URL should be converted back to '&' if they were displayed as & in the source html }; /** * Returns the anchor text that should be generated for the match. * * @return {String} */ UrlMatch.prototype.getAnchorText = function () { var anchorText = this.getMatchedText(); if (this.protocolRelativeMatch) { // Strip off any protocol-relative '//' from the anchor text anchorText = this.stripProtocolRelativePrefix(anchorText); } if (this.stripPrefix.scheme) { anchorText = this.stripSchemePrefix(anchorText); } if (this.stripPrefix.www) { anchorText = this.stripWwwPrefix(anchorText); } if (this.stripTrailingSlash) { anchorText = this.removeTrailingSlash(anchorText); // remove trailing slash, if there is one } if (this.decodePercentEncoding) { anchorText = this.removePercentEncoding(anchorText); } return anchorText; }; // --------------------------------------- // Utility Functionality /** * Strips the scheme prefix (such as "http://" or "https://") from the given * `url`. * * @private * @param {String} url The text of the anchor that is being generated, for * which to strip off the url scheme. * @return {String} The `url`, with the scheme stripped. */ UrlMatch.prototype.stripSchemePrefix = function (url) { return url.replace(this.schemePrefixRegex, ''); }; /** * Strips the 'www' prefix from the given `url`. * * @private * @param {String} url The text of the anchor that is being generated, for * which to strip off the 'www' if it exists. * @return {String} The `url`, with the 'www' stripped. */ UrlMatch.prototype.stripWwwPrefix = function (url) { return url.replace(this.wwwPrefixRegex, '$1'); // leave any scheme ($1), it one exists }; /** * Strips any protocol-relative '//' from the anchor text. * * @private * @param {String} text The text of the anchor that is being generated, for which to strip off the * protocol-relative prefix (such as stripping off "//") * @return {String} The `anchorText`, with the protocol-relative prefix stripped. */ UrlMatch.prototype.stripProtocolRelativePrefix = function (text) { return text.replace(this.protocolRelativeRegex, ''); }; /** * Removes any trailing slash from the given `anchorText`, in preparation for the text to be displayed. * * @private * @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing * slash ('/') that may exist. * @return {String} The `anchorText`, with the trailing slash removed. */ UrlMatch.prototype.removeTrailingSlash = function (anchorText) { if (anchorText.charAt(anchorText.length - 1) === '/') { anchorText = anchorText.slice(0, -1); } return anchorText; }; /** * Decodes percent-encoded characters from the given `anchorText`, in * preparation for the text to be displayed. * * @private * @param {String} anchorText The text of the anchor that is being * generated, for which to decode any percent-encoded characters. * @return {String} The `anchorText`, with the percent-encoded characters * decoded. */ UrlMatch.prototype.removePercentEncoding = function (anchorText) { // First, convert a few of the known % encodings to the corresponding // HTML entities that could accidentally be interpretted as special // HTML characters var preProcessedEntityAnchorText = anchorText .replace(/%22/gi, '"') // " char .replace(/%26/gi, '&') // & char .replace(/%27/gi, ''') // ' char .replace(/%3C/gi, '<') // < char .replace(/%3E/gi, '>'); // > char try { // Now attempt to decode the rest of the anchor text return decodeURIComponent(preProcessedEntityAnchorText); } catch (e) { // Invalid % escape sequence in the anchor text return preProcessedEntityAnchorText; } }; return UrlMatch; }(Match)); /** * @abstract * @class Autolinker.matcher.Matcher * * An abstract class and interface for individual matchers to find matches in * an input string with linkified versions of them. * * Note that Matchers do not take HTML into account - they must be fed the text * nodes of any HTML string, which is handled by {@link Autolinker#parse}. */ var Matcher = /** @class */ (function () { /** * @method constructor * @param {Object} cfg The configuration properties for the Matcher * instance, specified in an Object (map). */ function Matcher(cfg) { /** * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required) * * Reference to the AnchorTagBuilder instance to use to generate HTML tags * for {@link Autolinker.match.Match Matches}. */ this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator this.tagBuilder = cfg.tagBuilder; } return Matcher; }()); /* * This file builds and stores a library of the common regular expressions used * by the Autolinker utility. * * Other regular expressions may exist ad-hoc, but these are generally the * regular expressions that are shared between source files. */ /** * Regular expression to match upper and lowercase ASCII letters */ var letterRe = /[A-Za-z]/; /** * Regular expression to match ASCII digits */ var digitRe = /[0-9]/; /** * Regular expression to match whitespace */ var whitespaceRe = /\s/; /** * Regular expression to match quote characters */ var quoteRe = /['"]/; /** * Regular expression to match the range of ASCII control characters (0-31), and * the backspace char (127) */ var controlCharsRe = /[\x00-\x1F\x7F]/; /** * The string form of a regular expression that would match all of the * alphabetic ("letter") chars in the unicode character set when placed in a * RegExp character class (`[]`). This includes all international alphabetic * characters. * * These would be the characters matched by unicode regex engines `\p{L}` * escape ("all letters"). * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Letter' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ var alphaCharsStr = /A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC/ .source; // see note in above variable description /** * The string form of a regular expression that would match all emoji characters * Source: https://www.regextester.com/106421 */ var emojiStr = /\u00a9\u00ae\u2000-\u3300\ud83c\ud000-\udfff\ud83d\ud000-\udfff\ud83e\ud000-\udfff/ .source; /** * The string form of a regular expression that would match all of the * combining mark characters in the unicode character set when placed in a * RegExp character class (`[]`). * * These would be the characters matched by unicode regex engines `\p{M}` * escape ("all marks"). * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Mark' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ var marksStr = /\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F/ .source; // see note in above variable description /** * The string form of a regular expression that would match all of the * alphabetic ("letter") chars, emoji, and combining marks in the unicode character set * when placed in a RegExp character class (`[]`). This includes all * international alphabetic characters. * * These would be the characters matched by unicode regex engines `\p{L}\p{M}` * escapes and emoji characters. */ var alphaCharsAndMarksStr = alphaCharsStr + emojiStr + marksStr; /** * The string form of a regular expression that would match all of the * decimal number chars in the unicode character set when placed in a RegExp * character class (`[]`). * * These would be the characters matched by unicode regex engines `\p{Nd}` * escape ("all decimal numbers") * * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan) * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Decimal_Number' * regex's bmp * * VERY IMPORTANT: This set of characters is defined inside of a Regular * Expression literal rather than a string literal to prevent UglifyJS from * compressing the unicode escape sequences into their actual unicode * characters. If Uglify compresses these into the unicode characters * themselves, this results in the error "Range out of order in character * class" when these characters are used inside of a Regular Expression * character class (`[]`). See usages of this const. Alternatively, we can set * the UglifyJS option `ascii_only` to true for the build, but that doesn't * help others who are pulling in Autolinker into their own build and running * UglifyJS themselves. */ var decimalNumbersStr = /0-9\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19/ .source; // see note in above variable description /** * The string form of a regular expression that would match all of the * letters and decimal number chars in the unicode character set when placed in * a RegExp character class (`[]`). * * These would be the characters matched by unicode regex engines * `[\p{L}\p{Nd}]` escape ("all letters and decimal numbers") */ var alphaNumericCharsStr = alphaCharsAndMarksStr + decimalNumbersStr; /** * The string form of a regular expression that would match all of the * letters, combining marks, and decimal number chars in the unicode character * set when placed in a RegExp character class (`[]`). * * These would be the characters matched by unicode regex engines * `[\p{L}\p{M}\p{Nd}]` escape ("all letters, combining marks, and decimal * numbers") */ var alphaNumericAndMarksCharsStr = alphaCharsAndMarksStr + decimalNumbersStr; // Simplified IP regular expression var ipStr = '(?:[' + decimalNumbersStr + ']{1,3}\\.){3}[' + decimalNumbersStr + ']{1,3}'; // Protected domain label which do not allow "-" character on the beginning and the end of a single label var domainLabelStr = '[' + alphaNumericAndMarksCharsStr + '](?:[' + alphaNumericAndMarksCharsStr + '\\-]{0,61}[' + alphaNumericAndMarksCharsStr + '])?'; var getDomainLabelStr = function (group) { return '(?=(' + domainLabelStr + '))\\' + group; }; /** * A function to match domain names of a URL or email address. * Ex: 'google', 'yahoo', 'some-other-company', etc. */ var getDomainNameStr = function (group) { return '(?:' + getDomainLabelStr(group) + '(?:\\.' + getDomainLabelStr(group + 1) + '){0,126}|' + ipStr + ')'; }; /** * A regular expression that is simply the character class of the characters * that may be used in a domain name, minus the '-' or '.' */ var domainNameCharRegex = new RegExp("[" + alphaNumericAndMarksCharsStr + "]"); // NOTE: THIS IS A GENERATED FILE // To update with the latest TLD list, run `npm run update-tld-regex` or `yarn update-tld-regex` (depending on which you have installed) var tldRegex = /(?:xn--vermgensberatung-pwb|xn--vermgensberater-ctb|xn--clchc0ea0b2g2a9gcd|xn--w4r85el8fhu5dnra|northwesternmutual|travelersinsurance|vermögensberatung|xn--3oq18vl8pn36a|xn--5su34j936bgsg|xn--bck1b9a5dre4c|xn--mgbai9azgqp6j|xn--mgberp4a5d4ar|xn--xkc2dl3a5ee0h|vermögensberater|xn--fzys8d69uvgm|xn--mgba7c0bbn0a|xn--xkc2al3hye2a|americanexpress|kerryproperties|sandvikcoromant|xn--i1b6b1a6a2e|xn--kcrx77d1x4a|xn--lgbbat1ad8j|xn--mgba3a4f16a|xn--mgbaakc7dvf|xn--mgbc0a9azcg|xn--nqv7fs00ema|afamilycompany|americanfamily|bananarepublic|cancerresearch|cookingchannel|kerrylogistics|weatherchannel|xn--54b7fta0cc|xn--6qq986b3xl|xn--80aqecdr1a|xn--b4w605ferd|xn--fiq228c5hs|xn--h2breg3eve|xn--jlq61u9w7b|xn--mgba3a3ejt|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgbb9fbpob|xn--mgbbh1a71e|xn--mgbca7dzdo|xn--mgbi4ecexp|xn--mgbx4cd0ab|xn--rvc1e0am3e|international|lifeinsurance|spreadbetting|travelchannel|wolterskluwer|xn--eckvdtc9d|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--h2brj9c8c|xn--tiq49xqyj|xn--yfro4i67o|xn--ygbi2ammx|construction|lplfinancial|scholarships|versicherung|xn--3e0b707e|xn--45br5cyl|xn--80adxhks|xn--80asehdb|xn--8y0a063a|xn--gckr3f0f|xn--mgb9awbf|xn--mgbab2bd|xn--mgbgu82a|xn--mgbpl2fh|xn--mgbt3dhd|xn--mk1bu44c|xn--ngbc5azd|xn--ngbe9e0a|xn--ogbpf8fl|xn--qcka1pmc|accountants|barclaycard|blackfriday|blockbuster|bridgestone|calvinklein|contractors|creditunion|engineering|enterprises|foodnetwork|investments|kerryhotels|lamborghini|motorcycles|olayangroup|photography|playstation|productions|progressive|redumbrella|rightathome|williamhill|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--3bst00m|xn--3ds443g|xn--3hcrj9c|xn--42c2d9a|xn--45brj9c|xn--55qw42g|xn--6frz82g|xn--80ao21a|xn--9krt00a|xn--cck2b3b|xn--czr694b|xn--d1acj3b|xn--efvy88h|xn--estv75g|xn--fct429k|xn--fjq720a|xn--flw351e|xn--g2xx48c|xn--gecrj9c|xn--gk3at1e|xn--h2brj9c|xn--hxt814e|xn--imr513n|xn--j6w193g|xn--jvr189m|xn--kprw13d|xn--kpry57d|xn--kpu716f|xn--mgbbh1a|xn--mgbtx2b|xn--mix891f|xn--nyqy26a|xn--otu796d|xn--pbt977c|xn--pgbs0dh|xn--q9jyb4c|xn--rhqv96g|xn--rovu88b|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--vuq861b|xn--w4rs40l|xn--xhq521b|xn--zfr164b|சிங்கப்பூர்|accountant|apartments|associates|basketball|bnpparibas|boehringer|capitalone|consulting|creditcard|cuisinella|eurovision|extraspace|foundation|healthcare|immobilien|industries|management|mitsubishi|nationwide|newholland|nextdirect|onyourside|properties|protection|prudential|realestate|republican|restaurant|schaeffler|swiftcover|tatamotors|technology|telefonica|university|vistaprint|vlaanderen|volkswagen|xn--30rr7y|xn--3pxu8k|xn--45q11c|xn--4gbrim|xn--55qx5d|xn--5tzm5g|xn--80aswg|xn--90a3ac|xn--9dbq2a|xn--9et52u|xn--c2br7g|xn--cg4bki|xn--czrs0t|xn--czru2d|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--io0a7i|xn--kput3i|xn--mxtq1m|xn--o3cw4h|xn--pssy2u|xn--unup4y|xn--wgbh1c|xn--wgbl6a|xn--y9a3aq|accenture|alfaromeo|allfinanz|amsterdam|analytics|aquarelle|barcelona|bloomberg|christmas|community|directory|education|equipment|fairwinds|financial|firestone|fresenius|frontdoor|fujixerox|furniture|goldpoint|hisamitsu|homedepot|homegoods|homesense|honeywell|institute|insurance|kuokgroup|ladbrokes|lancaster|landrover|lifestyle|marketing|marshalls|melbourne|microsoft|panasonic|passagens|pramerica|richardli|scjohnson|shangrila|solutions|statebank|statefarm|stockholm|travelers|vacations|xn--90ais|xn--c1avg|xn--d1alf|xn--e1a4c|xn--fhbei|xn--j1aef|xn--j1amh|xn--l1acc|xn--ngbrx|xn--nqv7f|xn--p1acf|xn--tckwe|xn--vhquv|yodobashi|abudhabi|airforce|allstate|attorney|barclays|barefoot|bargains|baseball|boutique|bradesco|broadway|brussels|budapest|builders|business|capetown|catering|catholic|chrysler|cipriani|cityeats|cleaning|clinique|clothing|commbank|computer|delivery|deloitte|democrat|diamonds|discount|discover|download|engineer|ericsson|esurance|etisalat|everbank|exchange|feedback|fidelity|firmdale|football|frontier|goodyear|grainger|graphics|guardian|hdfcbank|helsinki|holdings|hospital|infiniti|ipiranga|istanbul|jpmorgan|lighting|lundbeck|marriott|maserati|mckinsey|memorial|merckmsd|mortgage|movistar|observer|partners|pharmacy|pictures|plumbing|property|redstone|reliance|saarland|samsclub|security|services|shopping|showtime|softbank|software|stcgroup|supplies|symantec|training|uconnect|vanguard|ventures|verisign|woodside|xn--90ae|xn--node|xn--p1ai|xn--qxam|yokohama|السعودية|abogado|academy|agakhan|alibaba|android|athleta|auction|audible|auspost|avianca|banamex|bauhaus|bentley|bestbuy|booking|brother|bugatti|capital|caravan|careers|cartier|channel|charity|chintai|citadel|clubmed|college|cologne|comcast|company|compare|contact|cooking|corsica|country|coupons|courses|cricket|cruises|dentist|digital|domains|exposed|express|farmers|fashion|ferrari|ferrero|finance|fishing|fitness|flights|florist|flowers|forsale|frogans|fujitsu|gallery|genting|godaddy|grocery|guitars|hamburg|hangout|hitachi|holiday|hosting|hoteles|hotmail|hyundai|iselect|ismaili|jewelry|juniper|kitchen|komatsu|lacaixa|lancome|lanxess|lasalle|latrobe|leclerc|liaison|limited|lincoln|markets|metlife|monster|netbank|netflix|network|neustar|okinawa|oldnavy|organic|origins|philips|pioneer|politie|realtor|recipes|rentals|reviews|rexroth|samsung|sandvik|schmidt|schwarz|science|shiksha|shriram|singles|staples|starhub|storage|support|surgery|systems|temasek|theater|theatre|tickets|tiffany|toshiba|trading|walmart|wanggou|watches|weather|website|wedding|whoswho|windows|winners|xfinity|yamaxun|youtube|zuerich|католик|اتصالات|الجزائر|العليان|پاکستان|كاثوليك|موبايلي|இந்தியா|abarth|abbott|abbvie|active|africa|agency|airbus|airtel|alipay|alsace|alstom|anquan|aramco|author|bayern|beauty|berlin|bharti|blanco|bostik|boston|broker|camera|career|caseih|casino|center|chanel|chrome|church|circle|claims|clinic|coffee|comsec|condos|coupon|credit|cruise|dating|datsun|dealer|degree|dental|design|direct|doctor|dunlop|dupont|durban|emerck|energy|estate|events|expert|family|flickr|futbol|gallup|garden|george|giving|global|google|gratis|health|hermes|hiphop|hockey|hotels|hughes|imamat|insure|intuit|jaguar|joburg|juegos|kaufen|kinder|kindle|kosher|lancia|latino|lawyer|lefrak|living|locker|london|luxury|madrid|maison|makeup|market|mattel|mobile|mobily|monash|mormon|moscow|museum|mutual|nagoya|natura|nissan|nissay|norton|nowruz|office|olayan|online|oracle|orange|otsuka|pfizer|photos|physio|piaget|pictet|quebec|racing|realty|reisen|repair|report|review|rocher|rogers|ryukyu|safety|sakura|sanofi|school|schule|search|secure|select|shouji|soccer|social|stream|studio|supply|suzuki|swatch|sydney|taipei|taobao|target|tattoo|tennis|tienda|tjmaxx|tkmaxx|toyota|travel|unicom|viajes|viking|villas|virgin|vision|voting|voyage|vuelos|walter|warman|webcam|xihuan|yachts|yandex|zappos|москва|онлайн|ابوظبي|ارامكو|الاردن|المغرب|امارات|فلسطين|مليسيا|भारतम्|இலங்கை|ファッション|actor|adult|aetna|amfam|amica|apple|archi|audio|autos|azure|baidu|beats|bible|bingo|black|boats|bosch|build|canon|cards|chase|cheap|cisco|citic|click|cloud|coach|codes|crown|cymru|dabur|dance|deals|delta|dodge|drive|dubai|earth|edeka|email|epost|epson|faith|fedex|final|forex|forum|gallo|games|gifts|gives|glade|glass|globo|gmail|green|gripe|group|gucci|guide|homes|honda|horse|house|hyatt|ikano|intel|irish|iveco|jetzt|koeln|kyoto|lamer|lease|legal|lexus|lilly|linde|lipsy|lixil|loans|locus|lotte|lotto|lupin|macys|mango|media|miami|money|mopar|movie|nadex|nexus|nikon|ninja|nokia|nowtv|omega|osaka|paris|parts|party|phone|photo|pizza|place|poker|praxi|press|prime|promo|quest|radio|rehab|reise|ricoh|rocks|rodeo|rugby|salon|sener|seven|sharp|shell|shoes|skype|sling|smart|smile|solar|space|sport|stada|store|study|style|sucks|swiss|tatar|tires|tirol|tmall|today|tokyo|tools|toray|total|tours|trade|trust|tunes|tushu|ubank|vegas|video|vodka|volvo|wales|watch|weber|weibo|works|world|xerox|yahoo|zippo|ایران|بازار|بھارت|سودان|سورية|همراه|भारोत|संगठन|বাংলা|భారత్|ഭാരതം|嘉里大酒店|aarp|able|adac|aero|aigo|akdn|ally|amex|arab|army|arpa|arte|asda|asia|audi|auto|baby|band|bank|bbva|beer|best|bike|bing|blog|blue|bofa|bond|book|buzz|cafe|call|camp|care|cars|casa|case|cash|cbre|cern|chat|citi|city|club|cool|coop|cyou|data|date|dclk|deal|dell|desi|diet|dish|docs|doha|duck|duns|dvag|erni|fage|fail|fans|farm|fast|fiat|fido|film|fire|fish|flir|food|ford|free|fund|game|gbiz|gent|ggee|gift|gmbh|gold|golf|goog|guge|guru|hair|haus|hdfc|help|here|hgtv|host|hsbc|icbc|ieee|imdb|immo|info|itau|java|jeep|jobs|jprs|kddi|kiwi|kpmg|kred|land|lego|lgbt|lidl|life|like|limo|link|live|loan|loft|love|ltda|luxe|maif|meet|meme|menu|mini|mint|mobi|moda|moto|name|navy|news|next|nico|nike|ollo|open|page|pars|pccw|pics|ping|pink|play|plus|pohl|porn|post|prod|prof|qpon|raid|read|reit|rent|rest|rich|rmit|room|rsvp|ruhr|safe|sale|sarl|save|saxo|scor|scot|seat|seek|sexy|shaw|shia|shop|show|silk|sina|site|skin|sncf|sohu|song|sony|spot|star|surf|talk|taxi|team|tech|teva|tiaa|tips|town|toys|tube|vana|visa|viva|vivo|vote|voto|wang|weir|wien|wiki|wine|work|xbox|yoga|zara|zero|zone|дети|сайт|بارت|بيتك|ڀارت|تونس|شبكة|عراق|عمان|موقع|भारत|ভারত|ভাৰত|ਭਾਰਤ|ભારત|ଭାରତ|ಭಾರತ|ලංකා|グーグル|クラウド|ポイント|大众汽车|组织机构|電訊盈科|香格里拉|aaa|abb|abc|aco|ads|aeg|afl|aig|anz|aol|app|art|aws|axa|bar|bbc|bbt|bcg|bcn|bet|bid|bio|biz|bms|bmw|bnl|bom|boo|bot|box|buy|bzh|cab|cal|cam|car|cat|cba|cbn|cbs|ceb|ceo|cfa|cfd|com|crs|csc|dad|day|dds|dev|dhl|diy|dnp|dog|dot|dtv|dvr|eat|eco|edu|esq|eus|fan|fit|fly|foo|fox|frl|ftr|fun|fyi|gal|gap|gdn|gea|gle|gmo|gmx|goo|gop|got|gov|hbo|hiv|hkt|hot|how|ibm|ice|icu|ifm|inc|ing|ink|int|ist|itv|jcb|jcp|jio|jll|jmp|jnj|jot|joy|kfh|kia|kim|kpn|krd|lat|law|lds|llc|lol|lpl|ltd|man|map|mba|med|men|mil|mit|mlb|mls|mma|moe|moi|mom|mov|msd|mtn|mtr|nab|nba|nec|net|new|nfl|ngo|nhk|now|nra|nrw|ntt|nyc|obi|off|one|ong|onl|ooo|org|ott|ovh|pay|pet|phd|pid|pin|pnc|pro|pru|pub|pwc|qvc|red|ren|ril|rio|rip|run|rwe|sap|sas|sbi|sbs|sca|scb|ses|sew|sex|sfr|ski|sky|soy|srl|srt|stc|tab|tax|tci|tdk|tel|thd|tjx|top|trv|tui|tvs|ubs|uno|uol|ups|vet|vig|vin|vip|wed|win|wme|wow|wtc|wtf|xin|xxx|xyz|you|yun|zip|бел|ком|қаз|мкд|мон|орг|рус|срб|укр|հայ|קום|عرب|قطر|كوم|مصر|कॉम|नेट|คอม|ไทย|ストア|セール|みんな|中文网|天主教|我爱你|新加坡|淡马锡|诺基亚|飞利浦|ac|ad|ae|af|ag|ai|al|am|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw|ελ|бг|ею|рф|გე|닷넷|닷컴|삼성|한국|コム|世界|中信|中国|中國|企业|佛山|信息|健康|八卦|公司|公益|台湾|台灣|商城|商店|商标|嘉里|在线|大拿|娱乐|家電|工行|广东|微博|慈善|手机|手表|招聘|政务|政府|新闻|时尚|書籍|机构|游戏|澳門|点看|珠宝|移动|网址|网店|网站|网络|联通|谷歌|购物|通販|集团|食品|餐厅|香港)/; // For debugging: search for other "For debugging" lines // import CliTable from 'cli-table'; /** * @class Autolinker.matcher.Email * @extends Autolinker.matcher.Matcher * * Matcher to find email matches in an input string. * * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details. */ var EmailMatcher = /** @class */ (function (_super) { __extends(EmailMatcher, _super); function EmailMatcher() { var _this = _super !== null && _super.apply(this, arguments) || this; /** * Valid characters that can be used in the "local" part of an email address, * i.e. the "name" part of "name@site.com" */ _this.localPartCharRegex = new RegExp("[" + alphaNumericAndMarksCharsStr + "!#$%&'*+/=?^_`{|}~-]"); /** * Stricter TLD regex which adds a beginning and end check to ensure * the string is a valid TLD */ _this.strictTldRegex = new RegExp("^" + tldRegex.source + "$"); return _this; } /** * @inheritdoc */ EmailMatcher.prototype.parseMatches = function (text) { var tagBuilder = this.tagBuilder, localPartCharRegex = this.localPartCharRegex, strictTldRegex = this.strictTldRegex, matches = [], len = text.length, noCurrentEmailMatch = new CurrentEmailMatch(); // for matching a 'mailto:' prefix var mailtoTransitions = { 'm': 'a', 'a': 'i', 'i': 'l', 'l': 't', 't': 'o', 'o': ':', }; var charIdx = 0, state = 0 /* NonEmailMatch */, currentEmailMatch = noCurrentEmailMatch; // For debugging: search for other "For debugging" lines // const table = new CliTable( { // head: [ 'charIdx', 'char', 'state', 'charIdx', 'currentEmailAddress.idx', 'hasDomainDot' ] // } ); while (charIdx < len) { var char = text.charAt(charIdx); // For debugging: search for other "For debugging" lines // table.push( // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ] // ); switch (state) { case 0 /* NonEmailMatch */: stateNonEmailAddress(char); break; case 1 /* Mailto */: stateMailTo(text.charAt(charIdx - 1), char); break; case 2 /* LocalPart */: stateLocalPart(char); break; case 3 /* LocalPartDot */: stateLocalPartDot(char); break; case 4 /* AtSign */: stateAtSign(char); break; case 5 /* DomainChar */: stateDomainChar(char); break; case 6 /* DomainHyphen */: stateDomainHyphen(char); break; case 7 /* DomainDot */: stateDomainDot(char); break; default: throwUnhandledCaseError(state); } // For debugging: search for other "For debugging" lines // table.push( // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ] // ); charIdx++; } // Capture any valid match at the end of the string captureMatchIfValidAndReset(); // For debugging: search for other "For debugging" lines //console.log( '\n' + table.toString() ); return matches; // Handles the state when we're not in an email address function stateNonEmailAddress(char) { if (char === 'm') { beginEmailMatch(1 /* Mailto */); } else if (localPartCharRegex.test(char)) { beginEmailMatch(); } } // Handles if we're reading a 'mailto:' prefix on the string function stateMailTo(prevChar, char) { if (prevChar === ':') { // We've reached the end of the 'mailto:' prefix if (localPartCharRegex.test(char)) { state = 2 /* LocalPart */; currentEmailMatch = new CurrentEmailMatch(__assign({}, currentEmailMatch, { hasMailtoPrefix: true })); } else { // we've matched 'mailto:' but didn't get anything meaningful // immediately afterwards (for example, we encountered a // space character, or an '@' character which formed 'mailto:@' resetToNonEmailMatchState(); } } else if (mailtoTransitions[prevChar] === char) ; else if (localPartCharRegex.test(char)) { // We we're reading a prefix of 'mailto:', but encountered a // different character that didn't continue the prefix state = 2 /* LocalPart */; } else if (char === '.') { // We we're reading a prefix of 'mailto:', but encountered a // dot character state = 3 /* LocalPartDot */; } else if (char === '@') { // We we're reading a prefix of 'mailto:', but encountered a // an @ character state = 4 /* AtSign */; } else { // not an email address character, return to "NonEmailAddress" state resetToNonEmailMatchState(); } } // Handles the state when we're currently in the "local part" of an // email address (as opposed to the "domain part") function stateLocalPart(char) { if (char === '.') { state = 3 /* LocalPartDot */; } else if (char === '@') { state = 4 /* AtSign */; } else if (localPartCharRegex.test(char)) ; else { // not an email address character, return to "NonEmailAddress" state resetToNonEmailMatchState(); } } // Handles the state where we've read function stateLocalPartDot(char) { if (char === '.') { // We read a second '.' in a row, not a valid email address // local part resetToNonEmailMatchState(); } else if (char === '@') { // We read the '@' character immediately after a dot ('.'), not // an email address resetToNonEmailMatchState(); } else if (localPartCharRegex.test(char)) { state = 2 /* LocalPart */; } else { // Anything else, not an email address resetToNonEmailMatchState(); } } function stateAtSign(char) { if (domainNameCharRegex.test(char)) { state = 5 /* DomainChar */; } else { // Anything else, not an email address resetToNonEmailMatchState(); } } function stateDomainChar(char) { if (char === '.') { state = 7 /* DomainDot */; } else if (char === '-') { state = 6 /* DomainHyphen */; } else if (domainNameCharRegex.test(char)) ; else { // Anything else, we potentially matched if the criteria has // been met captureMatchIfValidAndReset(); } } function stateDomainHyphen(char) { if (char === '-' || char === '.') { // Not valid to have two hyphens ("--") or hypen+dot ("-.") captureMatchIfValidAndReset(); } else if (domainNameCharRegex.test(char)) { state = 5 /* DomainChar */; } else { // Anything else captureMatchIfValidAndReset(); } } function stateDomainDot(char) { if (char === '.' || char === '-') { // not valid to have two dots ("..") or dot+hypen (".-") captureMatchIfValidAndReset(); } else if (domainNameCharRegex.test(char)) { state = 5 /* DomainChar */; // After having read a '.' and then a valid domain character, // we now know that the domain part of the email is valid, and // we have found at least a partial EmailMatch (however, the // email address may have additional characters from this point) currentEmailMatch = new CurrentEmailMatch(__assign({}, currentEmailMatch, { hasDomainDot: true })); } else { // Anything else captureMatchIfValidAndReset(); } } function beginEmailMatch(newState) { if (newState === void 0) { newState = 2 /* LocalPart */; } state = newState; currentEmailMatch = new CurrentEmailMatch({ idx: charIdx }); } function resetToNonEmailMatchState() { state = 0 /* NonEmailMatch */; currentEmailMatch = noCurrentEmailMatch; } /* * Captures the current email address as an EmailMatch if it's valid, * and resets the state to read another email address. */ function captureMatchIfValidAndReset() { if (currentEmailMatch.hasDomainDot) { // we need at least one dot in the domain to be considered a valid email address var matchedText = text.slice(currentEmailMatch.idx, charIdx); // If we read a '.' or '-' char that ended the email address // (valid domain name characters, but only valid email address // characters if they are followed by something else), strip // it off now if (/[-.]$/.test(matchedText)) { matchedText = matchedText.slice(0, -1); } var emailAddress = currentEmailMatch.hasMailtoPrefix ? matchedText.slice('mailto:'.length) : matchedText; // if the email address has a valid TLD, add it to the list of matches if (doesEmailHaveValidTld(emailAddress)) { matches.push(new EmailMatch({ tagBuilder: tagBuilder, matchedText: matchedText, offset: currentEmailMatch.idx, email: emailAddress })); } } resetToNonEmailMatchState(); /** * Determines if the given email address has a valid TLD or not * @param {string} emailAddress - email address * @return {Boolean} - true is email have valid TLD, false otherwise */ function doesEmailHaveValidTld(emailAddress) { var emailAddressTld = emailAddress.split('.').pop() || ''; var emailAddressNormalized = emailAddressTld.toLowerCase(); var isValidTld = strictTldRegex.test(emailAddressNormalized); return isValidTld; } } }; return EmailMatcher; }(Matcher)); var CurrentEmailMatch = /** @class */ (function () { function CurrentEmailMatch(cfg) { if (cfg === void 0) { cfg = {}; } this.idx = cfg.idx !== undefined ? cfg.idx : -1; this.hasMailtoPrefix = !!cfg.hasMailtoPrefix; this.hasDomainDot = !!cfg.hasDomainDot; } return CurrentEmailMatch; }()); /** * @private * @class Autolinker.matcher.UrlMatchValidator * @singleton * * Used by Autolinker to filter out false URL positives from the * {@link Autolinker.matcher.Url UrlMatcher}. * * Due to the limitations of regular expressions (including the missing feature * of look-behinds in JS regular expressions), we cannot always determine the * validity of a given match. This class applies a bit of additional logic to * filter out any false positives that have been matched by the * {@link Autolinker.matcher.Url UrlMatcher}. */ var UrlMatchValidator = /** @class */ (function () { function UrlMatchValidator() { } /** * Determines if a given URL match found by the {@link Autolinker.matcher.Url UrlMatcher} * is valid. Will return `false` for: * * 1) URL matches which do not have at least have one period ('.') in the * domain name (effectively skipping over matches like "abc:def"). * However, URL matches with a protocol will be allowed (ex: 'http://localhost') * 2) URL matches which do not have at least one word character in the * domain name (effectively skipping over matches like "git:1.0"). * 3) A protocol-relative url match (a URL beginning with '//') whose * previous character is a word character (effectively skipping over * strings like "abc//google.com") * * Otherwise, returns `true`. * * @param {String} urlMatch The matched URL, if there was one. Will be an * empty string if the match is not a URL match. * @param {String} protocolUrlMatch The match URL string for a protocol * match. Ex: 'http://yahoo.com'. This is used to match something like * 'http://localhost', where we won't double check that the domain name * has at least one '.' in it. * @return {Boolean} `true` if the match given is valid and should be * processed, or `false` if the match is invalid and/or should just not be * processed. */ UrlMatchValidator.isValid = function (urlMatch, protocolUrlMatch) { if ((protocolUrlMatch && !this.isValidUriScheme(protocolUrlMatch)) || this.urlMatchDoesNotHaveProtocolOrDot(urlMatch, protocolUrlMatch) || // At least one period ('.') must exist in the URL match for us to consider it an actual URL, *unless* it was a full protocol match (like 'http://localhost') (this.urlMatchDoesNotHaveAtLeastOneWordChar(urlMatch, protocolUrlMatch) && // At least one letter character must exist in the domain name after a protocol match. Ex: skip over something like "git:1.0" !this.isValidIpAddress(urlMatch)) || // Except if it's an IP address this.containsMultipleDots(urlMatch)) { return false; } return true; }; UrlMatchValidator.isValidIpAddress = function (uriSchemeMatch) { var newRegex = new RegExp(this.hasFullProtocolRegex.source + this.ipRegex.source); var uriScheme = uriSchemeMatch.match(newRegex); return uriScheme !== null; }; UrlMatchValidator.containsMultipleDots = function (urlMatch) { var stringBeforeSlash = urlMatch; if (this.hasFullProtocolRegex.test(urlMatch)) { stringBeforeSlash = urlMatch.split('://')[1]; } return stringBeforeSlash.split('/')[0].indexOf("..") > -1; }; /** * Determines if the URI scheme is a valid scheme to be autolinked. Returns * `false` if the scheme is 'javascript:' or 'vbscript:' * * @private * @param {String} uriSchemeMatch The match URL string for a full URI scheme * match. Ex: 'http://yahoo.com' or 'mailto:a@a.com'. * @return {Boolean} `true` if the scheme is a valid one, `false` otherwise. */ UrlMatchValidator.isValidUriScheme = function (uriSchemeMatch) { var uriSchemeMatchArr = uriSchemeMatch.match(this.uriSchemeRegex), uriScheme = uriSchemeMatchArr && uriSchemeMatchArr[0].toLowerCase(); return (uriScheme !== 'javascript:' && uriScheme !== 'vbscript:'); }; /** * Determines if a URL match does not have either: * * a) a full protocol (i.e. 'http://'), or * b) at least one dot ('.') in the domain name (for a non-full-protocol * match). * * Either situation is considered an invalid URL (ex: 'git:d' does not have * either the '://' part, or at least one dot in the domain name. If the * match was 'git:abc.com', we would consider this valid.) * * @private * @param {String} urlMatch The matched URL, if there was one. Will be an * empty string if the match is not a URL match. * @param {String} protocolUrlMatch The match URL string for a protocol * match. Ex: 'http://yahoo.com'. This is used to match something like * 'http://localhost', where we won't double check that the domain name * has at least one '.' in it. * @return {Boolean} `true` if the URL match does not have a full protocol, * or at least one dot ('.') in a non-full-protocol match. */ UrlMatchValidator.urlMatchDoesNotHaveProtocolOrDot = function (urlMatch, protocolUrlMatch) { return (!!urlMatch && (!protocolUrlMatch || !this.hasFullProtocolRegex.test(protocolUrlMatch)) && urlMatch.indexOf('.') === -1); }; /** * Determines if a URL match does not have at least one word character after * the protocol (i.e. in the domain name). * * At least one letter character must exist in the domain name after a * protocol match. Ex: skip over something like "git:1.0" * * @private * @param {String} urlMatch The matched URL, if there was one. Will be an * empty string if the match is not a URL match. * @param {String} protocolUrlMatch The match URL string for a protocol * match. Ex: 'http://yahoo.com'. This is used to know whether or not we * have a protocol in the URL string, in order to check for a word * character after the protocol separator (':'). * @return {Boolean} `true` if the URL match does not have at least one word * character in it after the protocol, `false` otherwise. */ UrlMatchValidator.urlMatchDoesNotHaveAtLeastOneWordChar = function (urlMatch, protocolUrlMatch) { if (urlMatch && protocolUrlMatch) { return !this.hasWordCharAfterProtocolRegex.test(urlMatch); } else { return false; } }; /** * Regex to test for a full protocol, with the two trailing slashes. Ex: 'http://' * * @private * @property {RegExp} hasFullProtocolRegex */ UrlMatchValidator.hasFullProtocolRegex = /^[A-Za-z][-.+A-Za-z0-9]*:\/\//; /** * Regex to find the URI scheme, such as 'mailto:'. * * This is used to filter out 'javascript:' and 'vbscript:' schemes. * * @private * @property {RegExp} uriSchemeRegex */ UrlMatchValidator.uriSchemeRegex = /^[A-Za-z][-.+A-Za-z0-9]*:/; /** * Regex to determine if at least one word char exists after the protocol (i.e. after the ':') * * @private * @property {RegExp} hasWordCharAfterProtocolRegex */ UrlMatchValidator.hasWordCharAfterProtocolRegex = new RegExp(":[^\\s]*?[" + alphaCharsStr + "]"); /** * Regex to determine if the string is a valid IP address * * @private * @property {RegExp} ipRegex */ UrlMatchValidator.ipRegex = /[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/; return UrlMatchValidator; }()); /** * @class Autolinker.matcher.Url * @extends Autolinker.matcher.Matcher * * Matcher to find URL matches in an input string. * * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details. */ var UrlMatcher = /** @class */ (function (_super) { __extends(UrlMatcher, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match instance, * specified in an Object (map). */ function UrlMatcher(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {Object} stripPrefix (required) * * The Object form of {@link Autolinker#cfg-stripPrefix}. */ _this.stripPrefix = { scheme: true, www: true }; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} stripTrailingSlash (required) * @inheritdoc Autolinker#stripTrailingSlash */ _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @cfg {Boolean} decodePercentEncoding (required) * @inheritdoc Autolinker#decodePercentEncoding */ _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator /** * @protected * @property {RegExp} matcherRegex * * The regular expression to match URLs with an optional scheme, port * number, path, query string, and hash anchor. * * Example matches: * * http://google.com * www.google.com * google.com/path/to/file?q1=1&q2=2#myAnchor * * * This regular expression will have the following capturing groups: * * 1. Group that matches a scheme-prefixed URL (i.e. 'http://google.com'). * This is used to match scheme URLs with just a single word, such as * 'http://localhost', where we won't double check that the domain name * has at least one dot ('.') in it. * 2. Group that matches a 'www.' prefixed URL. This is only matched if the * 'www.' text was not prefixed by a scheme (i.e.: not prefixed by * 'http://', 'ftp:', etc.) * 3. A protocol-relative ('//') match for the case of a 'www.' prefixed * URL. Will be an empty string if it is not a protocol-relative match. * We need to know the character before the '//' in order to determine * if it is a valid match or the // was in a string we don't want to * auto-link. * 4. Group that matches a known TLD (top level domain), when a scheme * or 'www.'-prefixed domain is not matched. * 5. A protocol-relative ('//') match for the case of a known TLD prefixed * URL. Will be an empty string if it is not a protocol-relative match. * See #3 for more info. */ _this.matcherRegex = (function () { var schemeRegex = /(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/, // match protocol, allow in format "http://" or "mailto:". However, do not match the first part of something like 'link:http://www.google.com' (i.e. don't match "link:"). Also, make sure we don't interpret 'google.com:8000' as if 'google.com' was a protocol here (i.e. ignore a trailing port number in this regex) wwwRegex = /(?:www\.)/, // starting with 'www.' // Allow optional path, query string, and hash anchor, not ending in the following characters: "?!:,.;" // http://blog.codinghorror.com/the-problem-with-urls/ urlSuffixRegex = new RegExp('[/?#](?:[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]?!:,.;\u2713]*[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]\u2713])?'); return new RegExp([ '(?:', '(', schemeRegex.source, getDomainNameStr(2), ')', '|', '(', '(//)?', wwwRegex.source, getDomainNameStr(6), ')', '|', '(', '(//)?', getDomainNameStr(10) + '\\.', tldRegex.source, '(?![-' + alphaNumericCharsStr + '])', ')', ')', '(?::[0-9]+)?', '(?:' + urlSuffixRegex.source + ')?' // match for path, query string, and/or hash anchor - optional ].join(""), 'gi'); })(); /** * A regular expression to use to check the character before a protocol-relative * URL match. We don't want to match a protocol-relative URL if it is part * of another word. * * For example, we want to match something like "Go to: //google.com", * but we don't want to match something like "abc//google.com" * * This regular expression is used to test the character before the '//'. * * @protected * @type {RegExp} wordCharRegExp */ _this.wordCharRegExp = new RegExp('[' + alphaNumericAndMarksCharsStr + ']'); _this.stripPrefix = cfg.stripPrefix; _this.stripTrailingSlash = cfg.stripTrailingSlash; _this.decodePercentEncoding = cfg.decodePercentEncoding; return _this; } /** * @inheritdoc */ UrlMatcher.prototype.parseMatches = function (text) { var matcherRegex = this.matcherRegex, stripPrefix = this.stripPrefix, stripTrailingSlash = this.stripTrailingSlash, decodePercentEncoding = this.decodePercentEncoding, tagBuilder = this.tagBuilder, matches = [], match; var _loop_1 = function () { var matchStr = match[0], schemeUrlMatch = match[1], wwwUrlMatch = match[4], wwwProtocolRelativeMatch = match[5], //tldUrlMatch = match[ 8 ], -- not needed at the moment tldProtocolRelativeMatch = match[9], offset = match.index, protocolRelativeMatch = wwwProtocolRelativeMatch || tldProtocolRelativeMatch, prevChar = text.charAt(offset - 1); if (!UrlMatchValidator.isValid(matchStr, schemeUrlMatch)) { return "continue"; } // If the match is preceded by an '@' character, then it is either // an email address or a username. Skip these types of matches. if (offset > 0 && prevChar === '@') { return "continue"; } // If it's a protocol-relative '//' match, but the character before the '//' // was a word character (i.e. a letter/number), then we found the '//' in the // middle of another word (such as "asdf//asdf.com"). In this case, skip the // match. if (offset > 0 && protocolRelativeMatch && this_1.wordCharRegExp.test(prevChar)) { return "continue"; } // If the URL ends with a question mark, don't include the question // mark as part of the URL. We'll assume the question mark was the // end of a sentence, such as: "Going to google.com?" if (/\?$/.test(matchStr)) { matchStr = matchStr.substr(0, matchStr.length - 1); } // Handle a closing parenthesis or square bracket at the end of the // match, and exclude it if there is not a matching open parenthesis // or square bracket in the match itself. if (this_1.matchHasUnbalancedClosingParen(matchStr)) { matchStr = matchStr.substr(0, matchStr.length - 1); // remove the trailing ")" } else { // Handle an invalid character after the TLD var pos = this_1.matchHasInvalidCharAfterTld(matchStr, schemeUrlMatch); if (pos > -1) { matchStr = matchStr.substr(0, pos); // remove the trailing invalid chars } } // The autolinker accepts many characters in a url's scheme (like `fake://test.com`). // However, in cases where a URL is missing whitespace before an obvious link, // (for example: `nowhitespacehttp://www.test.com`), we only want the match to start // at the http:// part. We will check if the match contains a common scheme and then // shift the match to start from there. var foundCommonScheme = ['http://', 'https://'].find(function (commonScheme) { return !!schemeUrlMatch && schemeUrlMatch.indexOf(commonScheme) !== -1; }); if (foundCommonScheme) { // If we found an overmatched URL, we want to find the index // of where the match should start and shift the match to // start from the beginning of the common scheme var indexOfSchemeStart = matchStr.indexOf(foundCommonScheme); matchStr = matchStr.substr(indexOfSchemeStart); schemeUrlMatch = schemeUrlMatch.substr(indexOfSchemeStart); offset = offset + indexOfSchemeStart; } var urlMatchType = schemeUrlMatch ? 'scheme' : (wwwUrlMatch ? 'www' : 'tld'), protocolUrlMatch = !!schemeUrlMatch; matches.push(new UrlMatch({ tagBuilder: tagBuilder, matchedText: matchStr, offset: offset, urlMatchType: urlMatchType, url: matchStr, protocolUrlMatch: protocolUrlMatch, protocolRelativeMatch: !!protocolRelativeMatch, stripPrefix: stripPrefix, stripTrailingSlash: stripTrailingSlash, decodePercentEncoding: decodePercentEncoding, })); }; var this_1 = this; while ((match = matcherRegex.exec(text)) !== null) { _loop_1(); } return matches; }; /** * Determines if a match found has an unmatched closing parenthesis or * square bracket. If so, the parenthesis or square bracket will be removed * from the match itself, and appended after the generated anchor tag. * * A match may have an extra closing parenthesis at the end of the match * because the regular expression must include parenthesis for URLs such as * "wikipedia.com/something_(disambiguation)", which should be auto-linked. * * However, an extra parenthesis *will* be included when the URL itself is * wrapped in parenthesis, such as in the case of: * "(wikipedia.com/something_(disambiguation))" * In this case, the last closing parenthesis should *not* be part of the * URL itself, and this method will return `true`. * * For square brackets in URLs such as in PHP arrays, the same behavior as * parenthesis discussed above should happen: * "[http://www.example.com/foo.php?bar[]=1&bar[]=2&bar[]=3]" * The closing square bracket should not be part of the URL itself, and this * method will return `true`. * * @protected * @param {String} matchStr The full match string from the {@link #matcherRegex}. * @return {Boolean} `true` if there is an unbalanced closing parenthesis or * square bracket at the end of the `matchStr`, `false` otherwise. */ UrlMatcher.prototype.matchHasUnbalancedClosingParen = function (matchStr) { var endChar = matchStr.charAt(matchStr.length - 1); var startChar; if (endChar === ')') { startChar = '('; } else if (endChar === ']') { startChar = '['; } else { return false; // not a close parenthesis or square bracket } // Find if there are the same number of open braces as close braces in // the URL string, minus the last character (which we have already // determined to be either ')' or ']' var numOpenBraces = 0; for (var i = 0, len = matchStr.length - 1; i < len; i++) { var char = matchStr.charAt(i); if (char === startChar) { numOpenBraces++; } else if (char === endChar) { numOpenBraces = Math.max(numOpenBraces - 1, 0); } } // If the number of open braces matches the number of close braces in // the URL minus the last character, then the match has *unbalanced* // braces because of the last character. Example of unbalanced braces // from the regex match: // "http://example.com?a[]=1]" if (numOpenBraces === 0) { return true; } return false; }; /** * Determine if there's an invalid character after the TLD in a URL. Valid * characters after TLD are ':/?#'. Exclude scheme matched URLs from this * check. * * @protected * @param {String} urlMatch The matched URL, if there was one. Will be an * empty string if the match is not a URL match. * @param {String} schemeUrlMatch The match URL string for a scheme * match. Ex: 'http://yahoo.com'. This is used to match something like * 'http://localhost', where we won't double check that the domain name * has at least one '.' in it. * @return {Number} the position where the invalid character was found. If * no such character was found, returns -1 */ UrlMatcher.prototype.matchHasInvalidCharAfterTld = function (urlMatch, schemeUrlMatch) { if (!urlMatch) { return -1; } var offset = 0; if (schemeUrlMatch) { offset = urlMatch.indexOf(':'); urlMatch = urlMatch.slice(offset); } var re = new RegExp("^((.?\/\/)?[-." + alphaNumericAndMarksCharsStr + "]*[-" + alphaNumericAndMarksCharsStr + "]\\.[-" + alphaNumericAndMarksCharsStr + "]+)"); var res = re.exec(urlMatch); if (res === null) { return -1; } offset += res[1].length; urlMatch = urlMatch.slice(res[1].length); if (/^[^-.A-Za-z0-9:\/?#]/.test(urlMatch)) { return offset; } return -1; }; return UrlMatcher; }(Matcher)); /** * @class Autolinker.matcher.Hashtag * @extends Autolinker.matcher.Matcher * * Matcher to find HashtagMatch matches in an input string. */ var HashtagMatcher = /** @class */ (function (_super) { __extends(HashtagMatcher, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match instance, * specified in an Object (map). */ function HashtagMatcher(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {String} serviceName * * The service to point hashtag matches to. See {@link Autolinker#hashtag} * for available values. */ _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator /** * The regular expression to match Hashtags. Example match: * * #asdf * * @protected * @property {RegExp} matcherRegex */ _this.matcherRegex = new RegExp("#[_" + alphaNumericAndMarksCharsStr + "]{1,139}(?![_" + alphaNumericAndMarksCharsStr + "])", 'g'); // lookahead used to make sure we don't match something above 139 characters /** * The regular expression to use to check the character before a username match to * make sure we didn't accidentally match an email address. * * For example, the string "asdf@asdf.com" should not match "@asdf" as a username. * * @protected * @property {RegExp} nonWordCharRegex */ _this.nonWordCharRegex = new RegExp('[^' + alphaNumericAndMarksCharsStr + ']'); _this.serviceName = cfg.serviceName; return _this; } /** * @inheritdoc */ HashtagMatcher.prototype.parseMatches = function (text) { var matcherRegex = this.matcherRegex, nonWordCharRegex = this.nonWordCharRegex, serviceName = this.serviceName, tagBuilder = this.tagBuilder, matches = [], match; while ((match = matcherRegex.exec(text)) !== null) { var offset = match.index, prevChar = text.charAt(offset - 1); // If we found the match at the beginning of the string, or we found the match // and there is a whitespace char in front of it (meaning it is not a '#' char // in the middle of a word), then it is a hashtag match. if (offset === 0 || nonWordCharRegex.test(prevChar)) { var matchedText = match[0], hashtag = match[0].slice(1); // strip off the '#' character at the beginning matches.push(new HashtagMatch({ tagBuilder: tagBuilder, matchedText: matchedText, offset: offset, serviceName: serviceName, hashtag: hashtag })); } } return matches; }; return HashtagMatcher; }(Matcher)); /** * @class Autolinker.matcher.Phone * @extends Autolinker.matcher.Matcher * * Matcher to find Phone number matches in an input string. * * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more * details. */ var PhoneMatcher = /** @class */ (function (_super) { __extends(PhoneMatcher, _super); function PhoneMatcher() { var _this = _super !== null && _super.apply(this, arguments) || this; /** * The regular expression to match Phone numbers. Example match: * * (123) 456-7890 * * This regular expression has the following capturing groups: * * 1 or 2. The prefixed '+' sign, if there is one. * * @protected * @property {RegExp} matcherRegex */ _this.matcherRegex = /(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/g; return _this; } // ex: (123) 456-7890, 123 456 7890, 123-456-7890, +18004441234,,;,10226420346#, // +1 (800) 444 1234, 10226420346#, 1-800-444-1234,1022,64,20346# /** * @inheritdoc */ PhoneMatcher.prototype.parseMatches = function (text) { var matcherRegex = this.matcherRegex, tagBuilder = this.tagBuilder, matches = [], match; while ((match = matcherRegex.exec(text)) !== null) { // Remove non-numeric values from phone number string var matchedText = match[0], cleanNumber = matchedText.replace(/[^0-9,;#]/g, ''), // strip out non-digit characters exclude comma semicolon and # plusSign = !!(match[1] || match[2]), // match[ 1 ] or match[ 2 ] is the prefixed plus sign, if there is one before = match.index == 0 ? '' : text.substr(match.index - 1, 1), after = text.substr(match.index + matchedText.length, 1), contextClear = !before.match(/\d/) && !after.match(/\d/); if (this.testMatch(match[3]) && this.testMatch(matchedText) && contextClear) { matches.push(new PhoneMatch({ tagBuilder: tagBuilder, matchedText: matchedText, offset: match.index, number: cleanNumber, plusSign: plusSign })); } } return matches; }; PhoneMatcher.prototype.testMatch = function (text) { return /\D/.test(text); }; return PhoneMatcher; }(Matcher)); /** * @class Autolinker.matcher.Mention * @extends Autolinker.matcher.Matcher * * Matcher to find/replace username matches in an input string. */ var MentionMatcher = /** @class */ (function (_super) { __extends(MentionMatcher, _super); /** * @method constructor * @param {Object} cfg The configuration properties for the Match instance, * specified in an Object (map). */ function MentionMatcher(cfg) { var _this = _super.call(this, cfg) || this; /** * @cfg {'twitter'/'instagram'/'soundcloud'} protected * * The name of service to link @mentions to. * * Valid values are: 'twitter', 'instagram', or 'soundcloud' */ _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator /** * Hash of regular expression to match username handles. Example match: * * @asdf * * @private * @property {Object} matcherRegexes */ _this.matcherRegexes = { 'twitter': new RegExp("@[_" + alphaNumericAndMarksCharsStr + "]{1,50}(?![_" + alphaNumericAndMarksCharsStr + "])", 'g'), 'instagram': new RegExp("@[_." + alphaNumericAndMarksCharsStr + "]{1,30}(?![_" + alphaNumericAndMarksCharsStr + "])", 'g'), 'soundcloud': new RegExp("@[-_." + alphaNumericAndMarksCharsStr + "]{1,50}(?![-_" + alphaNumericAndMarksCharsStr + "])", 'g') // lookahead used to make sure we don't match something above 50 characters }; /** * The regular expression to use to check the character before a username match to * make sure we didn't accidentally match an email address. * * For example, the string "asdf@asdf.com" should not match "@asdf" as a username. * * @private * @property {RegExp} nonWordCharRegex */ _this.nonWordCharRegex = new RegExp('[^' + alphaNumericAndMarksCharsStr + ']'); _this.serviceName = cfg.serviceName; return _this; } /** * @inheritdoc */ MentionMatcher.prototype.parseMatches = function (text) { var serviceName = this.serviceName, matcherRegex = this.matcherRegexes[this.serviceName], nonWordCharRegex = this.nonWordCharRegex, tagBuilder = this.tagBuilder, matches = [], match; if (!matcherRegex) { return matches; } while ((match = matcherRegex.exec(text)) !== null) { var offset = match.index, prevChar = text.charAt(offset - 1); // If we found the match at the beginning of the string, or we found the match // and there is a whitespace char in front of it (meaning it is not an email // address), then it is a username match. if (offset === 0 || nonWordCharRegex.test(prevChar)) { var matchedText = match[0].replace(/\.+$/g, ''), // strip off trailing . mention = matchedText.slice(1); // strip off the '@' character at the beginning matches.push(new MentionMatch({ tagBuilder: tagBuilder, matchedText: matchedText, offset: offset, serviceName: serviceName, mention: mention })); } } return matches; }; return MentionMatcher; }(Matcher)); // For debugging: search for other "For debugging" lines // import CliTable from 'cli-table'; /** * Parses an HTML string, calling the callbacks to notify of tags and text. * * ## History * * This file previously used a regular expression to find html tags in the input * text. Unfortunately, we ran into a bunch of catastrophic backtracking issues * with certain input text, causing Autolinker to either hang or just take a * really long time to parse the string. * * The current code is intended to be a O(n) algorithm that walks through * the string in one pass, and tries to be as cheap as possible. We don't need * to implement the full HTML spec, but rather simply determine where the string * looks like an HTML tag, and where it looks like text (so that we can autolink * that). * * This state machine parser is intended just to be a simple but performant * parser of HTML for the subset of requirements we have. We simply need to: * * 1. Determine where HTML tags are * 2. Determine the tag name (Autolinker specifically only cares about , *