Source: particleSystem/Emitter.js

/**
 * Emits particles to the particle system.
 *
 * @param  {!Object} options Setup properties.
 * @param  {!Grape2D.Vector} options.position Position of the emitter,
 *   and initial position of the particles.
 * @param  {!Grape2D.Vector} options.velocity Initial velocity of the
 *   particles.The length of the vector indicated its speed. The angle
 *   indicates the angle.
 * @param  {!number=} options.speedVariation Variation of the length of
 *   the velocity vector. So that particles created have a speed
 *   between <code>velocity.length-speedVariation</code> and
 *   <code>velocity.length+speedVariation</code>.
 * @param  {!number=} options.spread Spread of particles relative to the
 * velocity angle. It should be in radius.
 * @param  {!number} options.particleLife Life of a particle in milliseconds.
 * @param  {!number=} options.particleLifeVariation Life variation of
 *   particles created, relative to the life property.
 * @param  {!number=} options.maxParticles Maximum number of particles
 *   that this emitter can have, dead or alive. Because the particles
 *   are instantiated at construction time.
 * @param  {!number=} options.rate Rate of particles emitted, per second.
 *
 * @constructor
 */
Grape2D.Emitter = function(options) {
	/**
	 * Position of the emitter and initial position of newly created or
	 *   revived particles.
	 *
	 * @type {!Grape2D.Vector}
	 * @private
	 */
	this.position = options.position;
	/**
	 * Initial velocity of the particles.
	 *
	 * @type {!Grape2D.Vector}
	 * @private
	 */
	this.velocity = options.velocity;
	/**
	 * Cached value, of the velocity angle.
	 *
	 * @type {!number}
	 * @private
	 */
	this.vAngle = this.velocity.getAngle();
	/**
	 * Cached value, of the magnitude of the velocity.
	 *
	 * @type {!number}
	 * @private
	 */
	this.vMagnitude = this.velocity.getMagnitude();
	/**
	 * Speed variation of created or revived particles.
	 *
	 * @type {!number}
	 * @private
	 */
	this.speedVariation = options.speedVariation || 0;
	/**
	 * Spread of the particles, in relation to the velocity vector.
	 *
	 * @type {!number}
	 * @private
	 */
	this.spread = options.spread || 0;
	/**
	 * Life of particles.
	 *
	 * @type {!number}
	 * @private
	 */
	this.particleLife = options.particleLife || 200;
	/**
	 * Life variation of particles.
	 *
	 * @type {!number}
	 * @private
	 */
	this.particleLifeVariation = options.particleLifeVariation || 0;
	/**
	 * Emitter maximum particles.
	 *
	 * @type {!number}
	 * @private
	 */
	this.maxParticles = options.maxParticles;
	/**
	 * Rate of particles to emit per second.
	 *
	 * @type {!number}
	 * @private
	 */
	this.rate = options.rate || 60;
	/**
	 * Rate of particles to emit per millisecond.
	 *
	 * @type {!number}
	 * @private
	 */
	this.mrate = this.rate / 1000;
	/**
	 * Particles of the emitter.
	 *
	 * @type {!Array.<!Grape2D.Particle>}
	 * @private
	 */
	this.particles = [];
	this.createParticles();
	/**
	 * Particle system associated with this emitter.
	 *
	 * @type {Grape2D.ParticleSystem}
	 * @private
	 */
	this.particleSystem = null;
	/**
	 * Number of particle rendered after each render cycle.
	 *
	 * @type {!number}
	 * @private
	 */
	this.renderedParticles = 0;
};

Grape2D.Emitter.prototype = {
	constructor: Grape2D.Emitter,
	/**
	 * Gets the position of the emitter.
	 *
	 * @return {!Grape2D.Vector} Position of the emitter.
	 * @public
	 */
	getPosition: function() {
		return this.position;
	},
	/**
	 * Sets the position of the emitter.
	 *
	 * @param  {!Grape2D.Vector} position Position of the emitter.
	 * @public
	 */
	setPosition: function(position) {
		this.position.set(position);
	},
	/**
	 * Gets the velocity of the emitter.
	 *
	 * @return {!Grape2D.Vector} Velocity of the emitter.
	 * @public
	 */
	getVelocity: function() {
		return this.velocity;
	},
	/**
	 * Sets the velocity of the emitter.
	 *
	 * @param  {!Grape2D.Vector} velocity Velocity of the emitter.
	 * @public
	 */
	setVelocity: function(velocity) {
		this.velocity.set(velocity);
	},
	/**
	 * Gets the speed variation of the created particles.
	 *
	 * @return {!number} Speed variation.
	 * @public
	 */
	getSpeedVariation: function() {
		return this.speedVariation;
	},
	/**
	 * Sets the speed variation of the created particles.
	 *
	 * @param  {!number} speedVariation Speed variation.
	 * @public
	 */
	setSpeedVariation: function(speedVariation) {
		this.speedVariation = speedVariation;
	},
	/**
	 * Gets the spread of the created particles, relative to the
	 *   velocity vector angle.
	 *
	 * @return {!number} Spread of the created particles.
	 * @public
	 */
	getSpread: function() {
		return this.spread;
	},
	/**
	 * Sets the spread of the created particles, relative to the
	 *   velocity vector angle.
	 *
	 * @param  {!number} spread Spread of the created particles.
	 * @public
	 */
	setSpread: function(spread) {
		this.spread = spread;
	},
	/**
	 * Gets the particle life of the created particles.
	 *
	 * @return {!number} Particle life.
	 * @public
	 */
	getParticleLife: function() {
		return this.particleLife;
	},
	/**
	 * Sets the particle life of the created particles.
	 *
	 * @param  {!number} particleLife Particle life.
	 * @public
	 */
	setParticleLife: function(particleLife) {
		this.particleLife = particleLife;
	},
	/**
	 * Gets the particle life variation of the created particles,
	 *   relative to the particle life.
	 *
	 * @return {!number} Particle life variation.
	 * @public
	 */
	getParticleLifeVariation: function() {
		return this.particleLifeVariation;
	},
	/**
	 * Sets the particle life variation of the created particles,
	 *   relative to the particle life.
	 *
	 * @param  {!number} particleLifeVariation Particle life variation.
	 * @public
	 */
	setParticleLifeVariation: function(particleLifeVariation) {
		this.particleLifeVariation = particleLifeVariation;
	},
	/**
	 * Gets the rate that the emitter emits particles.
	 *
	 * @return {!number} Emission rate.
	 * @public
	 */
	getRate: function() {
		return this.rate;
	},
	/**
	 * Sets the rate that the emitter emits particles.
	 *
	 * @param  {!number} rate Emission rate.
	 * @public
	 */
	setRate: function(rate) {
		this.rate = rate;
	},
	/**
	 * Gets the particles associated with this emitter, they can be
	 *   either dead or alive.
	 *
	 * @return {!Array.<!Grape2D.Particle>} Particles.
	 * @public
	 */
	getParticles: function() {
		return this.particles;
	},
	/**
	 * Gets the particle system associated with this emitter.
	 *
	 * @return {?Grape2D.ParticleSystem} Particle system.
	 * @public
	 */
	getParticleSystem: function() {
		return this.particleSystem;
	},
	/**
	 * Sets the particle system associated with this emitter.
	 *
	 * @param  {!Grape2D.ParticleSystem} ps Particle system.
	 * @public
	 */
	setParticleSystem: function(ps) {
		this.particleSystem = ps;
	},
	/**
	 * Gets the number of particles rendered in the last call to the
	 *   {@link Grape2D.Emitter.render} method.
	 *
	 * @return {!number} Number of particles rendered.
	 */
	getRenderedParticles: function(){
		return this.renderedParticles;
	},
	/**
	 * Create particles. Up to the maximum number of particles of the
	 *   emitter.
	 *
	 * @protected
	 */
	createParticles: function() {
		for (var i = 0; i < this.maxParticles; i++) {
			this.particles.push(new Grape2D.Particle({
				position: this.position.clone(),
				velocity: Grape2D.Vector.createFromAngle(
					this.vAngle + Grape2D.Math.randFloat(-this.spread, this.spread),
					this.vMagnitude + Grape2D.Math.randFloat(-this.speedVariation, this.speedVariation)),
				lifeTime: -1
			}));
		}
	},
	/**
	 * Revives a particle according to the emitter properties.
	 *
	 * @param  {!Grape2D.Particle} particle Particle to revive.
	 * @protected
	 */
	reviveParticle: function(particle) {
		particle.revive(
			this.position,
			Grape2D.Vector.createFromAngle(this.vAngle + Grape2D.Math.randFloat(-this.spread, this.spread), this.vMagnitude + Grape2D.Math.randFloat(-this.speedVariation, this.speedVariation)),
			this.particleLife + Grape2D.Math.randInt(-this.particleLifeVariation, this.particleLifeVariation));

	},
	/**
	 * Updates an emitter. If there are particles dead, it revives them
	 *   according to the rate specified.
	 *   The emitter should have a particle system associated, if this
	 *   method is to be called, it could be done through <code>
	 *   emitter.setParticleSystem(particleSystem);</code>
	 *
	 * @param  {!number} dt Time elapsed since the last update.
	 * @param  {!Grape2D.Scene} scene Scene of the emitter.
	 * @public
	 */
	update: function(dt, scene) {
		var dead = [],
			rate = Grape2D.Math.floor(this.mrate * dt),
			fields = this.particleSystem.getFields(),
			particle;
		for (var i = 0; i < this.particles.length; i++) {
			particle = this.particles[i];
			if (particle.isDead()) {
				if (rate>=0) {
					this.reviveParticle(particle);
					rate--;
					this.particleSystem.submitParticle(particle);
				}
			} else {
				particle.submitToFields(fields);
				particle.update(dt, scene);
				this.particleSystem.submitParticle(particle);
			}
		}
	},
	/**
	 * Renders the particles into a renderer.
	 *
	 * @param  {!Grape2D.Renderer} renderer Renderer.
	 * @param  {!Grape2D.Camera} camera Camera.
	 * @public
	 */
	render: function(renderer, camera) {
		this.renderedParticles = 0;
		for (var i = 0; i < this.particles.length; i++) {
			if (this.particles[i].isAlive()) {
				this.particles[i].render(renderer, camera);
				this.renderedParticles++;
			}
		}
	}
};