cesium/packages/engine/Source/Scene/PrimitiveCollection.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

533 lines
17 KiB
JavaScript
Raw Permalink Normal View History

Migrate Cesium to ES6 Modules See https://github.com/AnalyticalGraphicsInc/cesium/pull/8224 for details. eslint There are a handful of new .eslintrc.json files, mostly to identify the files that are still AMD modules (Sandcastle/Workers). These are needed because you can't change the parser type with a comment directive (since the parser is the thing reading the file). We can finally detect unusued modules! So those have all been cleaned up as well. requirejs -> rollup & clean-css requirejs, almond, and karma-requirejs have all been removed. We now use rollup for building and minifying (via uglify) JS code and clean-css for css. These changes are fairly straight-forward and just involve calling rollup instead of requirejs in the build process. Overall build time is significantly faster. CI is ~11 minutes compared to ~17 in master. Running makeZipFile on my machine takes 69 seconds compared to 112 seconds in master. There's probably plenty of room for additional optimization here too. We wrote an published a small npm module, rollup-plugin-strip-pragma, for stripping the requirejs pragmas we use out of the release builds. This is maintained in the Tools/rollup-plugin-strip-pragma directory. As for what we produce. The built version of Cesium is now a UMD module. So it should work anywhere that hasn't made the jump to ES6 yet. For users that were already using the "legacy" combined/minified approach, nothing changes. One awesome thing about roll-up is that it compiles all of the workers at once and automatically detects shared codes and generates separate bundles under the hood. This means the size of our worker modules shrink dramatically and Cesium itself will load them much faster. The total minified/gzipped size of all workers in master is 2.6 MB compared to 225 KB in this branch! This should be most noticeable on demos like Geometry & Appearances which load lots of workers for the various geometry typs. roll-up is also used to build Cesium Viewer, which is now an ES6 app. We use clean-css via gulp and it is also a straightforward change from requirejs that requires no special mention. Workers While the spec allows for ES6 Web Workers, no browser actually supports them yet. That means we needed a way to get our workers into non-ES6 form. Thankfully, roll-up can generate AMD modules, which means we now have a build step to compile our Worker source code back into AMD and use the existing TaskProcessor to load and execute them. This build step is part of the standard build task and is called createWorkers. During development, these "built" workers are un-optimized so you can still debug them and read the code. Since there is a build step, that means if you are changing code that affects a worker, you need to re-run build, or you can use the build-watch task to do it automatically. The ES6 versions of Worker code has moved into Source/WorkersES6 and we build the workers into their "old home" of Source/Workers. cesiumWorkerBootstrapper and transferTypedArrayTest which were already non-AMD ES5 scripts remain living in the Workers directory. Surprisingly little was changed about TaskProcessor or the worker system in general, especially considering that I thought this would be one of the major hurdles. ThirdParty A lot of our ThirdParty either already had a hand-written wrapper for AMD (which I updated to ES6) or had UMD which created problems when importing the same code in both Node and the browser. I basically had to update the wrapper of every third-party library to fix these problems. In some cases I updated the library version itself (Autolinker, topojson). Nothing to be too concerned about, but future clean-up would be using npm versions of these libraries and auto-generating the wrappers as needed so we don't hand-edit things. Sandcastle Sandcastle is eternal and manages to live another day in it's ancient requirejs/dojo 1.x form. Sandcastle now automatically uses the ES6 version of Cesium if it is available and fallsback to the ES5 unminified version if it is now. The built version of Sandcastle always uses CesiumUnminified, just like master. This means Sandcastle still works in IE11 if you run the combine step first (or use the relase zip) Removed Cesium usage from Sandcastle proper, since it wasn't really needed Generate a VERSION propertyin the gallery index since Cesium is no longer being included. Remove requirejs from Sandcastle bucket Update bucket to use the built version of Cesium if it is available by fallbackto the ES6 version during development. Standalone.html was also updated There's a bit of room for further clean-up here, but I think this gets us into master. I did not rename bucket-requirejs.html because I'm pretty sure it would break previously shared demos. We can put in some backwards compatible code later on if we want. (But I'd rather just see a full Sandcastle rewrite). Specs Specs are now all ES6, except for TestWorkers, which remain standard JS worker modules. This means you can no longer run the unbuilt unit tests in IE11. No changes for Chrome and Firefox. Since the specs use ES6 modules and built Cesium is an ES5 UMD, I added a build-specs build step which generates a combined ES5 version of the specs which rely on Cesium as a global variable. We then inject these files into jasmine instead of the standard specs and everything works exactly as it did before. SpecRunner.html has been updated to inject the correct version of the script depending on the build/release query parameters. The Specs must always use Cesium by importing Source/Cesium.js, this is so we can replace it with the built Cesium as describe above. There's a bunch of room for clean-up here, such as unifying our two copies of jasmine into a single helper file, but I didn't want to start doing that clean-up as part of this already overly big PR. The important thing is that we can still test the built version and still test on IE/Edge as needed. I also found and fixed two bugs that were causing failing unit tests, one in BingMapsImageryProviderSpec.js (which was overwriting createImage andnot setting it back) and ShadowVolumeAppearance.js (which had a module level caching bug). I think these may have been the cause of random CI failures in master as well, but only time will tell. For coverage, we had to switch to karma-coverage-istanbul-instrumenter for native ES6 support, but that's it. Finally, I updated appveryor to build Cesium and run the built tests under IE. We still don't fail the build for IE, but we should probably fix that if we want to keep it going. NodeJS When NODE_ENV is production, we now require in the minified CesiumJS directly, which works great because it's now a UMD module. Otherwise, we use the excellant esmpackage to load individual modules, it was a fairly straightforward swap from our old requirejs usage. We could probably drop esm too if we don't care about debugging or if we provie source maps at some point.
2019-10-03 23:51:23 +08:00
import createGuid from "../Core/createGuid.js";
import Frozen from "../Core/Frozen.js";
Migrate Cesium to ES6 Modules See https://github.com/AnalyticalGraphicsInc/cesium/pull/8224 for details. eslint There are a handful of new .eslintrc.json files, mostly to identify the files that are still AMD modules (Sandcastle/Workers). These are needed because you can't change the parser type with a comment directive (since the parser is the thing reading the file). We can finally detect unusued modules! So those have all been cleaned up as well. requirejs -> rollup & clean-css requirejs, almond, and karma-requirejs have all been removed. We now use rollup for building and minifying (via uglify) JS code and clean-css for css. These changes are fairly straight-forward and just involve calling rollup instead of requirejs in the build process. Overall build time is significantly faster. CI is ~11 minutes compared to ~17 in master. Running makeZipFile on my machine takes 69 seconds compared to 112 seconds in master. There's probably plenty of room for additional optimization here too. We wrote an published a small npm module, rollup-plugin-strip-pragma, for stripping the requirejs pragmas we use out of the release builds. This is maintained in the Tools/rollup-plugin-strip-pragma directory. As for what we produce. The built version of Cesium is now a UMD module. So it should work anywhere that hasn't made the jump to ES6 yet. For users that were already using the "legacy" combined/minified approach, nothing changes. One awesome thing about roll-up is that it compiles all of the workers at once and automatically detects shared codes and generates separate bundles under the hood. This means the size of our worker modules shrink dramatically and Cesium itself will load them much faster. The total minified/gzipped size of all workers in master is 2.6 MB compared to 225 KB in this branch! This should be most noticeable on demos like Geometry & Appearances which load lots of workers for the various geometry typs. roll-up is also used to build Cesium Viewer, which is now an ES6 app. We use clean-css via gulp and it is also a straightforward change from requirejs that requires no special mention. Workers While the spec allows for ES6 Web Workers, no browser actually supports them yet. That means we needed a way to get our workers into non-ES6 form. Thankfully, roll-up can generate AMD modules, which means we now have a build step to compile our Worker source code back into AMD and use the existing TaskProcessor to load and execute them. This build step is part of the standard build task and is called createWorkers. During development, these "built" workers are un-optimized so you can still debug them and read the code. Since there is a build step, that means if you are changing code that affects a worker, you need to re-run build, or you can use the build-watch task to do it automatically. The ES6 versions of Worker code has moved into Source/WorkersES6 and we build the workers into their "old home" of Source/Workers. cesiumWorkerBootstrapper and transferTypedArrayTest which were already non-AMD ES5 scripts remain living in the Workers directory. Surprisingly little was changed about TaskProcessor or the worker system in general, especially considering that I thought this would be one of the major hurdles. ThirdParty A lot of our ThirdParty either already had a hand-written wrapper for AMD (which I updated to ES6) or had UMD which created problems when importing the same code in both Node and the browser. I basically had to update the wrapper of every third-party library to fix these problems. In some cases I updated the library version itself (Autolinker, topojson). Nothing to be too concerned about, but future clean-up would be using npm versions of these libraries and auto-generating the wrappers as needed so we don't hand-edit things. Sandcastle Sandcastle is eternal and manages to live another day in it's ancient requirejs/dojo 1.x form. Sandcastle now automatically uses the ES6 version of Cesium if it is available and fallsback to the ES5 unminified version if it is now. The built version of Sandcastle always uses CesiumUnminified, just like master. This means Sandcastle still works in IE11 if you run the combine step first (or use the relase zip) Removed Cesium usage from Sandcastle proper, since it wasn't really needed Generate a VERSION propertyin the gallery index since Cesium is no longer being included. Remove requirejs from Sandcastle bucket Update bucket to use the built version of Cesium if it is available by fallbackto the ES6 version during development. Standalone.html was also updated There's a bit of room for further clean-up here, but I think this gets us into master. I did not rename bucket-requirejs.html because I'm pretty sure it would break previously shared demos. We can put in some backwards compatible code later on if we want. (But I'd rather just see a full Sandcastle rewrite). Specs Specs are now all ES6, except for TestWorkers, which remain standard JS worker modules. This means you can no longer run the unbuilt unit tests in IE11. No changes for Chrome and Firefox. Since the specs use ES6 modules and built Cesium is an ES5 UMD, I added a build-specs build step which generates a combined ES5 version of the specs which rely on Cesium as a global variable. We then inject these files into jasmine instead of the standard specs and everything works exactly as it did before. SpecRunner.html has been updated to inject the correct version of the script depending on the build/release query parameters. The Specs must always use Cesium by importing Source/Cesium.js, this is so we can replace it with the built Cesium as describe above. There's a bunch of room for clean-up here, such as unifying our two copies of jasmine into a single helper file, but I didn't want to start doing that clean-up as part of this already overly big PR. The important thing is that we can still test the built version and still test on IE/Edge as needed. I also found and fixed two bugs that were causing failing unit tests, one in BingMapsImageryProviderSpec.js (which was overwriting createImage andnot setting it back) and ShadowVolumeAppearance.js (which had a module level caching bug). I think these may have been the cause of random CI failures in master as well, but only time will tell. For coverage, we had to switch to karma-coverage-istanbul-instrumenter for native ES6 support, but that's it. Finally, I updated appveryor to build Cesium and run the built tests under IE. We still don't fail the build for IE, but we should probably fix that if we want to keep it going. NodeJS When NODE_ENV is production, we now require in the minified CesiumJS directly, which works great because it's now a UMD module. Otherwise, we use the excellant esmpackage to load individual modules, it was a fairly straightforward swap from our old requirejs usage. We could probably drop esm too if we don't care about debugging or if we provie source maps at some point.
2019-10-03 23:51:23 +08:00
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
2023-09-21 11:55:33 +08:00
import Event from "../Core/Event.js";
2020-04-17 08:31:36 +08:00
/**
2014-05-15 04:44:43 +08:00
* A collection of primitives. This is most often used with {@link Scene#primitives},
* but <code>PrimitiveCollection</code> is also a primitive itself so collections can
2014-05-15 04:44:43 +08:00
* 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.
* @privateParam {boolean} [options.countReferences=false] Specifies whether adding and removing primitives from this collection alters their reference counts. If so, adding a
* primitive to this collection increments its reference count. Removing the primitive decrements its reference count and - if the count reaches zero **and** destroyPrimitives is true - destroys the primitive.
2025-05-26 17:35:23 +08:00
* This permits primitives to be shared between multiple collections.
*
* @example
* const billboards = new Cesium.BillboardCollection();
* const labels = new Cesium.LabelCollection();
*
* const collection = new Cesium.PrimitiveCollection();
2014-05-15 04:44:43 +08:00
* collection.add(billboards);
*
2014-05-15 04:44:43 +08:00
* scene.primitives.add(collection); // Add collection
* scene.primitives.add(labels); // Add regular primitive
2020-04-17 08:31:36 +08:00
*/
function PrimitiveCollection(options) {
options = options ?? Frozen.EMPTY_OBJECT;
2020-04-17 08:31:36 +08:00
this._primitives = [];
this._guid = createGuid();
2023-09-21 11:55:33 +08:00
this._primitiveAdded = new Event();
this._primitiveRemoved = new Event();
2020-04-17 08:31:36 +08:00
// Used by the OrderedGroundPrimitiveCollection
2018-05-10 03:12:43 +08:00
this._zIndex = undefined;
2020-04-17 08:31:36 +08:00
/**
* Determines if primitives in this collection will be shown.
2020-04-17 08:31:36 +08:00
*
* @type {boolean}
* @default true
2020-04-17 08:31:36 +08:00
*/
2025-03-04 03:03:53 +08:00
this.show = options.show ?? true;
2020-04-17 08:31:36 +08:00
/**
* 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
2020-04-17 08:31:36 +08:00
*
* @example
2014-05-15 04:44:43 +08:00
* // Example 1. Primitives are destroyed by default.
* const primitives = new Cesium.PrimitiveCollection();
* const labels = primitives.add(new Cesium.LabelCollection());
* primitives = primitives.destroy();
* const b = labels.isDestroyed(); // true
2020-04-17 08:31:36 +08:00
*
* @example
2014-05-15 04:44:43 +08:00
* // Example 2. Do not destroy primitives in a collection.
* const primitives = new Cesium.PrimitiveCollection();
* primitives.destroyPrimitives = false;
* const labels = primitives.add(new Cesium.LabelCollection());
* primitives = primitives.destroy();
* const b = labels.isDestroyed(); // false
* labels = labels.destroy(); // explicitly destroy
*/
2025-07-12 03:55:14 +08:00
this.destroyPrimitives = options.destroyPrimitives ?? true;
2025-07-12 03:55:14 +08:00
this._countReferences = options.countReferences ?? false;
2020-04-17 08:31:36 +08:00
}
Object.defineProperties(PrimitiveCollection.prototype, {
/**
2014-05-15 04:44:43 +08:00
* Gets the number of primitives in the collection.
2020-04-17 08:31:36 +08:00
*
2014-05-15 04:44:43 +08:00
* @memberof PrimitiveCollection.prototype
2020-04-17 08:31:36 +08:00
*
* @type {number}
2014-05-15 04:44:43 +08:00
* @readonly
2020-04-17 08:31:36 +08:00
*/
length: {
2014-05-15 04:44:43 +08:00
get: function () {
return this._primitives.length;
2020-04-17 08:31:36 +08:00
},
},
2023-09-21 11:55:33 +08:00
/**
* An event that is raised when a primitive is added to the collection.
* Event handlers are passed the primitive that was added.
* @memberof PrimitiveCollection.prototype
* @type {Event}
* @readonly
*/
primitiveAdded: {
get: function () {
return this._primitiveAdded;
},
},
/**
* An event that is raised when a primitive is removed from the collection.
* Event handlers are passed the primitive that was removed.
* <p>
* Note: Depending on the destroyPrimitives constructor option, the primitive may already be destroyed.
* </p>
2023-09-21 11:55:33 +08:00
* @memberof PrimitiveCollection.prototype
* @type {Event}
* @readonly
*/
primitiveRemoved: {
get: function () {
return this._primitiveRemoved;
},
},
2020-04-17 08:31:36 +08:00
});
/**
2014-05-15 04:44:43 +08:00
* Adds a primitive to the collection.
2020-04-17 08:31:36 +08:00
*
* @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.
2020-04-17 08:31:36 +08:00
*
* @example
* const billboards = scene.primitives.add(new Cesium.BillboardCollection());
2014-05-15 04:44:43 +08:00
*/
PrimitiveCollection.prototype.add = function (primitive, index) {
const hasIndex = defined(index);
2020-04-17 08:31:36 +08:00
//>>includeStart('debug', pragmas.debug);
2014-05-15 04:44:43 +08:00
if (!defined(primitive)) {
throw new DeveloperError("primitive is required.");
2020-04-17 08:31:36 +08:00
}
if (hasIndex) {
if (index < 0) {
throw new DeveloperError("index must be greater than or equal to zero.");
} else if (index > this._primitives.length) {
2019-08-06 02:11:22 +08:00
throw new DeveloperError(
"index must be less than or equal to the number of primitives.",
2020-04-17 08:31:36 +08:00
);
}
2020-04-17 08:31:36 +08:00
}
//>>includeEnd('debug');
2020-04-17 08:31:36 +08:00
const external = (primitive._external = primitive._external || {});
const composites = (external._composites = external._composites || {});
composites[this._guid] = {
collection: this,
2014-05-15 04:44:43 +08:00
};
2020-04-17 08:31:36 +08:00
if (!hasIndex) {
this._primitives.push(primitive);
} else {
this._primitives.splice(index, 0, primitive);
}
2020-04-17 08:31:36 +08:00
if (this._countReferences) {
if (!defined(external._referenceCount)) {
external._referenceCount = 1;
} else {
++external._referenceCount;
}
}
2023-09-21 11:55:33 +08:00
this._primitiveAdded.raiseEvent(primitive);
2013-09-25 23:33:35 +08:00
return primitive;
};
2020-04-17 08:31:36 +08:00
/**
2014-05-15 04:44:43 +08:00
* Removes a primitive from the collection.
*
* @param {object} [primitive] The primitive to remove.
* @returns {boolean} <code>true</code> if the primitive was removed; <code>false</code> if the primitive is <code>undefined</code> or was not found in the collection.
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2020-04-17 08:31:36 +08:00
*
*
* @example
* const billboards = scene.primitives.add(new Cesium.BillboardCollection());
* scene.primitives.remove(billboards); // Returns true
2020-04-17 08:31:36 +08:00
*
* @see PrimitiveCollection#destroyPrimitives
*/
PrimitiveCollection.prototype.remove = function (primitive) {
// PERFORMANCE_IDEA: We can obviously make this a lot faster.
if (this.contains(primitive)) {
const index = this._primitives.indexOf(primitive);
if (index !== -1) {
this._primitives.splice(index, 1);
2020-04-17 08:31:36 +08:00
delete primitive._external._composites[this._guid];
if (this._countReferences) {
primitive._external._referenceCount--;
}
2020-04-17 08:31:36 +08:00
if (
this.destroyPrimitives &&
(!this._countReferences || primitive._external._referenceCount <= 0)
) {
2014-05-15 04:44:43 +08:00
primitive.destroy();
}
2020-04-17 08:31:36 +08:00
2023-09-21 11:55:33 +08:00
this._primitiveRemoved.raiseEvent(primitive);
return true;
}
2014-05-15 04:44:43 +08:00
// else ... this is not possible, I swear.
2020-04-17 08:31:36 +08:00
}
2014-05-15 04:44:43 +08:00
return false;
2020-04-17 08:31:36 +08:00
};
/**
2025-07-12 03:55:14 +08:00
* Removes and destroys a primitive, regardless of destroyPrimitives or countReferences setting.
2014-05-15 04:44:43 +08:00
* @private
*/
PrimitiveCollection.prototype.removeAndDestroy = function (primitive) {
const removed = this.remove(primitive);
if (removed && !this.destroyPrimitives) {
primitive.destroy();
2014-05-15 04:44:43 +08:00
}
return removed;
2020-04-17 08:31:36 +08:00
};
/**
2014-05-15 04:44:43 +08:00
* Removes all primitives in the collection.
2020-04-17 08:31:36 +08:00
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
*
* @see PrimitiveCollection#destroyPrimitives
*/
PrimitiveCollection.prototype.removeAll = function () {
const primitives = this._primitives;
const length = primitives.length;
for (let i = 0; i < length; ++i) {
const primitive = primitives[i];
delete primitive._external._composites[this._guid];
if (this._countReferences) {
primitive._external._referenceCount--;
}
if (
this.destroyPrimitives &&
(!this._countReferences || primitive._external._referenceCount <= 0)
) {
primitive.destroy();
2020-04-17 08:31:36 +08:00
}
this._primitiveRemoved.raiseEvent(primitive);
2020-04-17 08:31:36 +08:00
}
this._primitives = [];
2020-04-17 08:31:36 +08:00
};
/**
* Determines if this collection contains a primitive.
2020-04-17 08:31:36 +08:00
*
* @param {object} [primitive] The primitive to check for.
* @returns {boolean} <code>true</code> if the primitive is in the collection; <code>false</code> if the primitive is <code>undefined</code> or was not found in the collection.
2014-05-15 04:44:43 +08:00
*
* @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]
);
2020-04-17 08:31:36 +08:00
};
function getPrimitiveIndex(compositePrimitive, primitive) {
//>>includeStart('debug', pragmas.debug);
if (!compositePrimitive.contains(primitive)) {
throw new DeveloperError("primitive is not in this collection.");
2020-04-17 08:31:36 +08:00
}
//>>includeEnd('debug');
2020-04-17 08:31:36 +08:00
return compositePrimitive._primitives.indexOf(primitive);
}
2020-04-17 08:31:36 +08:00
/**
2014-05-15 04:44:43 +08:00
* 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.
2014-05-15 04:44:43 +08:00
*
* @exception {DeveloperError} primitive is not in this collection.
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2020-04-17 08:31:36 +08:00
*
* @see PrimitiveCollection#raiseToTop
* @see PrimitiveCollection#lower
* @see PrimitiveCollection#lowerToBottom
*/
PrimitiveCollection.prototype.raise = function (primitive) {
if (defined(primitive)) {
const index = getPrimitiveIndex(this, primitive);
const primitives = this._primitives;
2020-04-17 08:31:36 +08:00
if (index !== primitives.length - 1) {
const p = primitives[index];
primitives[index] = primitives[index + 1];
primitives[index + 1] = p;
2020-04-17 08:31:36 +08:00
}
}
};
2020-04-17 08:31:36 +08:00
/**
2014-05-15 04:44:43 +08:00
* 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.
2014-05-15 04:44:43 +08:00
*
* @exception {DeveloperError} primitive is not in this collection.
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2020-04-17 08:31:36 +08:00
*
* @see PrimitiveCollection#raise
* @see PrimitiveCollection#lower
* @see PrimitiveCollection#lowerToBottom
*/
PrimitiveCollection.prototype.raiseToTop = function (primitive) {
if (defined(primitive)) {
const index = getPrimitiveIndex(this, primitive);
const primitives = this._primitives;
2020-04-17 08:31:36 +08:00
if (index !== primitives.length - 1) {
// PERFORMANCE_IDEA: Could be faster
primitives.splice(index, 1);
primitives.push(primitive);
2020-04-17 08:31:36 +08:00
}
}
};
2020-04-17 08:31:36 +08:00
/**
* 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.
2020-04-17 08:31:36 +08:00
*
2014-05-15 04:44:43 +08:00
* @exception {DeveloperError} primitive is not in this collection.
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2020-04-17 08:31:36 +08:00
*
2014-05-15 04:44:43 +08:00
* @see PrimitiveCollection#lowerToBottom
* @see PrimitiveCollection#raise
* @see PrimitiveCollection#raiseToTop
2020-04-17 08:31:36 +08:00
*/
PrimitiveCollection.prototype.lower = function (primitive) {
if (defined(primitive)) {
2014-05-15 04:44:43 +08:00
const index = getPrimitiveIndex(this, primitive);
const primitives = this._primitives;
2020-04-17 08:31:36 +08:00
if (index !== 0) {
2019-04-26 05:34:18 +08:00
const p = primitives[index];
2014-05-15 04:44:43 +08:00
primitives[index] = primitives[index - 1];
primitives[index - 1] = p;
2020-04-17 08:31:36 +08:00
}
}
};
/**
2014-05-15 04:44:43 +08:00
* 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.
2020-04-17 08:31:36 +08:00
*
* @param {object} [primitive] The primitive to lower to the bottom.
2020-04-17 08:31:36 +08:00
*
2014-05-15 04:44:43 +08:00
* @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)) {
const index = getPrimitiveIndex(this, primitive);
const primitives = this._primitives;
2020-04-17 08:31:36 +08:00
if (index !== 0) {
// PERFORMANCE_IDEA: Could be faster
primitives.splice(index, 1);
primitives.unshift(primitive);
2020-04-17 08:31:36 +08:00
}
}
};
/**
* Returns the primitive in the collection at the specified index.
2020-04-17 08:31:36 +08:00
*
* @param {number} index The zero-based index of the primitive to return.
* @returns {object} The primitive at the <code>index</code>.
2020-04-17 08:31:36 +08:00
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2012-09-06 05:41:17 +08:00
*
2020-04-17 08:31:36 +08:00
*
2012-09-06 05:41:17 +08:00
* @example
2015-10-24 07:20:40 +08:00
* // Toggle the show property of every primitive in the collection.
* const primitives = scene.primitives;
* const length = primitives.length;
* for (let i = 0; i < length; ++i) {
* const p = primitives.get(i);
* p.show = !p.show;
2020-04-17 08:31:36 +08:00
* }
*
* @see PrimitiveCollection#length
2020-04-17 08:31:36 +08:00
*/
PrimitiveCollection.prototype.get = function (index) {
2015-10-24 07:20:40 +08:00
//>>includeStart('debug', pragmas.debug);
if (!defined(index)) {
throw new DeveloperError("index is required.");
2020-04-17 08:31:36 +08:00
}
2013-12-31 04:37:32 +08:00
//>>includeEnd('debug');
2020-04-17 08:31:36 +08:00
2015-10-24 07:20:40 +08:00
return this._primitives[index];
};
2020-04-17 08:31:36 +08:00
2019-04-26 05:34:18 +08:00
/**
* @private
*/
PrimitiveCollection.prototype.update = function (frameState) {
2012-09-06 05:41:17 +08:00
if (!this.show) {
2019-04-26 05:34:18 +08:00
return;
2020-04-17 08:31:36 +08:00
}
2019-04-26 05:34:18 +08:00
const 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 (let i = 0; i < primitives.length; ++i) {
primitives[i].update(frameState);
}
};
2020-04-17 08:31:36 +08:00
2019-04-26 05:34:18 +08:00
/**
* @private
*/
PrimitiveCollection.prototype.prePassesUpdate = function (frameState) {
const 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 (let i = 0; i < primitives.length; ++i) {
const primitive = primitives[i];
2019-04-26 21:16:51 +08:00
if (defined(primitive.prePassesUpdate)) {
2019-04-26 05:34:18 +08:00
primitive.prePassesUpdate(frameState);
}
}
};
2020-04-17 08:31:36 +08:00
2019-04-26 05:34:18 +08:00
/**
* @private
*/
PrimitiveCollection.prototype.updateForPass = function (frameState, passState) {
const primitives = this._primitives;
2014-07-04 20:15:22 +08:00
// 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.
2014-05-15 04:44:43 +08:00
for (let i = 0; i < primitives.length; ++i) {
2019-04-26 05:34:18 +08:00
const primitive = primitives[i];
2019-04-26 21:16:51 +08:00
if (defined(primitive.updateForPass)) {
primitive.updateForPass(frameState, passState);
2020-04-17 08:31:36 +08:00
}
}
};
/**
* @private
2020-04-17 08:31:36 +08:00
*/
PrimitiveCollection.prototype.postPassesUpdate = function (frameState) {
const primitives = this._primitives;
2019-04-26 05:34:18 +08:00
// 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 (let i = 0; i < primitives.length; ++i) {
const primitive = primitives[i];
2019-04-26 21:16:51 +08:00
if (defined(primitive.postPassesUpdate)) {
2019-04-26 05:34:18 +08:00
primitive.postPassesUpdate(frameState);
}
2020-04-17 08:31:36 +08:00
}
2019-04-26 05:34:18 +08:00
};
2020-04-17 08:31:36 +08:00
/**
* Returns true if this object was destroyed; otherwise, false.
* <br /><br />
* If this object was destroyed, it should not be used; calling any function other than
* <code>isDestroyed</code> 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;
};
2020-04-17 08:31:36 +08:00
/**
2014-05-15 04:44:43 +08:00
* 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.
* <br /><br />
2014-05-15 04:44:43 +08:00
* 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.
* <br /><br />
2014-05-15 04:44:43 +08:00
* Once this collection is destroyed, it should not be used; calling any function other than
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
* assign the return value (<code>undefined</code>) to the object as done in the example.
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
*
2020-04-17 08:31:36 +08:00
*
* @example
* primitives = primitives && primitives.destroy();
*
* @see PrimitiveCollection#isDestroyed
*/
PrimitiveCollection.prototype.destroy = function () {
this.removeAll();
return destroyObject(this);
};
Migrate Cesium to ES6 Modules See https://github.com/AnalyticalGraphicsInc/cesium/pull/8224 for details. eslint There are a handful of new .eslintrc.json files, mostly to identify the files that are still AMD modules (Sandcastle/Workers). These are needed because you can't change the parser type with a comment directive (since the parser is the thing reading the file). We can finally detect unusued modules! So those have all been cleaned up as well. requirejs -> rollup & clean-css requirejs, almond, and karma-requirejs have all been removed. We now use rollup for building and minifying (via uglify) JS code and clean-css for css. These changes are fairly straight-forward and just involve calling rollup instead of requirejs in the build process. Overall build time is significantly faster. CI is ~11 minutes compared to ~17 in master. Running makeZipFile on my machine takes 69 seconds compared to 112 seconds in master. There's probably plenty of room for additional optimization here too. We wrote an published a small npm module, rollup-plugin-strip-pragma, for stripping the requirejs pragmas we use out of the release builds. This is maintained in the Tools/rollup-plugin-strip-pragma directory. As for what we produce. The built version of Cesium is now a UMD module. So it should work anywhere that hasn't made the jump to ES6 yet. For users that were already using the "legacy" combined/minified approach, nothing changes. One awesome thing about roll-up is that it compiles all of the workers at once and automatically detects shared codes and generates separate bundles under the hood. This means the size of our worker modules shrink dramatically and Cesium itself will load them much faster. The total minified/gzipped size of all workers in master is 2.6 MB compared to 225 KB in this branch! This should be most noticeable on demos like Geometry & Appearances which load lots of workers for the various geometry typs. roll-up is also used to build Cesium Viewer, which is now an ES6 app. We use clean-css via gulp and it is also a straightforward change from requirejs that requires no special mention. Workers While the spec allows for ES6 Web Workers, no browser actually supports them yet. That means we needed a way to get our workers into non-ES6 form. Thankfully, roll-up can generate AMD modules, which means we now have a build step to compile our Worker source code back into AMD and use the existing TaskProcessor to load and execute them. This build step is part of the standard build task and is called createWorkers. During development, these "built" workers are un-optimized so you can still debug them and read the code. Since there is a build step, that means if you are changing code that affects a worker, you need to re-run build, or you can use the build-watch task to do it automatically. The ES6 versions of Worker code has moved into Source/WorkersES6 and we build the workers into their "old home" of Source/Workers. cesiumWorkerBootstrapper and transferTypedArrayTest which were already non-AMD ES5 scripts remain living in the Workers directory. Surprisingly little was changed about TaskProcessor or the worker system in general, especially considering that I thought this would be one of the major hurdles. ThirdParty A lot of our ThirdParty either already had a hand-written wrapper for AMD (which I updated to ES6) or had UMD which created problems when importing the same code in both Node and the browser. I basically had to update the wrapper of every third-party library to fix these problems. In some cases I updated the library version itself (Autolinker, topojson). Nothing to be too concerned about, but future clean-up would be using npm versions of these libraries and auto-generating the wrappers as needed so we don't hand-edit things. Sandcastle Sandcastle is eternal and manages to live another day in it's ancient requirejs/dojo 1.x form. Sandcastle now automatically uses the ES6 version of Cesium if it is available and fallsback to the ES5 unminified version if it is now. The built version of Sandcastle always uses CesiumUnminified, just like master. This means Sandcastle still works in IE11 if you run the combine step first (or use the relase zip) Removed Cesium usage from Sandcastle proper, since it wasn't really needed Generate a VERSION propertyin the gallery index since Cesium is no longer being included. Remove requirejs from Sandcastle bucket Update bucket to use the built version of Cesium if it is available by fallbackto the ES6 version during development. Standalone.html was also updated There's a bit of room for further clean-up here, but I think this gets us into master. I did not rename bucket-requirejs.html because I'm pretty sure it would break previously shared demos. We can put in some backwards compatible code later on if we want. (But I'd rather just see a full Sandcastle rewrite). Specs Specs are now all ES6, except for TestWorkers, which remain standard JS worker modules. This means you can no longer run the unbuilt unit tests in IE11. No changes for Chrome and Firefox. Since the specs use ES6 modules and built Cesium is an ES5 UMD, I added a build-specs build step which generates a combined ES5 version of the specs which rely on Cesium as a global variable. We then inject these files into jasmine instead of the standard specs and everything works exactly as it did before. SpecRunner.html has been updated to inject the correct version of the script depending on the build/release query parameters. The Specs must always use Cesium by importing Source/Cesium.js, this is so we can replace it with the built Cesium as describe above. There's a bunch of room for clean-up here, such as unifying our two copies of jasmine into a single helper file, but I didn't want to start doing that clean-up as part of this already overly big PR. The important thing is that we can still test the built version and still test on IE/Edge as needed. I also found and fixed two bugs that were causing failing unit tests, one in BingMapsImageryProviderSpec.js (which was overwriting createImage andnot setting it back) and ShadowVolumeAppearance.js (which had a module level caching bug). I think these may have been the cause of random CI failures in master as well, but only time will tell. For coverage, we had to switch to karma-coverage-istanbul-instrumenter for native ES6 support, but that's it. Finally, I updated appveryor to build Cesium and run the built tests under IE. We still don't fail the build for IE, but we should probably fix that if we want to keep it going. NodeJS When NODE_ENV is production, we now require in the minified CesiumJS directly, which works great because it's now a UMD module. Otherwise, we use the excellant esmpackage to load individual modules, it was a fairly straightforward swap from our old requirejs usage. We could probably drop esm too if we don't care about debugging or if we provie source maps at some point.
2019-10-03 23:51:23 +08:00
export default PrimitiveCollection;