core_Particle.js

const HashGridItem = require("./HashGridItem");
const Vector2D = require("../utils/Vector2D");
const SelfBehavior = require("../behaviors/SelfBehavior");
const NearBehavior = require("../behaviors/NearBehavior");

/**
 * `Particle` is the main object of this physics engine. It is a 2D circle that is treated like a point mass at the center
 * and does **not** rotate. `Particle` is also a `HashGridItem` so it can be added to a `SpatialHashGrid`.
 * @extends {HashGridItem}
 */

class Particle extends HashGridItem {
	/**
	 * Instantiates new `Particle`
	 * @param {Vector2D} pos cartesian coordinates of the particle
	 * @param {Vector2D} vel velocity of the particle
	 * @param {Number} mass 
	 * @param {Number} radius 
	 * @param {Number} bounciness a value in [0,1] that represents the amount of energy retained after collision
	 * @param {Number} charge similar to real physical charge
	 * @param {String} color currently only supports HTML canvas colors format
	 * @constructor
	 */
	constructor(pos, vel, mass, radius, bounciness = 1, charge = 0, color="black") {
        super();
		this.charge = charge || 0;
		this.pos = pos;
		this.vel = vel || new Vector2D(0,0);
        this.force = new Vector2D(0, 0);
        this.mass = mass || 1;
        this.originalMass = mass || 1;
		this.radius = radius || 10;
		this.bounciness = bounciness;
		this.prevPos = this.pos;
        this.color = color;
		this.nearBehavior = [];
		this.selfBehavior = [];
	}

	/**
	 * Increments the position by velocity `v`
	 * @param {Vector2D} v 
	 * @param {Number} timeStep 
	 * @public
	 */
	applyVelocity(v, timeStep) {
		this.pos.addTo(v.mult(timeStep));
	}

	/**
	 * Applies force `f` to the velocity
	 * @param {Vector2D} f 
	 * @param {Number} timeStep 
	 * @public
	 */
    applyForce(f, timeStep) {
		this.vel.addTo(f.mult(timeStep / this.mass));
	}

	/**
	 * Increments the velocity by an acceleration `a`
	 * @param {Vector2D} a 
	 * @param {Number} timeStep 
	 * @public
	 */
	applyAcceleration(a, timeStep) {
		this.vel.addTo(a.mult(timeStep));
	}

	/**
	 * Adds a `SelfBehavior` to the particle if not added already
	 * @param {SelfBehavior} b 
	 * @return {Boolean} true if successfully added
	 * @public
	 */
	addSelfBehavior(b) {
		if (!this.selfBehavior.includes(b)) {
			this.selfBehavior.push(b);
			return true;
		}
		return false;
	}

	/**
	 * Adds a `NearBehavior` to the particle if not added already
	 * @param {NearBehavior} b 
	 * @return {Boolean} true if successfully added
	 * @public
	 */
	addNearBehavior(b) {
		if (!this.nearBehavior.includes(b)) {
			this.nearBehavior.push(b);
			return true;
		}
		return false;
	}

	/**
	 * Removes `NearBehavior` `b` if the particle has `b`  
	 * @param {NearBehavior} b 
	 * @returns {Boolean} true if the action is successful
	 * @public
	 */
	removeNearBehavior(b) {
		const index = this.nearBehavior.indexOf(b);
		if (index > -1) {
			this.nearBehavior.splice(index, 1);
			return true;
		}
		return false;
	}

	/**
	 * Removes `SelfBehavior` `b` if the particle has `b`  
	 * @param {SelfBehavior} b 
	 * @returns {Boolean} true if the action is successful
	 * @public
	 */
	removeSelfBehavior(b) {
		const index = this.selfBehavior.indexOf(b);
		if (index > -1) {
			this.selfBehavior.splice(index, 1);
			return true;
		}
		return false;
	}

	/**
	 * Clears all behaviors of the particle
	 * @public
	 */
	clearBehaviors() {
		this.nearBehavior = [];
		this.selfBehavior = [];
	}

    /**
     * @override
     * @returns {Number[]} 
     */
	getHashPos() {
		return [this.pos.x, this.pos.y];
	}

    /**
     * @override
     * @returns {Number[]} 
     */
	getHashDimensions() {
		return [this.radius * 2, this.radius * 2];
	}
}

module.exports = Particle;