Home Reference Source

src/behaviour/Attraction.js

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

import Behaviour from './Behaviour';
import { Vector3D } from '../math';
import { getEasingByName } from '../ease';
import { BEHAVIOUR_TYPE_ATTRACTION as type } from './types';

/**
 * Behaviour that causes particles to be attracted to a target position.
 *
 */
export default class Attraction extends Behaviour {
  /**
   * Constructs an Attraction behaviour instance.
   *
   * @param {Vector3D} targetPosition - The position the particles will be attracted to
   * @param {number} force - The attraction force scalar multiplier
   * @param {number} radius - The attraction radius
   * @param {number} [life=DEFAULT_LIFE] - The life of the particle
   * @param {function} [easing=DEFAULT_BEHAVIOUR_EASING] - The behaviour's decaying trend
   * @param {boolean} [isEnabled=true] - Determines if the behaviour will be applied or not
   * @return void
   */
  constructor(
    targetPosition = new Vector3D(),
    force = DEFAULT_ATTRACTION_FORCE_SCALAR,
    radius = DEFAULT_ATTRACITON_RADIUS,
    life = DEFAULT_LIFE,
    easing = DEFAULT_BEHAVIOUR_EASING,
    isEnabled = true
  ) {
    super(life, easing, type, isEnabled);

    /**
     * @desc The position the particles will be attracted to
     * @type {Vector3D}
     */
    this.targetPosition = targetPosition;

    /**
     * @desc The attraction radius
     * @type {number} - the attraction radius
     */
    this.radius = radius;

    /**
     * @desc The attraction force scalar multiplier
     * @type {number}
     */
    this.force = this.normalizeValue(force);

    /**
     * @desc The radius of the attraction squared
     * @type {number}
     */
    this.radiusSq = this.radius * this.radius;

    /**
     * @desc The attraction force in 3D space
     * @type {Vector3D}
     */
    this.attractionForce = new Vector3D();

    /**
     * @desc The linear attraction force
     * @type {number}
     */
    this.lengthSq = 0;
  }

  /**
   * Resets the behaviour properties.
   *
   * @param {Vector3D} targetPosition - the position the particles will be attracted to
   * @param {number} force - the attraction force multiplier
   * @param {number} radius - the attraction radius
   * @param {number} life - the life of the particle
   * @param {function} easing - The behaviour's decaying trend
   * @return void
   */
  reset(
    targetPosition = new Vector3D(),
    force = DEFAULT_ATTRACTION_FORCE_SCALAR,
    radius = DEFAULT_ATTRACITON_RADIUS,
    life,
    easing
  ) {
    this.targetPosition = targetPosition;
    this.radius = radius;
    this.force = this.normalizeValue(force);
    this.radiusSq = this.radius * this.radius;
    this.attractionForce = new Vector3D();
    this.lengthSq = 0;

    life && super.reset(life, easing);
  }

  /**
   * Mutates particle acceleration.
   *
   * @param {Particle} particle - the particle to apply the behaviour to
   * @param {number} time - particle engine time
   * @param {integer} index - the particle index
   * @return void
   */
  mutate(particle, time, index) {
    this.energize(particle, time, index);

    this.attractionForce.copy(this.targetPosition);
    this.attractionForce.sub(particle.position);

    this.lengthSq = this.attractionForce.lengthSq();

    if (
      this.lengthSq > PARTICLE_LENGTH_SQ_THRESHOLD &&
      this.lengthSq < this.radiusSq
    ) {
      this.attractionForce.normalize();
      this.attractionForce.scalar(1 - this.lengthSq / this.radiusSq);
      this.attractionForce.scalar(this.force);

      particle.acceleration.add(this.attractionForce);
    }
  }

  /**
   * Creates a Body initializer from JSON.
   *
   * @param {object} json - The JSON to construct the instance from.
   * @property {number} json.x - The target position x value
   * @property {number} json.y - The target position y value
   * @property {number} json.z - The target position z value
   * @property {number} json.force - The attraction force scalar multiplier
   * @property {number} json.life - The life of the particle
   * @property {string} json.easing - The behaviour's decaying trend
   * @return {Body}
   */
  static fromJSON(json) {
    const { x, y, z, force, radius, life, easing, isEnabled = true } = json;

    return new Attraction(
      new Vector3D(x, y, z),
      force,
      radius,
      life,
      getEasingByName(easing),
      isEnabled
    );
  }
}