docs: function(origin, direction, callback) {
          var dirX = direction.x,
              dirY = direction.y;
          // copy input data
          // TODO maybe allow HashMap.key search with point only
          origin = {
              _x: origin._x,
              _y: origin._y,
              _w: 0,
              _h: 0
          };
  
          var keyBounds = this._keyBoundaries();
          var keys = HashMap.key(origin, keyHolder);
  
          // calculate col & row cell indices
          var currentCol = keys.x1,
              currentRow = keys.y1;
          var minCol = keyBounds.minX,
              minRow = keyBounds.minY,
              maxCol = keyBounds.maxX,
              maxRow = keyBounds.maxY;
          // direction to traverse cells
          var stepCol = dirX > 0 ? 1 : dirX < 0 ? -1 : 0,
              stepRow = dirY > 0 ? 1 : dirY < 0 ? -1 : 0;
  
          // first, next cell edge in absolute coordinates
          var firstCellEdgeX =
                  dirX >= 0 ? (currentCol + 1) * cellsize : currentCol * cellsize,
              firstCellEdgeY =
                  dirY >= 0 ? (currentRow + 1) * cellsize : currentRow * cellsize;
  
          // distance from origin to previous cell edge
          var previousDistance = -Infinity;
  
          // distances to next horizontal and vertical cell edge
          var deltaDistanceX = 0, // distance for the ray to be advanced to cross a whole cell horizontally
              deltaDistanceY = 0, // distance for the ray to be advanced to cross a whole cell vertically
              nextDistanceX = Infinity, // distance we can advance(increase magnitude) ray until we advance to next horizontal cell
              nextDistanceY = Infinity; // distance we can advance(increase magnitude) ray until we advance to next vertical cell
  
          var norm;
          if (dirX !== 0) {
              norm = 1.0 / dirX;
              nextDistanceX = (firstCellEdgeX - origin._x) * norm;
              deltaDistanceX = cellsize * stepCol * norm;
          }
          if (dirY !== 0) {
              norm = 1.0 / dirY;
              nextDistanceY = (firstCellEdgeY - origin._y) * norm;
              deltaDistanceY = cellsize * stepRow * norm;
          }
  
          // advance starting cell to be inside of map bounds
          while (
              (stepCol === 1 && currentCol < minCol && minCol !== Infinity) ||
              (stepCol === -1 && currentCol > maxCol && maxCol !== -Infinity) ||
              (stepRow === 1 && currentRow < minRow && minRow !== Infinity) ||
              (stepRow === -1 && currentRow > maxRow && maxRow !== -Infinity)
          ) {
              // advance to closest cell
              if (nextDistanceX < nextDistanceY) {
                  previousDistance = nextDistanceX;
  
                  currentCol += stepCol;
                  nextDistanceX += deltaDistanceX;
              } else {
                  previousDistance = nextDistanceY;
  
                  currentRow += stepRow;
                  nextDistanceY += deltaDistanceY;
              }
          }
  
          var cell;
          // traverse over cells
          // TODO: maybe change condition to `while (currentCol !== endX) || (currentRow !== endY)`
          while (
              minCol <= currentCol &&
              currentCol <= maxCol &&
              (minRow <= currentRow && currentRow <= maxRow)
          ) {
              // process cell
              if ((cell = this.map[(currentCol << 16) ^ currentRow])) {
                  // check each object inside this cell
                  for (var k = 0; k < cell.length; k++) {
                      // if supplied callback returns true, abort traversal
                      if (callback(cell[k], previousDistance)) return;
                  }
              }
  
              // advance to closest cell
              if (nextDistanceX < nextDistanceY) {
                  previousDistance = nextDistanceX;
  
                  currentCol += stepCol;
                  nextDistanceX += deltaDistanceX;
              } else {
                  previousDistance = nextDistanceY;
  
                  currentRow += stepRow;
                  nextDistanceY += deltaDistanceY;
              }
          }
      }