text: /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   *
   * Owner: david@famo.us
   * @license MPL 2.0
   * @copyright Famous Industries, Inc. 2015
   */
  
      var Constraint = require('./Constraint');
      var Vector = require('../../math/Vector');
  
      /**
       *  A wall describes an infinite two-dimensional plane that physics bodies
       *    can collide with. To define a wall, you must give it a distance (from
       *    the center of the physics engine's origin, and a normal defining the plane
       *    of the wall.
       *
       *    (wall)
       *      |
       *      | (normal)     (origin)
       *      | --->            *
       *      |
       *      |    (distance)
       *      ...................
       *            (100px)
       *
       *      e.g., Wall({normal : [1,0,0], distance : 100})
       *      would be a wall 100 pixels to the left, whose normal points right
       *
       *  @class Wall
       *  @constructor
       *  @extends Constraint
       *  @param {Options} [options] An object of configurable options.
       *  @param {Number} [options.restitution] The energy ratio lost in a collision (0 = stick, 1 = elastic). Range : [0, 1]
       *  @param {Number} [options.drift] Baumgarte stabilization parameter. Makes constraints "loosely" (0) or "tightly" (1) enforced. Range : [0, 1]
       *  @param {Number} [options.slop] Amount of penetration in pixels to ignore before collision event triggers.
       *  @param {Array} [options.normal] The normal direction to the wall.
       *  @param {Number} [options.distance] The distance from the origin that the wall is placed.
       *  @param {onContact} [options.onContact] How to handle collision against the wall.
       *
       */
      function Wall(options) {
          this.options = Object.create(Wall.DEFAULT_OPTIONS);
          if (options) this.setOptions(options);
  
          //registers
          this.diff = new Vector();
          this.impulse = new Vector();
  
          Constraint.call(this);
      }
  
      Wall.prototype = Object.create(Constraint.prototype);
      Wall.prototype.constructor = Wall;
  
      /**
       * @property Wall.ON_CONTACT
       * @type Object
       * @protected
       * @static
       */
      Wall.ON_CONTACT = {
  
          /**
           * Physical bodies bounce off the wall
           * @attribute REFLECT
           */
          REFLECT : 0,
  
          /**
           * Physical bodies are unaffected. Usecase is to fire events on contact.
           * @attribute SILENT
           */
          SILENT : 1
      };
  
      Wall.DEFAULT_OPTIONS = {
          restitution : 0.5,
          drift : 0.5,
          slop : 0,
          normal : [1, 0, 0],
          distance : 0,
          onContact : Wall.ON_CONTACT.REFLECT
      };
  
      /*
       * Setter for options.
       *
       * @method setOptions
       * @param options {Objects}
       */
      Wall.prototype.setOptions = function setOptions(options) {
          if (options.normal !== undefined) {
              if (options.normal instanceof Vector) this.options.normal = options.normal.clone();
              if (options.normal instanceof Array)  this.options.normal = new Vector(options.normal);
          }
          if (options.restitution !== undefined) this.options.restitution = options.restitution;
          if (options.drift !== undefined) this.options.drift = options.drift;
          if (options.slop !== undefined) this.options.slop = options.slop;
          if (options.distance !== undefined) this.options.distance = options.distance;
          if (options.onContact !== undefined) this.options.onContact = options.onContact;
      };
  
      function _getNormalVelocity(n, v) {
          return v.dot(n);
      }
  
      function _getDistanceFromOrigin(p) {
          var n = this.options.normal;
          var d = this.options.distance;
          return p.dot(n) + d;
      }
  
      function _onEnter(particle, overlap, dt) {
          var p = particle.position;
          var v = particle.velocity;
          var m = particle.mass;
          var n = this.options.normal;
          var action = this.options.onContact;
          var restitution = this.options.restitution;
          var impulse = this.impulse;
  
          var drift = this.options.drift;
          var slop = -this.options.slop;
          var gamma = 0;
  
          if (this._eventOutput) {
              var data = {particle : particle, wall : this, overlap : overlap, normal : n};
              this._eventOutput.emit('preCollision', data);
              this._eventOutput.emit('collision', data);
          }
  
          switch (action) {
              case Wall.ON_CONTACT.REFLECT:
                  var lambda = (overlap < slop)
                      ? -((1 + restitution) * n.dot(v) + drift / dt * (overlap - slop)) / (m * dt + gamma)
                      : -((1 + restitution) * n.dot(v)) / (m * dt + gamma);
  
                  impulse.set(n.mult(dt * lambda));
                  particle.applyImpulse(impulse);
                  particle.setPosition(p.add(n.mult(-overlap)));
                  break;
          }
  
          if (this._eventOutput) this._eventOutput.emit('postCollision', data);
      }
  
      function _onExit(particle, overlap, dt) {
          var action = this.options.onContact;
          var p = particle.position;
          var n = this.options.normal;
  
          if (action === Wall.ON_CONTACT.REFLECT) {
              particle.setPosition(p.add(n.mult(-overlap)));
          }
      }
  
      /**
       * Adds an impulse to a physics body's velocity due to the wall constraint
       *
       * @method applyConstraint
       * @param targets {Array.Body}  Array of bodies to apply the constraint to
       * @param source {Body}         The source of the constraint
       * @param dt {Number}           Delta time
       */
      Wall.prototype.applyConstraint = function applyConstraint(targets, source, dt) {
          var n = this.options.normal;
  
          for (var i = 0; i < targets.length; i++) {
              var particle = targets[i];
              var p = particle.position;
              var v = particle.velocity;
              var r = particle.radius || 0;
  
              var overlap = _getDistanceFromOrigin.call(this, p.add(n.mult(-r)));
              var nv = _getNormalVelocity.call(this, n, v);
  
              if (overlap <= 0) {
                  if (nv < 0) _onEnter.call(this, particle, overlap, dt);
                  else _onExit.call(this, particle, overlap, dt);
              }
          }
      };
  
      module.exports = Wall;
  