Home Reference Source

src/behaviour/Behaviour.js

import { DEFAULT_BEHAVIOUR_EASING, DEFAULT_LIFE } from './constants';

import { BEHAVIOUR_TYPE_ABSTRACT } from './types';
import { MEASURE } from '../constants';
import isNumber from 'lodash/isNumber';
import { uid } from '../utils';

/**
 * The base behaviour class.
 * Behaviours manage a particle's behaviour after they have been emitted.
 *
 */
export default class Behaviour {
  /**
   * Constructs a Behaviour instance.
   *
   * @param {number} [life=Infinity] - The life of the behaviour
   * @param {function} [easing=DEFAULT_BEHAVIOUR_EASING] - The behaviour's decaying trend
   * @param {string} [type=BEHAVIOUR_TYPE_ABSTRACT] - The behaviour type
   * @param {boolean} [isEnabled=true] - Determines if the behaviour will be applied or not
   * @return void
   */
  constructor(
    life = Infinity,
    easing = DEFAULT_BEHAVIOUR_EASING,
    type = BEHAVIOUR_TYPE_ABSTRACT,
    isEnabled = true
  ) {
    /**
     * @desc The class type.
     * @type {string}
     */
    this.type = type;

    /**
     * @desc Determines if the behaviour will be applied or not
     * @type {boolean}
     */
    this.isEnabled = isEnabled;

    /**
     * @desc The behaviour's id
     * @type {string} id
     */
    this.id = `behaviour-${uid()}`;

    /**
     * @desc The life of the behaviour
     * @type {number}
     */
    this.life = life;

    /**
     * @desc The behaviour's decaying trend
     * @type {function}
     */
    this.easing = easing;

    /**
     * @desc The age of the behaviour
     * @type {number}
     */
    this.age = 0;

    /**
     * @desc The energy of the behaviour
     * @type {number}
     */
    this.energy = 1;

    /**
     * Determines if the behaviour is dead or not
     * @type {boolean}
     */
    this.dead = false;
  }

  /**
   * Reset this behaviour's parameters
   *
   * @param {number} [life=DEFAULT_LIFE] - The life of the behaviour
   * @param {function} [easing=DEFAULT_BEHAVIOUR_EASING] - The behaviour's decaying trend
   */
  reset(life = DEFAULT_LIFE, easing = DEFAULT_BEHAVIOUR_EASING) {
    this.life = life;
    this.easing = easing || DEFAULT_BEHAVIOUR_EASING;
  }

  /**
   * Ensures that life is infinity if an invalid value is supplied.
   *
   * @return void
   */
  set life(life) {
    this._life = isNumber(life) ? life : DEFAULT_LIFE;
  }

  /**
   * Gets the behaviour's life.
   *
   * @return {Number}
   */
  get life() {
    return this._life;
  }

  /**
   * Normalize a force by 1:100;
   *
   * @param {Vector3D} force - The force to normalize.
   * @return {Vector3D}
   */
  normalizeForce(force) {
    return force.scalar(MEASURE);
  }

  /**
   * Normalize a value by 1:100;
   *
   * @param {number} value - The value to normalize
   * @return {number}
   */
  normalizeValue(value) {
    return value * MEASURE;
  }

  /**
   * Set the behaviour's initial properties on the particle.
   *
   * @param {Particle} particle
   * @abstract
   */
  initialize(particle) {} // eslint-disable-line

  /**
   * Apply behaviour to the target as a factor of time.
   * Internally calls the mutate method to change properties on the target
   * Will not do so if the behaviour is disabled
   *
   * @abstract
   * @param {Particle|Emitter} target - The particle or emitter to apply the behaviour to
   * @param {Number} time - the system integration time
   * @param {integer} index - the target index
   * @return mixed
   */
  applyBehaviour(target, time, index) {
    if (!this.isEnabled) {
      return;
    }

    this.mutate(target, time, index);
  }

  /**
   * Change the target's properties according to specific behaviour logic.
   *
   * @abstract
   * @param {Particle|Emitter} target - The particle or emitter to apply the behaviour to
   * @param {Number} time - the system integration time
   * @return mixed
   */
  mutate(target, time, index) {} // eslint-disable-line

  /**
   * Compares the age of the behaviour vs integration time and determines
   * if the behaviour should be set to dead or not.
   * Sets the behaviour energy as a factor of particle age and life.
   *
   * @param {Particle} particle - The particle to apply the behaviour to
   * @param {Number} time - the system integration time
   * @return void
   */
  energize(particle, time) {
    if (this.dead) {
      return;
    }

    this.age += time;

    if (this.age >= this.life) {
      this.energy = 0;
      this.dead = true;

      return;
    }

    const scale = this.easing(particle.age / particle.life);

    this.energy = Math.max(1 - scale, 0);
  }

  /**
   * Destory this behaviour.
   *
   * @abstract
   */
  destroy() {}

  /**
   * Returns a new instance of the behaviour from the JSON object passed.
   *
   * @abstract
   * @param {object} json - JSON object containing the required constructor properties
   * @return {Behaviour}
   */
  fromJSON(json) {} // eslint-disable-line
}