Source: particles/particle.js

/* 
 * File: particle.js
 * Defines a particle
 */
"use strict";

import * as loop from "../core/loop.js";
import * as particleSystem from "../components/particle_system.js";
import ParticleRenderable from "../renderables/particle_renderable.js";
import * as debugDraw from "../core/debug_draw.js";

let kSizeFactor = 0.2;

class Particle {
    /**
     * @classdesc Defines a square particle that has physics support. The size and color of a particle can change over a set lifetime.
     * <p>Found in Chapter 10, page 649 of the textbook</p>
     * Examples: 
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter10/10.1.particles/index.html 10.1 Simple Particles},
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter10/10.2.particle_collisions/index.html 10.2 Particle Collision},
     * {@link https://mylesacd.github.io/build-your-own-2d-game-engine-2e-doc/BookSourceCode/chapter10/10.3.particle_emitters/index.html 10.3 Particle Emitters}
     * 
     * 
     * @param {string} texture - path to the texture file for this Particle
     * @param {float} x - starting X world coordinate 
     * @param {float} y - starting Y world coordinate 
     * @param {integer} life - number of cycles this particle lives for
     * @returns {Particle} a new Particle instance
     */
    constructor(texture, x, y, life) {
        this.mRenderComponent = new ParticleRenderable(texture);
        this.setPosition(x, y);

        // position control
        this.mVelocity = vec2.fromValues(0, 0);
        this.mAcceleration = particleSystem.getSystemAcceleration();
        this.mDrag = 0.95;

        // Color control
        this.mDeltaColor = [0, 0, 0, 0];

        // Size control
        this.mSizeDelta = 0;

        // Life control
        this.mCyclesToLive = life;
    }

    /**
     * Draw a cross marker on this Particle for debugging
     * @method
     * @param {Camera} aCamera - the Camera to draw to
     */
    drawMarker(aCamera) {
        let size = this.getSize();
        debugDraw.drawCrossMarker(aCamera, this.getPosition(), size[0] * kSizeFactor, [0, 1, 0, 1]);
    }

    /**
     * Draw the ParticleRenderable of this Particle to the Camera
     * @method
     * @param {Camera} aCamera - the Camera to draw to
     */
    draw(aCamera) {
        this.mRenderComponent.draw(aCamera);
    }

    /**
     * Update the position, velocity, size, and color of this Particle
     * @method
     */
    update() {
        this.mCyclesToLive--;

        let dt = loop.getUpdateIntervalInSeconds();

        // Symplectic Euler
        //    v += a * dt
        //    x += v * dt
        let p = this.getPosition();
        vec2.scaleAndAdd(this.mVelocity, this.mVelocity, this.mAcceleration, dt);
        vec2.scale(this.mVelocity, this.mVelocity, this.mDrag);
        vec2.scaleAndAdd(p, p, this.mVelocity, dt);

        // update color
        let c = this.mRenderComponent.getColor();
        vec4.add(c, c, this.mDeltaColor);
    
        // update size
        let xf = this.mRenderComponent.getXform();
        let s = xf.getWidth() * this.mSizeDelta;
        xf.setSize(s, s);
    }

    /**
     * Set the final color for this Particle by changing the delta color accordingly
     * @method
     * @param {vec4} f - final target [R,G,B,A] color array 
     */
    setFinalColor = function(f) {    
        vec4.sub(this.mDeltaColor, f, this.getColor());
        if (this.mCyclesToLive !== 0) {
            vec4.scale(this.mDeltaColor, this.mDeltaColor, 1/this.mCyclesToLive);
        }
    }

    /**
     * Set the current color of this Particle
     * @method
     * @param {vec4} c - [R,G,B,A] color array
     */
    setColor(c) { this.mRenderComponent.setColor(c); }

    /**
     * Returns the current color of this particle
     * @method
     * @returns {vec4} [R,G,B,A] color array
     */
    getColor() { return this.mRenderComponent.getColor(); }

    /**
     * Returns whether the bounds are set to be drawn
     * @method
     * @returns {boolean} mDrawBounds - true if bounds are being drawn
     */
    getDrawBounds() { return this.mDrawBounds; }

    /**
     * Set whether the bounds of this Particle are drawn
     * @method
     * @param {boolean} d - true to draw bounds
     */
    setDrawBounds(d) { this.mDrawBounds = d; }

    /**
     * Returns the X,Y world coordinate position of this Particle
     * @method
     * @returns {vec2} current position vector
     */
    getPosition() { return this.mRenderComponent.getXform().getPosition(); }

    /**
     * Set the X,Y world coordinate position of this Particle
     * @method
     * @param {float} xPos - the new X position
     * @param {float} yPos - the new Y position
     */
    setPosition(xPos, yPos) { 
        this.mRenderComponent.getXform().setXPos(xPos); 
        this.mRenderComponent.getXform().setYPos(yPos); 
    }

    /**
     * Returns the current world coordinate size of this Particle
     * @method
     * @returns {float} the Particle's size
     */
    getSize() { return this.mRenderComponent.getXform().getSize(); }

    /**
     * Set the X and Y world coordinate size of this Particle
     * @method
     * @param {float} x - horizontal size
     * @param {float} y - vertical size
     */
    setSize(x, y) { this.mRenderComponent.getXform().setSize(x, y); }

    /**
     * Returns the X,Y world coordinate velocity vector of this Particle
     * @method
     * @returns {vec} mVelocity - velocity vector
     */
    getVelocity() { return this.mVelocity; }

    /**
     * Set the X,Y world coordinate velocity vector of this Particle
     * @method
     * @param {float} x - horizontal velocity 
     * @param {float} y - vertical velocity
     */
    setVelocity(x, y) { 
        this.mVelocity[0] = x;
        this.mVelocity[1] = y;
    }
    /**
     * Returns the X,Y world coordinate acceleration vector of this Particle
     * @method
     * @returns {vec2} mAcceleration - acceleration vector
     */
    getAcceleration() { return this.mAcceleration; }
    /**
     * Set the X,Y world coordinate acceleration vector of this Particle
     * @method
     * @param {float} x - horizontal acceleration
     * @param {float} y - vertical acceleration
     */
    setAcceleration(x, y) { 
        this.mAcceleration[0] = x;
        this.mAcceleration[1] = y;
    }

    /**
     * Set the velocity drag multiplier of this Particle
     * <p> On update calls the velocity is multiplied by this value</p>
     * @method
     * @param {float} d - drag multiplier value
     */
    setDrag(d) { this.mDrag = d; }
    /**
     * Returns the drag value of this Particle
     * @method
     * @returns {float} mDrag - drag value
     */
    getDrag() { return this.mDrag; }

    /**
     * Set the size changing factor
     * <p> On update calls the size is multiplied by this value</p>
     * @method
     * @param {float} d - multipier applied on every update
     */
    setSizeDelta(d) { this.mSizeDelta = d; }

    /**
     * Returns whether this Particle has reached the end of its lifetime
     * @method
     * @returns {boolean} true if lifetime has ended
     */
    hasExpired() { return (this.mCyclesToLive < 0); }
}

export default Particle;