text: /**
   * A small library for testing collisions between moving tokens in Roll20.
   */
  var TokenCollisions = (function() {
      /**
       * Returns the first token, from some list of tokens, that a token has
       * collided with during its last movement.
       * @param {Graphic} token           The token that moved
       * @param {Graphic[]} otherTokens   The list of tokens we are testing collisions with. 
       * @return {Graphic || false} If the token collided with another token, the first token that
       *          it would collide with during its movement is returned. Otherwise it returns false.
       */
      var getFirstCollision = function(token, otherTokens) {
          // Get all the points for the movement.
          var movePts = _getAllPointsDuringMovement(token);
          
          // Check each segment of movement to see if we triggered a trap.
          for(var i = 0; i < movePts.length-1; i++) {
              var p1 = movePts[i];
              var p2 = movePts[i+1];
              
              // If we encountered a trap, we're done.
              var collidedToken = _getFirstCollisionInWaypoint(token, otherTokens, p1, p2);
              if(collidedToken) {
                  return collidedToken;
              }
          }
          return false
      };
      
      
      /**
       * Returns the list of tokens, from some list of tokens, that a token has 
       * collided with during its last movement.
       * @param {Graphic} token           The token that moved
       * @param {Graphic[]} otherTokens   The tokens we're checking collisions with.
       * @return {Graphic[]}
       */
      var getCollisions = function(token, otherTokens) {
          var movePts = _getAllPointsDuringMovement(token);
          var collisions = [];
          
          // Check each segment of movement to see if we triggered a trap.
          for(var i = 0; i < movePts.length-1; i++) {
              var p1 = movePts[i];
              var p2 = movePts[i+1];
              
              // Append any collisions in this segment to the list.
              var newCollisions = _getCollisionsInWaypoint(token, otherTokens, p1, p2);
              collisions = collisions.concat(newCollisions);
          }
          return collisions
      };
      
      
      /**
       * @private
       * Gets the list of all points traversed during a token's movement, including 
       * its current position at the end of the movement.
       * @param {Graphic} token
       * @return {vec2[]}
       */
      var _getAllPointsDuringMovement = function(token) {
          var results = [];
          
          var moveCoords = token.get("lastmove").split(",");
          for(var i=0; i < moveCoords.length; i++) {
              moveCoords[i] = parseInt(moveCoords[i]);
          }
          moveCoords.push(parseInt(token.get("left")));
          moveCoords.push(parseInt(token.get("top")));
          
          for(var i=0; i <= moveCoords.length - 2; i+=2) {
              var x = moveCoords[i];
              var y = moveCoords[i+1];
              results.push([x, y]);
          }
          
          return results;
      };
      
      
      /**
       * @private
       * Checks if a token collided with another token during its movement between two points.
       * @param {Graphic} token
       * @param {Graphic[]} otherTokens   The list of tokens we are testing collisions with.
       * @param {vec2} startPt
       * @param {vec2} endPt
       * @return {Graphic || false} The first other token that token collided with in its movement. If there was no collision, return false.
       */
      var _getFirstCollisionInWaypoint = function(token, otherTokens, startPt, endPt) {
          var collisions = _getCollisionsInWaypoint(token, otherTokens, startPt, endPt);
          if(collisions.length > 0) {
              var bestToken;
              var bestDist;
              _.each(collisions, function(other) {
                  var otherPt = _getTokenPt(other);
                  var h = VecMath.ptSegDist(otherPt, startPt, endPt);
                  var r = (other.get('width') + token.get('width'))/2.0;
                  var ww = r*r - h*h;
                  
                  var projection = VecMath.projection(VecMath.vec(startPt, endPt), VecMath.vec(startPt, otherPt));
                  var dist = VecMath.length(projection);
                  dist *= dist;
                  dist -= ww;
                  dist = dist;
                  
                  if(bestDist === undefined || dist < bestDist) {
                      bestDist = dist;
                      bestToken = other;
                  }
              });
              return bestToken;
          }
          else {
              return false;
          }
      };
      
      
      /**
       * @private
       * For some token, this gets the list of tokens it collided with during its
       * movement between two points, from some list of other tokens.
       * @param {Graphic} token
       * @param {Graphic[]} otherTokens
       * @param {vec2} startPt
       * @param {vec2} endPt
       * @return {Graphic[]}
       */
      var _getCollisionsInWaypoint = function(token, otherTokens, startPt, endPt) {
          var result = [];
  
          for(var i in otherTokens) {
              var other = otherTokens[i];  
              if(_checkCollisionInWaypoint(token, other, startPt, endPt)) {
                  result.push(other);
              }
          }
          
          return result;
      };
      
      /**
       * @private
       * Checks if a token collides with the other token during its movement between
       * two points.
       * @param {Graphic} token   The moving token
       * @param {Graphic} other
       * @param {vec2} startPt
       * @param {vec2} endPt
       * @return {Boolean} true iff there was a collision.
       */
      var _checkCollisionInWaypoint = function(token, other, startPt, endPt) {
          var otherPt = _getTokenPt(other);
          
          // We assume that all tokens are circular, therefore width = diameter.
          var thresholdDist = (parseInt(other.get("width")) + parseInt(token.get("width")))/2;
          
          var distFromStart = Math.ceil(VecMath.dist(startPt, otherPt)) + 1; // +1 to make up for rounding error.
          
          // Don't count the other token if our movement already started in it.
          if(distFromStart >= thresholdDist) {
              // Figure out the closest distance we came to the other token during 
              // the movement.
              var dist = Math.ceil(VecMath.ptSegDist(otherPt, startPt, endPt)) + 1; // +1 to make up for rounding error.
              if(dist < thresholdDist) {
                  return true;
              }
          }
          
          return false;
      };
      
      
      /** 
       * @private
       * Returns the token nearest to the specified point 
       * out of some list of tokens.
       * @param {vec2} pt
       * @param {Graphic[]} tokens
       * @return {Graphic}
       */
      var _getNearestTokenToPoint = function(pt, tokens) {
        var result = null;
        var bestDist = -1;
        for(var i in tokens) {
            var token = tokens[i];
            
            var tokenPt = _getTokenPt(token);
            var dist = VecMath.dist(pt, tokenPt);
            
            if(bestDist === -1 || dist < bestDist) {
                result = token;
                bestDist = dist;
            }
        }
        
        return result;
      };
      
      
      /**
       * @private
       * Gets the position of a token.
       * @param {Graphic} token
       * @return {vec2}
       */
      var _getTokenPt = function(token) {
          var x = token.get("left");
          var y = token.get("top");
          return [x, y];
      };
      
      
      // The exposed API.
      return {
          getFirstCollision: getFirstCollision,
          getCollisions: getCollisions
      };
  })();