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

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

886 lines
28 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 Cartesian2 from "../Core/Cartesian2.js";
import Cartesian3 from "../Core/Cartesian3.js";
import Check from "../Core/Check.js";
import Color from "../Core/Color.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 Event from "../Core/Event.js";
import JulianDate from "../Core/JulianDate.js";
import CesiumMath from "../Core/Math.js";
import Matrix4 from "../Core/Matrix4.js";
import BillboardCollection from "./BillboardCollection.js";
import CircleEmitter from "./CircleEmitter.js";
import Particle from "./Particle.js";
2020-04-17 08:31:36 +08:00
2018-04-05 23:06:51 +08:00
const defaultImageSize = new Cartesian2(1.0, 1.0);
2020-04-17 08:31:36 +08:00
/**
2018-04-13 04:20:56 +08:00
* A ParticleSystem manages the updating and display of a collection of particles.
2020-04-17 08:31:36 +08:00
*
2019-10-25 22:23:04 +08:00
* @alias ParticleSystem
* @constructor
2020-04-17 08:31:36 +08:00
*
* @param {object} [options] Object with the following properties:
* @param {boolean} [options.show=true] Whether to display the particle system.
Generate official TypeScript type definitions It's been a long requested feature for us to have official TypeScript type definitions. While the community has done a yeoman's job of manually supporting various efforts, the most recent incarnation of which is `@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code base makes manual maintenance a Sisyphean task. Thankfully, our decision to maintain meticulous JSDoc API documentation continues to pay dividends and is what makes automatically generating TypeScript definitions possible. Using the excellent https://github.com/englercj/tsd-jsdoc project we can now automatically generate and even partially validate official definitions as part of the build process. (Thanks to @bampakoa who contributed some early PRs to both CesiumJS and tsd-jsdoc over a year ago and is how I learned about tsd-jsdoc) While tsd-jsdoc output is mostly where we need it to be, we do post-processing on it as well. This lets us clean up the output and also make sure these definitions work whether users include cesium via module, i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e. `'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR into that project from us. The post-processing is part typescript compiler API, part string manipulation. It works and is straightforward but we might want to go full TS api in the future if we decide we need to do more complicated tasks. The output of tsd-jsdoc is currently a little noisy because of some incorrect error reporting, but I'm talking with the maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them fixed. No need to hold up this PR for it though. The definition is generated as a single `Cesium.d.ts` file in Source, so it lives alongside Cesium.js. It is ignored by git but generated by a separate `build-ts` task as part of CI and makeZipFile. This task also validates the file by compiling it with TypeScript, so if a developer does anything too egregious, the build will fail. Definitions are automatically included in our npm packages and release zips and will be automatically used by IDEs thanks to the `types` setting in package.json. This means that IDEs such as VS Code will prefer these types over the existing `@types/cesium` version by default. I didn't want to slow the `build` step down, so I made this a separate step, but in the future we could consider running it by default and we could also unignore this file in Git so that PR reviewers can see the impact, if any, our code changes have on the generated definitions. This might be a good idea as an additional sanity check and should only actually change when the public API itself changes. But the issue would be remembering to run it before submitting the code (or we could use git hooks I suppose?) I just don't want to slow down devs so I'm hesitant to do anything like this out of the gate. We can definitely revisit in the future. A particular exciting thing about this approach is that it exposed a ton of badness in our current JSDoc markup, which is now fixed. Previously, our only concern was "does the doc look right" and we didn't pay attention to whether the meta information generated by JSDoc correctly captured type information (because up until it didn't matter). We leaned particular hard on `@exports` which acted as a catch-all but has now been completely removed from the codebase. All this means is that our documentation as a whole has now improved greatly and will continue to be maintained at this new higher level thanks to incorporating TS definition creation into our pipeline! One minor caveat here is that obviously we changed our JSDoc usage to both make it correct and also accommodate TypeScript. The main drawback to these fixes is that enums are now generated as globals in the doc, rather than modules. This means they no longer have their own dedicated page and are instead on the globals page, but I changed the code to ensure they are still in the table of contents that we generate. I think this trade-off is perfectly fine, but I wanted to mention it since it does change the doc some. We can certainly look into whether we can generate enums on their own page if we think that makes sense. (I actually like this approach a little better personally). Last major piece, the actual code. 99% of the changes in this PR only affect the JSDoc. There are two exceptions: A few of our enums also have private functions tacked onto them. I had to move these functions to be outside the initializer but otherwise they are unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm? We had a few places where we had method signatures with optional parameters that came _before_ required parameters, which is silly. This is invalid TypeScript (and not good API design no matter the language). In 99% of cases this was `equalsEpsilon` style functions where the lhs/rhs were optional but the epsilon was not. I remember the discussion around this when we first did it because we were paranoid about defaulting to 0, but it's an edge case and it's silly so I just changed the epsilon functions to default to zero now, problem solved. Here's a high level summary of the JS changes: * Use proper `@enum` notation instead of `@exports` for enums. * Use proper `@namespace` instead of `@exports` for static classes. * Use proper `@function` instead of `@exports` for standalone functions. * Fix `Promise` markup to actually include the type in all cases, i.e. `Promise` => `Promise<void>` or `Promise<Cartesian3[]>`. * Fix bad markup that referenced types that do not exist (or no longer exist) at the spec level, `Image` => `HTMLImageElement`, `Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not exist and much be expressed as a lsit of all applicable types, `Int8Array|Uint8Array|Int16Array|Uint16Array...`. * Use dot notation instead of tilde in callbacks, to better support TypeScript, i.e. `EasingFunction~Callback` becomes `EasingFunction.Callback`. The only exception is when we had a callback type that was i.e. `exportKml~ModelCallback` becomes `exportKmlModelCallback` (a module global type rather than a type on exportKml). This is because it's not possible to have exportKml be both a function and a namespace in this current toolchain. Not a big deal either way since these are meta-types used for defining callbacks but I wanted to mention it. * There were some edge cases where private types that were referenced in the public API but don't exist in the JSDoc. These were trivial to fix by either tweaking the JSDoc to avoid leaking the type or in some cases, just as `PixelDataType`, simply exposing the private type as public. I also found a few cases where things were accidentally public, I marked these as private (these were extreme edge cases so I'm not concerned about breaking changes). Appearances took an optional `RenderState` in their options, I just changed the type to `Object` which we can clean up further later if we need to. * Lots of other little misc JSDoc issues that became obvious once we started to generate definitions (duplicate parameters for example). Thanks again to the community for helping generate ideas and discussions around TS definitions over the last few years and a big thanks to @javagl for helping behind the scenes on this specific effort by evaluating a few different approaches and workaround before we settled on this one (I'm working on a blog with all of the gory details for those interested). Finally, while I'm thrilled with how this turned out (all ~41000 lines and 1.9MB of it), I can guarantee we will uncover some issues with the type definitions as more people use it. The good news is that improving it is now just a matter of fixing the JSDoc, which will benefit the community as a whole and not just TS users. Fixes #2730 Fixes #5717
2020-05-27 10:40:05 +08:00
* @param {ParticleSystem.updateCallback} [options.updateCallback] The callback function to be called each frame to update a particle.
2017-06-16 05:04:17 +08:00
* @param {ParticleEmitter} [options.emitter=new CircleEmitter(0.5)] The particle emitter for this system.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the particle system from model to world coordinates.
* @param {Matrix4} [options.emitterModelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the particle system emitter within the particle systems local coordinate system.
* @param {number} [options.emissionRate=5] The number of particles to emit per second.
* @param {ParticleBurst[]} [options.bursts] An array of {@link ParticleBurst}, emitting bursts of particles at periodic times.
* @param {boolean} [options.loop=true] Whether the particle system should loop its bursts when it is complete.
* @param {number} [options.scale=1.0] Sets the scale to apply to the image of the particle for the duration of its particleLife.
* @param {number} [options.startScale] The initial scale to apply to the image of the particle at the beginning of its life.
* @param {number} [options.endScale] The final scale to apply to the image of the particle at the end of its life.
2018-04-13 04:20:56 +08:00
* @param {Color} [options.color=Color.WHITE] Sets the color of a particle for the duration of its particleLife.
* @param {Color} [options.startColor] The color of the particle at the beginning of its life.
* @param {Color} [options.endColor] The color of the particle at the end of its life.
* @param {object} [options.image] The URI, HTMLImageElement, or HTMLCanvasElement to use for the billboard.
2018-04-05 23:06:51 +08:00
* @param {Cartesian2} [options.imageSize=new Cartesian2(1.0, 1.0)] If set, overrides the minimumImageSize and maximumImageSize inputs that scale the particle image's dimensions in pixels.
* @param {Cartesian2} [options.minimumImageSize] Sets the minimum bound, width by height, above which to randomly scale the particle image's dimensions in pixels.
2018-04-13 04:20:56 +08:00
* @param {Cartesian2} [options.maximumImageSize] Sets the maximum bound, width by height, below which to randomly scale the particle image's dimensions in pixels.
* @param {boolean} [options.sizeInMeters] Sets if the size of particles is in meters or pixels. <code>true</code> to size the particles in meters; otherwise, the size is in pixels.
* @param {number} [options.speed=1.0] If set, overrides the minimumSpeed and maximumSpeed inputs with this value.
* @param {number} [options.minimumSpeed] Sets the minimum bound in meters per second above which a particle's actual speed will be randomly chosen.
* @param {number} [options.maximumSpeed] Sets the maximum bound in meters per second below which a particle's actual speed will be randomly chosen.
* @param {number} [options.lifetime=Number.MAX_VALUE] How long the particle system will emit particles, in seconds.
* @param {number} [options.particleLife=5.0] If set, overrides the minimumParticleLife and maximumParticleLife inputs with this value.
* @param {number} [options.minimumParticleLife] Sets the minimum bound in seconds for the possible duration of a particle's life above which a particle's actual life will be randomly chosen.
* @param {number} [options.maximumParticleLife] Sets the maximum bound in seconds for the possible duration of a particle's life below which a particle's actual life will be randomly chosen.
* @param {number} [options.mass=1.0] Sets the minimum and maximum mass of particles in kilograms.
* @param {number} [options.minimumMass] Sets the minimum bound for the mass of a particle in kilograms. A particle's actual mass will be chosen as a random amount above this value.
* @param {number} [options.maximumMass] Sets the maximum mass of particles in kilograms. A particle's actual mass will be chosen as a random amount below this value.
2021-07-02 02:32:03 +08:00
* @demo {@link https://cesium.com/learn/cesiumjs-learn/cesiumjs-particle-systems/|Particle Systems Tutorial}
2019-10-10 00:39:09 +08:00
* @demo {@link https://sandcastle.cesium.com/?src=Particle%20System.html&label=Showcases|Particle Systems Tutorial Demo}
* @demo {@link https://sandcastle.cesium.com/?src=Particle%20System%20Fireworks.html&label=Showcases|Particle Systems Fireworks Demo}
2020-04-17 08:31:36 +08:00
*/
function ParticleSystem(options) {
options = options ?? Frozen.EMPTY_OBJECT;
2020-04-17 08:31:36 +08:00
/**
* Whether to display the particle system.
* @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
/**
2018-04-13 04:20:56 +08:00
* An array of force callbacks. The callback is passed a {@link Particle} and the difference from the last time
Generate official TypeScript type definitions It's been a long requested feature for us to have official TypeScript type definitions. While the community has done a yeoman's job of manually supporting various efforts, the most recent incarnation of which is `@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code base makes manual maintenance a Sisyphean task. Thankfully, our decision to maintain meticulous JSDoc API documentation continues to pay dividends and is what makes automatically generating TypeScript definitions possible. Using the excellent https://github.com/englercj/tsd-jsdoc project we can now automatically generate and even partially validate official definitions as part of the build process. (Thanks to @bampakoa who contributed some early PRs to both CesiumJS and tsd-jsdoc over a year ago and is how I learned about tsd-jsdoc) While tsd-jsdoc output is mostly where we need it to be, we do post-processing on it as well. This lets us clean up the output and also make sure these definitions work whether users include cesium via module, i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e. `'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR into that project from us. The post-processing is part typescript compiler API, part string manipulation. It works and is straightforward but we might want to go full TS api in the future if we decide we need to do more complicated tasks. The output of tsd-jsdoc is currently a little noisy because of some incorrect error reporting, but I'm talking with the maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them fixed. No need to hold up this PR for it though. The definition is generated as a single `Cesium.d.ts` file in Source, so it lives alongside Cesium.js. It is ignored by git but generated by a separate `build-ts` task as part of CI and makeZipFile. This task also validates the file by compiling it with TypeScript, so if a developer does anything too egregious, the build will fail. Definitions are automatically included in our npm packages and release zips and will be automatically used by IDEs thanks to the `types` setting in package.json. This means that IDEs such as VS Code will prefer these types over the existing `@types/cesium` version by default. I didn't want to slow the `build` step down, so I made this a separate step, but in the future we could consider running it by default and we could also unignore this file in Git so that PR reviewers can see the impact, if any, our code changes have on the generated definitions. This might be a good idea as an additional sanity check and should only actually change when the public API itself changes. But the issue would be remembering to run it before submitting the code (or we could use git hooks I suppose?) I just don't want to slow down devs so I'm hesitant to do anything like this out of the gate. We can definitely revisit in the future. A particular exciting thing about this approach is that it exposed a ton of badness in our current JSDoc markup, which is now fixed. Previously, our only concern was "does the doc look right" and we didn't pay attention to whether the meta information generated by JSDoc correctly captured type information (because up until it didn't matter). We leaned particular hard on `@exports` which acted as a catch-all but has now been completely removed from the codebase. All this means is that our documentation as a whole has now improved greatly and will continue to be maintained at this new higher level thanks to incorporating TS definition creation into our pipeline! One minor caveat here is that obviously we changed our JSDoc usage to both make it correct and also accommodate TypeScript. The main drawback to these fixes is that enums are now generated as globals in the doc, rather than modules. This means they no longer have their own dedicated page and are instead on the globals page, but I changed the code to ensure they are still in the table of contents that we generate. I think this trade-off is perfectly fine, but I wanted to mention it since it does change the doc some. We can certainly look into whether we can generate enums on their own page if we think that makes sense. (I actually like this approach a little better personally). Last major piece, the actual code. 99% of the changes in this PR only affect the JSDoc. There are two exceptions: A few of our enums also have private functions tacked onto them. I had to move these functions to be outside the initializer but otherwise they are unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm? We had a few places where we had method signatures with optional parameters that came _before_ required parameters, which is silly. This is invalid TypeScript (and not good API design no matter the language). In 99% of cases this was `equalsEpsilon` style functions where the lhs/rhs were optional but the epsilon was not. I remember the discussion around this when we first did it because we were paranoid about defaulting to 0, but it's an edge case and it's silly so I just changed the epsilon functions to default to zero now, problem solved. Here's a high level summary of the JS changes: * Use proper `@enum` notation instead of `@exports` for enums. * Use proper `@namespace` instead of `@exports` for static classes. * Use proper `@function` instead of `@exports` for standalone functions. * Fix `Promise` markup to actually include the type in all cases, i.e. `Promise` => `Promise<void>` or `Promise<Cartesian3[]>`. * Fix bad markup that referenced types that do not exist (or no longer exist) at the spec level, `Image` => `HTMLImageElement`, `Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not exist and much be expressed as a lsit of all applicable types, `Int8Array|Uint8Array|Int16Array|Uint16Array...`. * Use dot notation instead of tilde in callbacks, to better support TypeScript, i.e. `EasingFunction~Callback` becomes `EasingFunction.Callback`. The only exception is when we had a callback type that was i.e. `exportKml~ModelCallback` becomes `exportKmlModelCallback` (a module global type rather than a type on exportKml). This is because it's not possible to have exportKml be both a function and a namespace in this current toolchain. Not a big deal either way since these are meta-types used for defining callbacks but I wanted to mention it. * There were some edge cases where private types that were referenced in the public API but don't exist in the JSDoc. These were trivial to fix by either tweaking the JSDoc to avoid leaking the type or in some cases, just as `PixelDataType`, simply exposing the private type as public. I also found a few cases where things were accidentally public, I marked these as private (these were extreme edge cases so I'm not concerned about breaking changes). Appearances took an optional `RenderState` in their options, I just changed the type to `Object` which we can clean up further later if we need to. * Lots of other little misc JSDoc issues that became obvious once we started to generate definitions (duplicate parameters for example). Thanks again to the community for helping generate ideas and discussions around TS definitions over the last few years and a big thanks to @javagl for helping behind the scenes on this specific effort by evaluating a few different approaches and workaround before we settled on this one (I'm working on a blog with all of the gory details for those interested). Finally, while I'm thrilled with how this turned out (all ~41000 lines and 1.9MB of it), I can guarantee we will uncover some issues with the type definitions as more people use it. The good news is that improving it is now just a matter of fixing the JSDoc, which will benefit the community as a whole and not just TS users. Fixes #2730 Fixes #5717
2020-05-27 10:40:05 +08:00
* @type {ParticleSystem.updateCallback}
2018-04-13 04:20:56 +08:00
* @default undefined
2020-04-17 08:31:36 +08:00
*/
2018-04-13 04:20:56 +08:00
this.updateCallback = options.updateCallback;
2020-04-17 08:31:36 +08:00
2017-06-16 05:04:17 +08:00
/**
* Whether the particle system should loop it's bursts when it is complete.
* @type {boolean}
* @default true
2020-04-17 08:31:36 +08:00
*/
2025-03-04 03:03:53 +08:00
this.loop = options.loop ?? true;
2020-04-17 08:31:36 +08:00
/**
* The URI, HTMLImageElement, or HTMLCanvasElement to use for the billboard.
* @type {object}
* @default undefined
2020-04-17 08:31:36 +08:00
*/
2025-03-04 03:03:53 +08:00
this.image = options.image ?? undefined;
2020-04-17 08:31:36 +08:00
let emitter = options.emitter;
if (!defined(emitter)) {
2017-06-16 04:29:52 +08:00
emitter = new CircleEmitter(0.5);
2020-04-17 08:31:36 +08:00
}
this._emitter = emitter;
2020-04-17 08:31:36 +08:00
this._bursts = options.bursts;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._modelMatrix = Matrix4.clone(options.modelMatrix ?? Matrix4.IDENTITY);
this._emitterModelMatrix = Matrix4.clone(
2025-03-04 03:03:53 +08:00
options.emitterModelMatrix ?? Matrix4.IDENTITY,
2020-04-17 08:31:36 +08:00
);
this._matrixDirty = true;
2018-04-05 23:06:51 +08:00
this._combinedMatrix = new Matrix4();
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._startColor = Color.clone(
2025-03-04 03:03:53 +08:00
options.color ?? options.startColor ?? Color.WHITE,
2020-04-17 08:31:36 +08:00
);
2018-04-13 04:20:56 +08:00
this._endColor = Color.clone(
2025-03-04 03:03:53 +08:00
options.color ?? options.endColor ?? Color.WHITE,
2020-04-17 08:31:36 +08:00
);
2025-03-04 03:03:53 +08:00
this._startScale = options.scale ?? options.startScale ?? 1.0;
this._endScale = options.scale ?? options.endScale ?? 1.0;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._emissionRate = options.emissionRate ?? 5.0;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._minimumSpeed = options.speed ?? options.minimumSpeed ?? 1.0;
this._maximumSpeed = options.speed ?? options.maximumSpeed ?? 1.0;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._minimumParticleLife =
options.particleLife ?? options.minimumParticleLife ?? 5.0;
this._maximumParticleLife =
options.particleLife ?? options.maximumParticleLife ?? 5.0;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._minimumMass = options.mass ?? options.minimumMass ?? 1.0;
this._maximumMass = options.mass ?? options.maximumMass ?? 1.0;
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._minimumImageSize = Cartesian2.clone(
2025-03-04 03:03:53 +08:00
options.imageSize ?? options.minimumImageSize ?? defaultImageSize,
2020-04-17 08:31:36 +08:00
);
2018-04-13 04:20:56 +08:00
this._maximumImageSize = Cartesian2.clone(
2025-03-04 03:03:53 +08:00
options.imageSize ?? options.maximumImageSize ?? defaultImageSize,
2020-04-17 08:31:36 +08:00
);
2025-03-04 03:03:53 +08:00
this._sizeInMeters = options.sizeInMeters ?? false;
2020-04-17 08:31:36 +08:00
2025-03-04 03:03:53 +08:00
this._lifetime = options.lifetime ?? Number.MAX_VALUE;
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._billboardCollection = undefined;
this._particles = [];
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
// An array of available particles that we can reuse instead of allocating new.
this._particlePool = [];
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._previousTime = undefined;
this._currentTime = 0.0;
this._carryOver = 0.0;
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._complete = new Event();
this._isComplete = false;
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
this._updateParticlePool = true;
this._particleEstimate = 0;
}
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
Object.defineProperties(ParticleSystem.prototype, {
2020-04-17 08:31:36 +08:00
/**
2018-04-13 04:20:56 +08:00
* The particle emitter for this
2018-04-05 23:06:51 +08:00
* @memberof ParticleSystem.prototype
* @type {ParticleEmitter}
* @default CircleEmitter
2020-04-17 08:31:36 +08:00
*/
emitter: {
get: function () {
return this._emitter;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
2018-04-13 04:20:56 +08:00
Check.defined("value", value);
//>>includeEnd('debug');
this._emitter = value;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-04-13 04:20:56 +08:00
* An array of {@link ParticleBurst}, emitting bursts of particles at periodic times.
* @memberof ParticleSystem.prototype
* @type {ParticleBurst[]}
* @default undefined
2020-04-17 08:31:36 +08:00
*/
2018-04-13 04:20:56 +08:00
bursts: {
get: function () {
return this._bursts;
2020-04-17 08:31:36 +08:00
},
2018-04-13 04:20:56 +08:00
set: function (value) {
this._bursts = value;
this._updateParticlePool = true;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-04-13 04:20:56 +08:00
* The 4x4 transformation matrix that transforms the particle system from model to world coordinates.
* @memberof ParticleSystem.prototype
* @type {Matrix4}
* @default Matrix4.IDENTITY
2020-04-17 08:31:36 +08:00
*/
modelMatrix: {
get: function () {
return this._modelMatrix;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.defined("value", value);
2018-04-13 04:20:56 +08:00
//>>includeEnd('debug');
this._matrixDirty =
2018-04-13 04:20:56 +08:00
this._matrixDirty || !Matrix4.equals(this._modelMatrix, value);
Matrix4.clone(value, this._modelMatrix);
2020-04-17 08:31:36 +08:00
},
},
/**
2018-04-13 04:20:56 +08:00
* The 4x4 transformation matrix that transforms the particle system emitter within the particle systems local coordinate system.
* @memberof ParticleSystem.prototype
* @type {Matrix4}
* @default Matrix4.IDENTITY
2020-04-17 08:31:36 +08:00
*/
2018-04-13 04:20:56 +08:00
emitterModelMatrix: {
get: function () {
2018-04-13 04:20:56 +08:00
return this._emitterModelMatrix;
2020-04-17 08:31:36 +08:00
},
2018-04-13 04:20:56 +08:00
set: function (value) {
2019-10-10 00:39:09 +08:00
//>>includeStart('debug', pragmas.debug);
Check.defined("value", value);
2019-10-10 00:39:09 +08:00
//>>includeEnd('debug');
this._matrixDirty =
2019-10-10 00:39:09 +08:00
this._matrixDirty || !Matrix4.equals(this._emitterModelMatrix, value);
Matrix4.clone(value, this._emitterModelMatrix);
2020-04-17 08:31:36 +08:00
},
},
/**
2019-10-10 00:39:09 +08:00
* The color of the particle at the beginning of its life.
* @memberof ParticleSystem.prototype
* @type {Color}
* @default Color.WHITE
*/
startColor: {
get: function () {
return this._startColor;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.defined("value", value);
//>>includeEnd('debug');
Color.clone(value, this._startColor);
2020-04-17 08:31:36 +08:00
},
},
/**
* The color of the particle at the end of its life.
* @memberof ParticleSystem.prototype
* @type {Color}
* @default Color.WHITE
*/
endColor: {
2018-04-05 23:06:51 +08:00
get: function () {
return this._endColor;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
2018-04-05 23:06:51 +08:00
//>>includeStart('debug', pragmas.debug);
Check.defined("value", value);
2018-04-05 23:06:51 +08:00
//>>includeEnd('debug');
Color.clone(value, this._endColor);
2020-04-17 08:31:36 +08:00
},
},
/**
* The initial scale to apply to the image of the particle at the beginning of its life.
* @memberof ParticleSystem.prototype
* @type {number}
* @default 1.0
2020-04-17 08:31:36 +08:00
*/
startScale: {
get: function () {
return this._startScale;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._startScale = value;
2020-04-17 08:31:36 +08:00
},
},
/**
* The final scale to apply to the image of the particle at the end of its life.
* @memberof ParticleSystem.prototype
* @type {number}
* @default 1.0
*/
endScale: {
get: function () {
return this._endScale;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._endScale = value;
2020-04-17 08:31:36 +08:00
},
},
/**
* The number of particles to emit per second.
* @memberof ParticleSystem.prototype
* @type {number}
* @default 5
*/
2018-04-05 23:06:51 +08:00
emissionRate: {
get: function () {
return this._emissionRate;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._emissionRate = value;
this._updateParticlePool = true;
2020-04-17 08:31:36 +08:00
},
},
/**
* Sets the minimum bound in meters per second above which a particle's actual speed will be randomly chosen.
* @memberof ParticleSystem.prototype
* @type {number}
* @default 1.0
*/
minimumSpeed: {
get: function () {
return this._minimumSpeed;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._minimumSpeed = value;
},
},
2020-04-17 08:31:36 +08:00
/**
* Sets the maximum bound in meters per second below which a particle's actual speed will be randomly chosen.
* @memberof ParticleSystem.prototype
* @type {number}
* @default 1.0
2020-04-17 08:31:36 +08:00
*/
maximumSpeed: {
get: function () {
return this._maximumSpeed;
2020-04-17 08:31:36 +08:00
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
2018-04-13 04:20:56 +08:00
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._maximumSpeed = value;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-04-13 04:20:56 +08:00
* Sets the minimum bound in seconds for the possible duration of a particle's life above which a particle's actual life will be randomly chosen.
* @memberof ParticleSystem.prototype
* @type {number}
2018-04-13 04:20:56 +08:00
* @default 5.0
2020-04-17 08:31:36 +08:00
*/
minimumParticleLife: {
2018-04-13 04:20:56 +08:00
get: function () {
return this._minimumParticleLife;
},
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._minimumParticleLife = value;
},
},
2020-04-17 08:31:36 +08:00
/**
2018-04-13 04:20:56 +08:00
* Sets the maximum bound in seconds for the possible duration of a particle's life below which a particle's actual life will be randomly chosen.
* @memberof ParticleSystem.prototype
* @type {number}
2018-04-13 04:20:56 +08:00
* @default 5.0
2020-04-17 08:31:36 +08:00
*/
2018-04-13 04:20:56 +08:00
maximumParticleLife: {
get: function () {
return this._maximumParticleLife;
2020-04-17 08:31:36 +08:00
},
2018-04-13 04:20:56 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._maximumParticleLife = value;
this._updateParticlePool = true;
},
},
2020-04-17 08:31:36 +08:00
/**
2018-04-13 04:20:56 +08:00
* Sets the minimum mass of particles in kilograms.
* @memberof ParticleSystem.prototype
* @type {number}
2018-04-13 04:20:56 +08:00
* @default 1.0
2020-04-17 08:31:36 +08:00
*/
2018-04-13 04:20:56 +08:00
minimumMass: {
get: function () {
return this._minimumMass;
2020-04-17 08:31:36 +08:00
},
2018-04-13 04:20:56 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
2018-04-05 23:06:51 +08:00
//>>includeEnd('debug');
2018-04-13 04:20:56 +08:00
this._minimumMass = value;
},
},
2020-04-17 08:31:36 +08:00
/**
2018-04-13 04:20:56 +08:00
* Sets the maximum mass of particles in kilograms.
* @memberof ParticleSystem.prototype
* @type {number}
2018-04-13 04:20:56 +08:00
* @default 1.0
2020-04-17 08:31:36 +08:00
*/
maximumMass: {
2018-04-13 04:20:56 +08:00
get: function () {
return this._maximumMass;
2020-04-17 08:31:36 +08:00
},
2018-04-13 04:20:56 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
2018-04-05 23:06:51 +08:00
//>>includeEnd('debug');
2018-04-13 04:20:56 +08:00
this._maximumMass = value;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-12-14 03:28:55 +08:00
* Sets the minimum bound, width by height, above which to randomly scale the particle image's dimensions in pixels.
* @memberof ParticleSystem.prototype
* @type {Cartesian2}
* @default new Cartesian2(1.0, 1.0)
2020-04-17 08:31:36 +08:00
*/
2018-12-14 03:28:55 +08:00
minimumImageSize: {
get: function () {
return this._minimumImageSize;
2020-04-17 08:31:36 +08:00
},
2018-12-14 03:28:55 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
2018-04-05 23:06:51 +08:00
Check.typeOf.object("value", value);
Check.typeOf.number.greaterThanOrEquals("value.x", value.x, 0.0);
Check.typeOf.number.greaterThanOrEquals("value.y", value.y, 0.0);
2018-12-14 03:28:55 +08:00
//>>includeEnd('debug');
this._minimumImageSize = value;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-12-14 03:37:39 +08:00
* Sets the maximum bound, width by height, below which to randomly scale the particle image's dimensions in pixels.
* @memberof ParticleSystem.prototype
* @type {Cartesian2}
* @default new Cartesian2(1.0, 1.0)
2020-04-17 08:31:36 +08:00
*/
2018-12-14 03:37:39 +08:00
maximumImageSize: {
get: function () {
return this._maximumImageSize;
2020-04-17 08:31:36 +08:00
},
2018-12-14 03:37:39 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
2018-04-05 23:06:51 +08:00
Check.typeOf.object("value", value);
Check.typeOf.number.greaterThanOrEquals("value.x", value.x, 0.0);
Check.typeOf.number.greaterThanOrEquals("value.y", value.y, 0.0);
//>>includeEnd('debug');
2018-12-14 03:37:39 +08:00
this._maximumImageSize = value;
2020-04-17 08:31:36 +08:00
},
},
/**
* Gets or sets if the particle size is in meters or pixels. <code>true</code> to size particles in meters; otherwise, the size is in pixels.
* @memberof ParticleSystem.prototype
* @type {boolean}
* @default false
2020-04-17 08:31:36 +08:00
*/
sizeInMeters: {
get: function () {
return this._sizeInMeters;
2020-04-17 08:31:36 +08:00
},
2018-04-05 23:06:51 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.bool("value", value);
2018-04-05 23:06:51 +08:00
//>>includeEnd('debug');
this._sizeInMeters = value;
2020-04-17 08:31:36 +08:00
},
},
/**
2018-04-05 23:06:51 +08:00
* How long the particle system will emit particles, in seconds.
* @memberof ParticleSystem.prototype
* @type {number}
2018-04-05 23:06:51 +08:00
* @default Number.MAX_VALUE
2020-04-17 08:31:36 +08:00
*/
2018-04-05 23:06:51 +08:00
lifetime: {
get: function () {
return this._lifetime;
2020-04-17 08:31:36 +08:00
},
2018-04-05 23:06:51 +08:00
set: function (value) {
//>>includeStart('debug', pragmas.debug);
2018-04-05 23:06:51 +08:00
Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
//>>includeEnd('debug');
this._lifetime = value;
2020-04-17 08:31:36 +08:00
},
},
/**
* Fires an event when the particle system has reached the end of its lifetime.
* @memberof ParticleSystem.prototype
* @type {Event}
2020-04-17 08:31:36 +08:00
*/
complete: {
get: function () {
return this._complete;
2020-04-17 08:31:36 +08:00
},
},
/**
* When <code>true</code>, the particle system has reached the end of its lifetime; <code>false</code> otherwise.
* @memberof ParticleSystem.prototype
* @type {boolean}
2020-04-17 08:31:36 +08:00
*/
isComplete: {
get: function () {
return this._isComplete;
2020-04-17 08:31:36 +08:00
},
},
2017-06-22 04:29:33 +08:00
});
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
function updateParticlePool(system) {
const emissionRate = system._emissionRate;
const life = system._maximumParticleLife;
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
let burstAmount = 0;
const bursts = system._bursts;
if (defined(bursts)) {
const length = bursts.length;
for (let i = 0; i < length; ++i) {
burstAmount += bursts[i].maximum;
}
}
2020-04-17 08:31:36 +08:00
const billboardCollection = system._billboardCollection;
const image = system.image;
2020-04-17 08:31:36 +08:00
2018-04-13 04:20:56 +08:00
const particleEstimate = Math.ceil(emissionRate * life + burstAmount);
2018-04-05 23:06:51 +08:00
const particles = system._particles;
2017-06-22 04:29:33 +08:00
const particlePool = system._particlePool;
2018-04-13 04:20:56 +08:00
const numToAdd = Math.max(
particleEstimate - particles.length - particlePool.length,
0,
);
2020-04-17 08:31:36 +08:00
2018-04-05 23:06:51 +08:00
for (let j = 0; j < numToAdd; ++j) {
2019-09-19 00:59:30 +08:00
const particle = new Particle();
2018-04-05 23:06:51 +08:00
particle._billboard = billboardCollection.add({
image: image,
// Make the newly added billboards invisible when updating the particle pool
// to prevent the billboards from being displayed when the particles
// are not created. The billboard will always be set visible in
// updateBillboard function when its corresponding particle update.
show: false,
});
particlePool.push(particle);
2020-04-17 08:31:36 +08:00
}
system._particleEstimate = particleEstimate;
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
function getOrCreateParticle(system) {
// Try to reuse an existing particle from the pool.
let particle = system._particlePool.pop();
if (!defined(particle)) {
// Create a new one
particle = new Particle();
2020-04-17 08:31:36 +08:00
}
return particle;
2020-04-17 08:31:36 +08:00
}
function addParticleToPool(system, particle) {
system._particlePool.push(particle);
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
function freeParticlePool(system) {
2018-04-05 23:06:51 +08:00
const particles = system._particles;
const particlePool = system._particlePool;
2017-06-22 04:29:33 +08:00
const billboardCollection = system._billboardCollection;
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
const numParticles = particles.length;
const numInPool = particlePool.length;
const estimate = system._particleEstimate;
2020-04-17 08:31:36 +08:00
2018-04-05 23:06:51 +08:00
const start = numInPool - Math.max(estimate - numParticles - numInPool, 0);
2017-06-22 04:29:33 +08:00
for (let i = start; i < numInPool; ++i) {
const p = particlePool[i];
billboardCollection.remove(p._billboard);
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
particlePool.length = start;
2020-04-17 08:31:36 +08:00
}
function removeBillboard(particle) {
2017-06-22 04:29:33 +08:00
if (defined(particle._billboard)) {
particle._billboard.show = false;
2020-04-17 08:31:36 +08:00
}
}
2017-06-22 04:29:33 +08:00
function updateBillboard(system, particle) {
let billboard = particle._billboard;
if (!defined(billboard)) {
2017-06-22 04:29:33 +08:00
billboard = particle._billboard = system._billboardCollection.add({
image: particle.image,
});
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
billboard.width = particle.imageSize.x;
billboard.height = particle.imageSize.y;
billboard.position = particle.position;
billboard.sizeInMeters = system.sizeInMeters;
billboard.show = true;
2020-04-17 08:31:36 +08:00
2017-03-08 02:36:07 +08:00
// Update the color
const r = CesiumMath.lerp(
particle.startColor.red,
2017-06-22 04:29:33 +08:00
particle.endColor.red,
particle.normalizedAge,
);
const g = CesiumMath.lerp(
2017-06-22 04:29:33 +08:00
particle.startColor.green,
particle.endColor.green,
2018-04-13 04:20:56 +08:00
particle.normalizedAge,
2017-06-22 04:29:33 +08:00
);
const b = CesiumMath.lerp(
2017-06-22 04:29:33 +08:00
particle.startColor.blue,
particle.endColor.blue,
2018-04-13 04:20:56 +08:00
particle.normalizedAge,
2020-04-17 08:31:36 +08:00
);
const a = CesiumMath.lerp(
2017-06-22 04:29:33 +08:00
particle.startColor.alpha,
particle.endColor.alpha,
particle.normalizedAge,
2020-04-17 08:31:36 +08:00
);
2017-06-22 04:29:33 +08:00
billboard.color = new Color(r, g, b, a);
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
// Update the scale
billboard.scale = CesiumMath.lerp(
particle.startScale,
2018-04-13 04:20:56 +08:00
particle.endScale,
particle.normalizedAge,
2020-04-17 08:31:36 +08:00
);
2017-06-22 04:29:33 +08:00
}
2020-04-17 08:31:36 +08:00
function addParticle(system, particle) {
particle.startColor = Color.clone(system._startColor, particle.startColor);
particle.endColor = Color.clone(system._endColor, particle.endColor);
particle.startScale = system._startScale;
particle.endScale = system._endScale;
particle.image = system.image;
particle.life = CesiumMath.randomBetween(
system._minimumParticleLife,
system._maximumParticleLife,
2020-04-17 08:31:36 +08:00
);
particle.mass = CesiumMath.randomBetween(
system._minimumMass,
system._maximumMass,
2020-04-17 08:31:36 +08:00
);
2018-04-05 23:06:51 +08:00
particle.imageSize.x = CesiumMath.randomBetween(
system._minimumImageSize.x,
system._maximumImageSize.x,
2020-04-17 08:31:36 +08:00
);
particle.imageSize.y = CesiumMath.randomBetween(
2018-04-05 23:06:51 +08:00
system._minimumImageSize.y,
system._maximumImageSize.y,
2020-04-17 08:31:36 +08:00
);
// Reset the normalizedAge and age in case the particle was reused.
particle._normalizedAge = 0.0;
particle._age = 0.0;
2020-04-17 08:31:36 +08:00
const speed = CesiumMath.randomBetween(
system._minimumSpeed,
system._maximumSpeed,
2020-04-17 08:31:36 +08:00
);
2017-03-10 01:37:22 +08:00
Cartesian3.multiplyByScalar(particle.velocity, speed, particle.velocity);
2020-04-17 08:31:36 +08:00
system._particles.push(particle);
2020-04-17 08:31:36 +08:00
}
function calculateNumberToEmit(system, dt) {
2017-03-10 02:50:06 +08:00
// This emitter is finished if it exceeds it's lifetime.
if (system._isComplete) {
2017-03-10 02:50:06 +08:00
return 0;
2020-04-17 08:31:36 +08:00
}
dt = CesiumMath.mod(dt, system._lifetime);
2020-04-17 08:31:36 +08:00
// Compute the number of particles to emit based on the emissionRate.
2018-04-05 23:06:51 +08:00
const v = dt * system._emissionRate;
2017-03-10 02:50:06 +08:00
let numToEmit = Math.floor(v);
system._carryOver += v - numToEmit;
if (system._carryOver > 1.0) {
2017-03-10 02:50:06 +08:00
numToEmit++;
system._carryOver -= 1.0;
2020-04-17 08:31:36 +08:00
}
2017-03-10 02:50:06 +08:00
// Apply any bursts
if (defined(system.bursts)) {
const length = system.bursts.length;
for (let i = 0; i < length; i++) {
const burst = system.bursts[i];
2017-06-21 08:47:10 +08:00
const currentTime = system._currentTime;
if (defined(burst) && !burst._complete && currentTime > burst.time) {
numToEmit += CesiumMath.randomBetween(burst.minimum, burst.maximum);
burst._complete = true;
2020-04-17 08:31:36 +08:00
}
}
2020-04-17 08:31:36 +08:00
}
return numToEmit;
2020-04-17 08:31:36 +08:00
}
const rotatedVelocityScratch = new Cartesian3();
2020-04-17 08:31:36 +08:00
/**
* @private
2020-04-17 08:31:36 +08:00
*/
ParticleSystem.prototype.update = function (frameState) {
if (!this.show) {
2020-04-17 08:31:36 +08:00
return;
}
if (!defined(this._billboardCollection)) {
this._billboardCollection = new BillboardCollection();
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
if (this._updateParticlePool) {
updateParticlePool(this);
this._updateParticlePool = false;
2020-04-17 08:31:36 +08:00
}
// Compute the frame time
let dt = 0.0;
if (this._previousTime) {
dt = JulianDate.secondsDifference(frameState.time, this._previousTime);
2020-04-17 08:31:36 +08:00
}
if (dt < 0.0) {
dt = 0.0;
2020-04-17 08:31:36 +08:00
}
const particles = this._particles;
const emitter = this._emitter;
const updateCallback = this.updateCallback;
2020-04-17 08:31:36 +08:00
let i;
let particle;
2020-04-17 08:31:36 +08:00
// update particles and remove dead particles
let length = particles.length;
2017-03-10 04:12:15 +08:00
for (i = 0; i < length; ++i) {
2017-04-15 04:30:59 +08:00
particle = particles[i];
if (!particle.update(dt, updateCallback)) {
removeBillboard(particle);
// Add the particle back to the pool so it can be reused.
addParticleToPool(this, particle);
particles[i] = particles[length - 1];
2020-04-17 08:31:36 +08:00
--i;
--length;
2020-04-17 08:31:36 +08:00
} else {
2017-06-22 04:29:33 +08:00
updateBillboard(this, particle);
}
2020-04-17 08:31:36 +08:00
}
2017-06-22 04:29:33 +08:00
particles.length = length;
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
const numToEmit = calculateNumberToEmit(this, dt);
2020-04-17 08:31:36 +08:00
2017-06-22 04:29:33 +08:00
if (numToEmit > 0 && defined(emitter)) {
// Compute the final model matrix by combining the particle systems model matrix and the emitter matrix.
if (this._matrixDirty) {
this._combinedMatrix = Matrix4.multiply(
this.modelMatrix,
this.emitterModelMatrix,
this._combinedMatrix,
);
2017-06-22 04:29:33 +08:00
this._matrixDirty = false;
}
const combinedMatrix = this._combinedMatrix;
2020-04-17 08:31:36 +08:00
for (i = 0; i < numToEmit; i++) {
// Create a new particle.
particle = getOrCreateParticle(this);
2020-04-17 08:31:36 +08:00
// Let the emitter initialize the particle.
this._emitter.emit(particle);
2020-04-17 08:31:36 +08:00
//For the velocity we need to add it to the original position and then multiply by point.
Cartesian3.add(
particle.position,
particle.velocity,
rotatedVelocityScratch,
2020-04-17 08:31:36 +08:00
);
Matrix4.multiplyByPoint(
combinedMatrix,
rotatedVelocityScratch,
2017-05-11 02:48:45 +08:00
rotatedVelocityScratch,
2020-04-17 08:31:36 +08:00
);
// Change the position to be in world coordinates
particle.position = Matrix4.multiplyByPoint(
combinedMatrix,
particle.position,
particle.position,
2020-04-17 08:31:36 +08:00
);
// Orient the velocity in world space as well.
Cartesian3.subtract(
rotatedVelocityScratch,
particle.position,
particle.velocity,
2020-04-17 08:31:36 +08:00
);
Cartesian3.normalize(particle.velocity, particle.velocity);
2020-04-17 08:31:36 +08:00
// Add the particle to the system.
addParticle(this, particle);
updateBillboard(this, particle);
}
2020-04-17 08:31:36 +08:00
}
this._billboardCollection.update(frameState);
this._previousTime = JulianDate.clone(frameState.time, this._previousTime);
2017-06-16 01:43:35 +08:00
this._currentTime += dt;
2020-04-17 08:31:36 +08:00
if (
2018-04-05 23:06:51 +08:00
this._lifetime !== Number.MAX_VALUE &&
this._currentTime > this._lifetime
2020-04-17 08:31:36 +08:00
) {
if (this.loop) {
this._currentTime = CesiumMath.mod(this._currentTime, this._lifetime);
2017-06-02 05:00:32 +08:00
if (this.bursts) {
const burstLength = this.bursts.length;
2017-06-02 05:00:32 +08:00
// Reset any bursts
for (i = 0; i < burstLength; i++) {
this.bursts[i]._complete = false;
}
2020-04-17 08:31:36 +08:00
}
} else {
this._isComplete = true;
this._complete.raiseEvent(this);
}
2020-04-17 08:31:36 +08:00
}
// free particles in the pool and release billboard GPU memory
if (frameState.frameNumber % 120 === 0) {
2017-03-10 01:37:22 +08:00
freeParticlePool(this);
}
2017-03-10 02:50:06 +08:00
};
2020-04-17 08:31:36 +08:00
2018-04-05 23:06:51 +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.
2020-04-17 08:31:36 +08:00
*
* @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
2020-04-17 08:31:36 +08:00
*
2017-05-11 02:48:45 +08:00
* @see ParticleSystem#destroy
*/
ParticleSystem.prototype.isDestroyed = function () {
2017-06-22 04:29:33 +08:00
return false;
};
2020-04-17 08:31:36 +08:00
/**
* 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.
* <br /><br />
* Once an object 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.
2020-04-17 08:31:36 +08:00
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
2020-04-17 08:31:36 +08:00
*
* @see ParticleSystem#isDestroyed
*/
ParticleSystem.prototype.destroy = function () {
2017-06-22 04:29:33 +08:00
this._billboardCollection =
this._billboardCollection && this._billboardCollection.destroy();
2018-04-05 23:06:51 +08:00
return destroyObject(this);
2017-06-02 05:00:32 +08:00
};
2020-04-17 08:31:36 +08:00
2017-06-02 05:00:32 +08:00
/**
2017-06-21 03:31:09 +08:00
* A function used to modify attributes of the particle at each time step. This can include force modifications,
* color, sizing, etc.
*
Generate official TypeScript type definitions It's been a long requested feature for us to have official TypeScript type definitions. While the community has done a yeoman's job of manually supporting various efforts, the most recent incarnation of which is `@types/cesium`, the sheer scale and ever-evolving nature of Cesium's code base makes manual maintenance a Sisyphean task. Thankfully, our decision to maintain meticulous JSDoc API documentation continues to pay dividends and is what makes automatically generating TypeScript definitions possible. Using the excellent https://github.com/englercj/tsd-jsdoc project we can now automatically generate and even partially validate official definitions as part of the build process. (Thanks to @bampakoa who contributed some early PRs to both CesiumJS and tsd-jsdoc over a year ago and is how I learned about tsd-jsdoc) While tsd-jsdoc output is mostly where we need it to be, we do post-processing on it as well. This lets us clean up the output and also make sure these definitions work whether users include cesium via module, i.e. `import { Cartesian3 } from 'cesium'`, or individual files, i.e. `'import Cartesian3 from 'cesium/Source/Core/Cartesian3'`. There were also some quirks of tsd-jsdoc output we fixed that may eventually turn into a PR into that project from us. The post-processing is part typescript compiler API, part string manipulation. It works and is straightforward but we might want to go full TS api in the future if we decide we need to do more complicated tasks. The output of tsd-jsdoc is currently a little noisy because of some incorrect error reporting, but I'm talking with the maintainer in https://github.com/englercj/tsd-jsdoc/issues/133 to get them fixed. No need to hold up this PR for it though. The definition is generated as a single `Cesium.d.ts` file in Source, so it lives alongside Cesium.js. It is ignored by git but generated by a separate `build-ts` task as part of CI and makeZipFile. This task also validates the file by compiling it with TypeScript, so if a developer does anything too egregious, the build will fail. Definitions are automatically included in our npm packages and release zips and will be automatically used by IDEs thanks to the `types` setting in package.json. This means that IDEs such as VS Code will prefer these types over the existing `@types/cesium` version by default. I didn't want to slow the `build` step down, so I made this a separate step, but in the future we could consider running it by default and we could also unignore this file in Git so that PR reviewers can see the impact, if any, our code changes have on the generated definitions. This might be a good idea as an additional sanity check and should only actually change when the public API itself changes. But the issue would be remembering to run it before submitting the code (or we could use git hooks I suppose?) I just don't want to slow down devs so I'm hesitant to do anything like this out of the gate. We can definitely revisit in the future. A particular exciting thing about this approach is that it exposed a ton of badness in our current JSDoc markup, which is now fixed. Previously, our only concern was "does the doc look right" and we didn't pay attention to whether the meta information generated by JSDoc correctly captured type information (because up until it didn't matter). We leaned particular hard on `@exports` which acted as a catch-all but has now been completely removed from the codebase. All this means is that our documentation as a whole has now improved greatly and will continue to be maintained at this new higher level thanks to incorporating TS definition creation into our pipeline! One minor caveat here is that obviously we changed our JSDoc usage to both make it correct and also accommodate TypeScript. The main drawback to these fixes is that enums are now generated as globals in the doc, rather than modules. This means they no longer have their own dedicated page and are instead on the globals page, but I changed the code to ensure they are still in the table of contents that we generate. I think this trade-off is perfectly fine, but I wanted to mention it since it does change the doc some. We can certainly look into whether we can generate enums on their own page if we think that makes sense. (I actually like this approach a little better personally). Last major piece, the actual code. 99% of the changes in this PR only affect the JSDoc. There are two exceptions: A few of our enums also have private functions tacked onto them. I had to move these functions to be outside the initializer but otherwise they are unchanged. This ensures that a valid TS enum is generated from our code, since you can't have functions globbed onto enums in the TS world. If we were writing TS code by hand, we could use declaration merging with a namespace, but the toolchain we are using doesn't have a way to express that right now. There were two cases where these extra functions weren't private, `ComponentDataType` and `IndexDataType`. That means that as far as the TS definitions goes, the helper methods don't exist. I consder this an edge case and we can write up issues to investigate later. I'm actually not even sure if these functions are public on purposes, @lilleyse can you confirm? We had a few places where we had method signatures with optional parameters that came _before_ required parameters, which is silly. This is invalid TypeScript (and not good API design no matter the language). In 99% of cases this was `equalsEpsilon` style functions where the lhs/rhs were optional but the epsilon was not. I remember the discussion around this when we first did it because we were paranoid about defaulting to 0, but it's an edge case and it's silly so I just changed the epsilon functions to default to zero now, problem solved. Here's a high level summary of the JS changes: * Use proper `@enum` notation instead of `@exports` for enums. * Use proper `@namespace` instead of `@exports` for static classes. * Use proper `@function` instead of `@exports` for standalone functions. * Fix `Promise` markup to actually include the type in all cases, i.e. `Promise` => `Promise<void>` or `Promise<Cartesian3[]>`. * Fix bad markup that referenced types that do not exist (or no longer exist) at the spec level, `Image` => `HTMLImageElement`, `Canvas` => `HTMLCanvasElement`, etc.. `TypedArray` in particular does not exist and much be expressed as a lsit of all applicable types, `Int8Array|Uint8Array|Int16Array|Uint16Array...`. * Use dot notation instead of tilde in callbacks, to better support TypeScript, i.e. `EasingFunction~Callback` becomes `EasingFunction.Callback`. The only exception is when we had a callback type that was i.e. `exportKml~ModelCallback` becomes `exportKmlModelCallback` (a module global type rather than a type on exportKml). This is because it's not possible to have exportKml be both a function and a namespace in this current toolchain. Not a big deal either way since these are meta-types used for defining callbacks but I wanted to mention it. * There were some edge cases where private types that were referenced in the public API but don't exist in the JSDoc. These were trivial to fix by either tweaking the JSDoc to avoid leaking the type or in some cases, just as `PixelDataType`, simply exposing the private type as public. I also found a few cases where things were accidentally public, I marked these as private (these were extreme edge cases so I'm not concerned about breaking changes). Appearances took an optional `RenderState` in their options, I just changed the type to `Object` which we can clean up further later if we need to. * Lots of other little misc JSDoc issues that became obvious once we started to generate definitions (duplicate parameters for example). Thanks again to the community for helping generate ideas and discussions around TS definitions over the last few years and a big thanks to @javagl for helping behind the scenes on this specific effort by evaluating a few different approaches and workaround before we settled on this one (I'm working on a blog with all of the gory details for those interested). Finally, while I'm thrilled with how this turned out (all ~41000 lines and 1.9MB of it), I can guarantee we will uncover some issues with the type definitions as more people use it. The good news is that improving it is now just a matter of fixing the JSDoc, which will benefit the community as a whole and not just TS users. Fixes #2730 Fixes #5717
2020-05-27 10:40:05 +08:00
* @callback ParticleSystem.updateCallback
*
* @param {Particle} particle The particle being updated.
* @param {number} dt The time in seconds since the last update.
2020-04-17 08:31:36 +08:00
*
* @example
* function applyGravity(particle, dt) {
* const position = particle.position;
* const gravityVector = Cesium.Cartesian3.normalize(position, new Cesium.Cartesian3());
* Cesium.Cartesian3.multiplyByScalar(gravityVector, GRAVITATIONAL_CONSTANT * dt, gravityVector);
* particle.velocity = Cesium.Cartesian3.add(particle.velocity, gravityVector, particle.velocity);
* }
*/
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 ParticleSystem;