UNPKG

pathfinding

Version:

Comprehensive pathfinding library for grid based games

247 lines (207 loc) 6.21 kB
/** * Backtrace according to the parent records and return the path. * (including both start and end nodes) * @param {Node} node End node * @return {Array<Array<number>>} the path */ function backtrace(node) { var path = [[node.x, node.y]]; while (node.parent) { node = node.parent; path.push([node.x, node.y]); } return path.reverse(); } exports.backtrace = backtrace; /** * Backtrace from start and end node, and return the path. * (including both start and end nodes) * @param {Node} * @param {Node} */ function biBacktrace(nodeA, nodeB) { var pathA = backtrace(nodeA), pathB = backtrace(nodeB); return pathA.concat(pathB.reverse()); } exports.biBacktrace = biBacktrace; /** * Compute the length of the path. * @param {Array<Array<number>>} path The path * @return {number} The length of the path */ function pathLength(path) { var i, sum = 0, a, b, dx, dy; for (i = 1; i < path.length; ++i) { a = path[i - 1]; b = path[i]; dx = a[0] - b[0]; dy = a[1] - b[1]; sum += Math.sqrt(dx * dx + dy * dy); } return sum; } exports.pathLength = pathLength; /** * Given the start and end coordinates, return all the coordinates lying * on the line formed by these coordinates, based on Bresenham's algorithm. * http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification * @param {number} x0 Start x coordinate * @param {number} y0 Start y coordinate * @param {number} x1 End x coordinate * @param {number} y1 End y coordinate * @return {Array<Array<number>>} The coordinates on the line */ function interpolate(x0, y0, x1, y1) { var abs = Math.abs, line = [], sx, sy, dx, dy, err, e2; dx = abs(x1 - x0); dy = abs(y1 - y0); sx = (x0 < x1) ? 1 : -1; sy = (y0 < y1) ? 1 : -1; err = dx - dy; while (true) { line.push([x0, y0]); if (x0 === x1 && y0 === y1) { break; } e2 = 2 * err; if (e2 > -dy) { err = err - dy; x0 = x0 + sx; } if (e2 < dx) { err = err + dx; y0 = y0 + sy; } } return line; } exports.interpolate = interpolate; /** * Given a compressed path, return a new path that has all the segments * in it interpolated. * @param {Array<Array<number>>} path The path * @return {Array<Array<number>>} expanded path */ function expandPath(path) { var expanded = [], len = path.length, coord0, coord1, interpolated, interpolatedLen, i, j; if (len < 2) { return expanded; } for (i = 0; i < len - 1; ++i) { coord0 = path[i]; coord1 = path[i + 1]; interpolated = interpolate(coord0[0], coord0[1], coord1[0], coord1[1]); interpolatedLen = interpolated.length; for (j = 0; j < interpolatedLen - 1; ++j) { expanded.push(interpolated[j]); } } expanded.push(path[len - 1]); return expanded; } exports.expandPath = expandPath; /** * Smoothen the give path. * The original path will not be modified; a new path will be returned. * @param {PF.Grid} grid * @param {Array<Array<number>>} path The path */ function smoothenPath(grid, path) { var len = path.length, x0 = path[0][0], // path start x y0 = path[0][1], // path start y x1 = path[len - 1][0], // path end x y1 = path[len - 1][1], // path end y sx, sy, // current start coordinate ex, ey, // current end coordinate newPath, i, j, coord, line, testCoord, blocked; sx = x0; sy = y0; newPath = [[sx, sy]]; for (i = 2; i < len; ++i) { coord = path[i]; ex = coord[0]; ey = coord[1]; line = interpolate(sx, sy, ex, ey); blocked = false; for (j = 1; j < line.length; ++j) { testCoord = line[j]; if (!grid.isWalkableAt(testCoord[0], testCoord[1])) { blocked = true; break; } } if (blocked) { lastValidCoord = path[i - 1]; newPath.push(lastValidCoord); sx = lastValidCoord[0]; sy = lastValidCoord[1]; } } newPath.push([x1, y1]); return newPath; } exports.smoothenPath = smoothenPath; /** * Compress a path, remove redundant nodes without altering the shape * The original path is not modified * @param {Array<Array<number>>} path The path * @return {Array<Array<number>>} The compressed path */ function compressPath(path) { // nothing to compress if(path.length < 3) { return path; } var compressed = [], sx = path[0][0], // start x sy = path[0][1], // start y px = path[1][0], // second point x py = path[1][1], // second point y dx = px - sx, // direction between the two points dy = py - sy, // direction between the two points lx, ly, ldx, ldy, sq, i; // normalize the direction sq = Math.sqrt(dx*dx + dy*dy); dx /= sq; dy /= sq; // start the new path compressed.push([sx,sy]); for(i = 2; i < path.length; i++) { // store the last point lx = px; ly = py; // store the last direction ldx = dx; ldy = dy; // next point px = path[i][0]; py = path[i][1]; // next direction dx = px - lx; dy = py - ly; // normalize sq = Math.sqrt(dx*dx + dy*dy); dx /= sq; dy /= sq; // if the direction has changed, store the point if ( dx !== ldx || dy !== ldy ) { compressed.push([lx,ly]); } } // store the last point compressed.push([px,py]); return compressed; } exports.compressPath = compressPath;