Source: objects/ParticleEmitter.js

import { ObjectProxy } from '@uino/base-thing';
import { Utils } from '../common/Utils'


const __ = {
	private: Symbol('private'),
}


/**
 * @class ParticleEmitter
 * The particle emitter.
 * @memberof THING
 * @public
 * @example
 * const emitter = new THING.ParticleEmitter();
 * emitter.particleCount = 100;
 * emitter.distribution = 1;
 * emitter.duration = 2;
 * emitter.isLookAtCamera = true;
 * emitter.isLookAtCameraY = true;
 * emitter.wiggle = {
 * 	spread:0,
 * 	value:0
 * };
 * emitter.rotation = {
 * 	axisSpread:[0, 0, 0],
 * 	randomise:false,
 * 	angleSpread:0,
 * 	center:[0, 0, 0],
 * 	angle:0,
 * 	static:false,
 * 	axis:[0, 1, 0]
 * };
 * emitter.velocity = {
 * 	randomise:false,
 * 	value:[0, 25, 0],
 * 	distribution:1,
 * 	spread:[10, 7.5, 10]
 * };
 * emitter.size = {
 * 	randomise:[false, false],
 * 	spread:[0, 0],
 * 	value:[2, 0]
 * };
 * emitter.angle = {
 * 	randomise:[false, false],
 * 	spread:[0, 0],
 * 	value:[1, 0]
 * };
 * emitter.opacity = {
 * 	randomise:[false, false],
 * 	spread:[0, 0],
 * 	value:[1, 0.1]
 * };
 * emitter.color = {
 * 	randomise:[false, false],
 * 	spread:[[0, 0, 0], [0, 0, 0]],
 * 	value:[[0, 0, 0], [1, 1, 0]]
 * };
 * emitter.acceleration = {
 * 	randomise:false,
 * 	value:[0, -10, 0],
 * 	distribution:1,
 * 	spread:[0, 0, 0]
 * };
 * emitter.maxAge = {
 * 	spread:0,
 * 	value:2
 * };
 * emitter.position = {
 *  randomise:false,
 *  radius:10,
 *  spreadClamp:[0, 0, 0],
 *  value:[0, 0, -50],
 *  radiusScale:[1, 1, 1],
 *  distribution:1,
 *  spread:[0, 0, 0]
 * };
 * emitter.drag = {
 *  randomise:false,
 *  spread:0,
 *  value:0
 * };
 */
class ParticleEmitter {

	constructor(param = {}) {
		this.node = param['node'] || Utils.createObject('ParticleEmitter');

		this[__.private] = {};
		let _private = this[__.private];

		_private.maxAge = this.node.getAttribute('MaxAge');

		_private.position = this.node.getAttribute('Position');
		_private.rotation = this.node.getAttribute('Rotation');
		_private.velocity = this.node.getAttribute('Velocity');

		_private.listColor = this.node.getAttribute('ListColor');

		_private.listOpacity = this.node.getAttribute('ListOpacity');
		_private.listSize = this.node.getAttribute('ListSize');
		_private.listAngle = this.node.getAttribute('ListAngle');

		_private.acceleration = this.node.getAttribute('Acceleration');

		_private.drag = this.node.getAttribute('Drag');

		_private.onChange = null;

		const that = this;
		_private.objectProxy = new ObjectProxy({
			data: {
				MaxAge: _private.maxAge,
				Position: _private.position,
				Rotation: _private.rotation,
				Velocity: _private.velocity,
				ListColor: _private.listColor,
				ListOpacity: _private.listOpacity,
				ListSize: _private.listSize,
				ListAngle: _private.listAngle,
				Acceleration: _private.acceleration,
				Drag: _private.drag,
			},
			onChange: function (ev) {
				that.setAttribute(ev.propName, ev.data[ev.propName]);

				if (_private.onChange) {
					_private.onChange(ev);
				}
			}
		});
	}

	/**
	 * Get attribute.
	 * @param {ParticleEmitterAttributeType} key The key
	 * @returns {any}
	 * @public
	 */
	getAttribute(key) {
		return this.node.getAttribute(key);
	}

	/**
	 * Set attribute.
	 * @param {ParticleEmitterAttributeType} key The key
	 * @param {any} value The value.
	 * @public
	 */
	setAttribute(key, value) {
		this.node.setAttribute(key, value);
	}

	// #region  Accessors

	/**
	 * Get/Set particle count.The count of particle when rendering.
	 * @type {Number}
	 * @public
	 */
	get particleCount() {
		return this.getAttribute('ParticleCount');
	}
	set particleCount(value) {
		this.setAttribute('ParticleCount', value);
	}

	/**
	 * Get/Set direction.The direction of the particle. If value is `1`, particle will start at beginning of particle's lifecycle.If value is `-1`, particle will start at end of particle's lifecycle and work it's way backwards.
	 * @type {Number}
	 * @public
	 */
	get direction() {
		return this.getAttribute('Direction');
	}
	set direction(value) {
		this.setAttribute('Direction', value);
	}

	/**
	 * Get/Set isStatic.True indicates these particles should be not be simulated.
	 * @type {Boolean}
	 * @public
	 */
	get isStatic() {
		return this.getAttribute('IsStatic');
	}
	set isStatic(value) {
		this.setAttribute('IsStatic', value);
	}

	/**
	 * Get/Set active multiplier.A value between 0 and 1 describing what percentage of this particle's particlesPerSecond should be emitted, where 0 is 0%, and 1 is 100%.
	 * @type {Number}
	 * @public
	 */
	get activeMultiplier() {
		return this.getAttribute('ActiveMultiplier');
	}
	set activeMultiplier(value) {
		this.setAttribute('ActiveMultiplier', value);
	}

	/**
	 * Get/Set distribution.It use to control its particle's spawn position and force behaviour.The options are 1(Box),2(Sphere),3(Disc),4(Line).
	 * @type {Number}
	 * @public
	 */
	get distribution() {
		return this.getAttribute('Distribution');
	}
	set distribution(value) {
		this.setAttribute('Distribution', value);
	}

	/**
	 * Get/Set duration.The duration in seconds that this emitter should live for.If not specified, the emitter will emit particles indefinitely.
	 * @type {Number|null}
	 * @public
	 */
	get duration() {
		return this.getAttribute('Duration');
	}
	set duration(value) {
		this.setAttribute('Duration', value);
	}


	/**
	 * Get/Set IsLookAtCamera.It takes effect when UseMesh is true.True indicates its particle will always look at camera.
	 * @type {Boolean}
	 * @public
	 */
	get isLookAtCamera() {
		return this.getAttribute('IsLookAtCamera');
	}
	set isLookAtCamera(value) {
		this.setAttribute('IsLookAtCamera', value);
	}

	/**
	 * Get/Set IsLookAtCamera.It takes effect when UseMesh is true.True indicates its particle will always look at camera and lock y-axis.
	 * @type {Boolean}
	 * @public
	 */
	get isLookAtCameraY() {
		return this.getAttribute('IsLookAtCameraY');
	}
	set isLookAtCameraY(value) {
		this.setAttribute('IsLookAtCameraY', value);
	}

	/**
	 * The particle's maximum age attribute.
	 * @typedef {Object} MaxAge
	 * @property {Number} value A number describing the amount of maxAge to apply to all particles.
	 * @property {Number} spread A number describing the maxAge variance on a per-particle basis.
	 */

	/**
	 * Get/Set maxAge attribute.
	 * @type {MaxAge}
	 * @public
	 */
	get maxAge() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.MaxAge;
	}
	set maxAge(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.maxAge[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's position attribute.
	 * @typedef {Object} Position
	 * @property {Array<Number>} value An array describing this emitter's base position.
	 * @property {Array<Number>} spread An array describing this emitter's position variance on a per-particle basis.
	 * @property {Array<Number>} spreadClamp An array describing the numeric multiples the particle's should be spread out over.
	 * @property {Number} radius This emitter's base radius.
	 * @property {Array<Number>} radiusScale An array describing the radius's scale in all three axes.
	 * @property {Number} distribution A specific distribution to use when radiusing particles. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's position should be re-randomised or not.
	*/

	/**
	 * Get position.
	 * @type {Position}
	 * @public
	 */
	get position() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Position;
	}
	set position(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.position[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's rotation attribute.
	 * @typedef {Object} Rotation
	 * @property {Array<Number>} axis An array describing this emitter's axis of rotation.
	 * @property {Array<Number>} axisSpread An array describing the amount of variance to apply to the axis of rotation on a per-particle basis.
	 * @property {Number} angle The angle of rotation, given in radians. If `Rotation.static` is true, the emitter will start off rotated at this angle, and stay as such.
	 *                          Otherwise, the particles will rotate from 0radians to this value over their lifetimes.
	 * @property {Number} angleSpread The amount of variance in each particle's rotation angle.
	 * @property {Boolean} static Whether the rotation should be static or not.
	 * @property {Array<Number>} center An array describing the center point of rotation.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's rotation should be re-randomised or not.
	 */

	/**
	 * Get rotation.
	 * @type {Rotation}
	 * @public
	 */
	get rotation() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Rotation;
	}
	set rotation(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.rotation[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's velocity attribute.
	 * @typedef {Object} Velocity
	 * @property {Array<Number>} value An array describing this emitter's base velocity.
	 * @property {Array<Number>} spread An array describing this emitter's velocity variance on a per-particle basis.
	 * @property {Number} distribution A specific distribution to use when calculating a particle's velocity. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's velocity should be re-randomised or not.
	*/

	/**
	 * Get velocity.
	 * @type {Velocity}
	 * @public
	 */
	get velocity() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Velocity;
	}
	set velocity(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.velocity[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's color attribute.
	 * @typedef {Object} ListColor
	 * @property {Array<Number>} value Either an array, or multiple arrays to describe the color of a particle over it's lifetime.
	 * @property {Array<Number>} spread Either an array, or multiple arrays to describe the color variance of a particle over it's lifetime.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's color should be re-randomised or not.
	*/

	/**
	 * Get color.
	 * @type {ListColor}
	 * @public
	 */
	get color() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListColor;
	}
	set color(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.color[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's opacity attribute.
	 * @typedef {Object} ListOpacity
	 * @property {Number} value Either a number, or an array of numbers to describe the opacity of a particle over it's lifetime.
	 * @property {Number} spread Either a number, or an array of numbers to describe the opacity variance of a particle over it's lifetime.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's opacity should be re-randomised or not.
	*/

	/**
	 * Get opacity.
	 * @type {ListOpacity}
	 * @public
	 */
	get opacity() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListOpacity;
	}
	set opacity(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.opacity[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's size attribute.
	 * @typedef {Object} ListSize
	 * @property {Number} value Either a number, or an array of numbers to describe the size of a particle over it's lifetime.
	 * @property {Number} spread Either a number, or an array of numbers to describe the size variance of a particle over it's lifetime.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's size should be re-randomised or not.
	*/

	/**
	 * Get size.
	 * @type {ListSize}
	 * @public
	 */
	get size() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListSize;
	}
	set size(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.size[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's angle attribute.
	 * @typedef {Object} ListAngle
	 * @property {Number} value Either a number, or an array of numbers to describe the angle of a particle over it's lifetime.
	 * @property {Number} spread Either a number, or an array of numbers to describe the angle variance of a particle over it's lifetime.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's angle should be re-randomised or not.
	*/

	/**
	 * Get angle.
	 * @type {ListAngle}
	 * @public
	 */
	get angle() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.ListAngle;
	}
	set angle(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.angle[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's acceleration attribute.
	 * @typedef {Object} Acceleration
	 * @property {Array<Number>} value An array describing this emitter's base acceleration.
	 * @property {Array<Number>} spread An array describing this emitter's acceleration variance on a per-particle basis.
	 * @property {Number} distribution A specific distribution to use when calculating a particle's acceleration. Overrides the `ParticleEmitterAttributeType.Distribution` option.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's acceleration should be re-randomised or not.
	*/

	/**
	 * Get acceleration.
	 * @type {Acceleration}
	 * @public
	 */
	get acceleration() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Acceleration;
	}
	set acceleration(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.acceleration[i] = value[i];
					break;
			}
		}
	}

	/**
	 * The emitter's drag attribute.
	 * @typedef {Object} Drag
	 * @property {Number} value A number between 0 and 1 describing the amount of drag to apply to all particles.
	 * @property {Number} spread A number describing the drag variance on a per-particle basis.
	 * @property {Boolean} randomise When a particle is re-spawned, whether it's drag should be re-randomised or not.
	*/

	/**
	 * Get drag attribute.
	 * @type {Drag}
	 * @public
	 */
	get drag() {
		let _private = this[__.private];

		return _private.objectProxy.dataProxy.Drag;
	}
	set drag(value) {
		for (let i in value) {
			switch (i) {
				default:
					this.drag[i] = value[i];
					break;
			}
		}
	}

	/**
	 * Get/Set when change callback function.
	 * @type {Function}
	 * @private
	 */
	get onChange() {
		let _private = this[__.private];

		return _private.onChange;
	}
	set onChange(value) {
		let _private = this[__.private];

		_private.onChange = value;
	}

	// #endregion

}


export { ParticleEmitter }