peng-pathfinding
Version:
Comprehensive pathfinding library for grid based games
1,363 lines (1,293 loc) • 143 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pengPathfinding = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
module.exports = require('./lib/heap');
},{"./lib/heap":2}],2:[function(require,module,exports){
// Generated by CoffeeScript 1.8.0
(function() {
var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
floor = Math.floor, min = Math.min;
/*
Default comparison function to be used
*/
defaultCmp = function(x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
};
/*
Insert item x in list a, and keep it sorted assuming a is sorted.
If x is already in a, insert it to the right of the rightmost x.
Optional args lo (default 0) and hi (default a.length) bound the slice
of a to be searched.
*/
insort = function(a, x, lo, hi, cmp) {
var mid;
if (lo == null) {
lo = 0;
}
if (cmp == null) {
cmp = defaultCmp;
}
if (lo < 0) {
throw new Error('lo must be non-negative');
}
if (hi == null) {
hi = a.length;
}
while (lo < hi) {
mid = floor((lo + hi) / 2);
if (cmp(x, a[mid]) < 0) {
hi = mid;
} else {
lo = mid + 1;
}
}
return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
};
/*
Push item onto heap, maintaining the heap invariant.
*/
heappush = function(array, item, cmp) {
if (cmp == null) {
cmp = defaultCmp;
}
array.push(item);
return _siftdown(array, 0, array.length - 1, cmp);
};
/*
Pop the smallest item off the heap, maintaining the heap invariant.
*/
heappop = function(array, cmp) {
var lastelt, returnitem;
if (cmp == null) {
cmp = defaultCmp;
}
lastelt = array.pop();
if (array.length) {
returnitem = array[0];
array[0] = lastelt;
_siftup(array, 0, cmp);
} else {
returnitem = lastelt;
}
return returnitem;
};
/*
Pop and return the current smallest value, and add the new item.
This is more efficient than heappop() followed by heappush(), and can be
more appropriate when using a fixed size heap. Note that the value
returned may be larger than item! That constrains reasonable use of
this routine unless written as part of a conditional replacement:
if item > array[0]
item = heapreplace(array, item)
*/
heapreplace = function(array, item, cmp) {
var returnitem;
if (cmp == null) {
cmp = defaultCmp;
}
returnitem = array[0];
array[0] = item;
_siftup(array, 0, cmp);
return returnitem;
};
/*
Fast version of a heappush followed by a heappop.
*/
heappushpop = function(array, item, cmp) {
var _ref;
if (cmp == null) {
cmp = defaultCmp;
}
if (array.length && cmp(array[0], item) < 0) {
_ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
_siftup(array, 0, cmp);
}
return item;
};
/*
Transform list into a heap, in-place, in O(array.length) time.
*/
heapify = function(array, cmp) {
var i, _i, _j, _len, _ref, _ref1, _results, _results1;
if (cmp == null) {
cmp = defaultCmp;
}
_ref1 = (function() {
_results1 = [];
for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
return _results1;
}).apply(this).reverse();
_results = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
i = _ref1[_i];
_results.push(_siftup(array, i, cmp));
}
return _results;
};
/*
Update the position of the given item in the heap.
This function should be called every time the item is being modified.
*/
updateItem = function(array, item, cmp) {
var pos;
if (cmp == null) {
cmp = defaultCmp;
}
pos = array.indexOf(item);
if (pos === -1) {
return;
}
_siftdown(array, 0, pos, cmp);
return _siftup(array, pos, cmp);
};
/*
Find the n largest elements in a dataset.
*/
nlargest = function(array, n, cmp) {
var elem, result, _i, _len, _ref;
if (cmp == null) {
cmp = defaultCmp;
}
result = array.slice(0, n);
if (!result.length) {
return result;
}
heapify(result, cmp);
_ref = array.slice(n);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
elem = _ref[_i];
heappushpop(result, elem, cmp);
}
return result.sort(cmp).reverse();
};
/*
Find the n smallest elements in a dataset.
*/
nsmallest = function(array, n, cmp) {
var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
if (cmp == null) {
cmp = defaultCmp;
}
if (n * 10 <= array.length) {
result = array.slice(0, n).sort(cmp);
if (!result.length) {
return result;
}
los = result[result.length - 1];
_ref = array.slice(n);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
elem = _ref[_i];
if (cmp(elem, los) < 0) {
insort(result, elem, 0, null, cmp);
result.pop();
los = result[result.length - 1];
}
}
return result;
}
heapify(array, cmp);
_results = [];
for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
_results.push(heappop(array, cmp));
}
return _results;
};
_siftdown = function(array, startpos, pos, cmp) {
var newitem, parent, parentpos;
if (cmp == null) {
cmp = defaultCmp;
}
newitem = array[pos];
while (pos > startpos) {
parentpos = (pos - 1) >> 1;
parent = array[parentpos];
if (cmp(newitem, parent) < 0) {
array[pos] = parent;
pos = parentpos;
continue;
}
break;
}
return array[pos] = newitem;
};
_siftup = function(array, pos, cmp) {
var childpos, endpos, newitem, rightpos, startpos;
if (cmp == null) {
cmp = defaultCmp;
}
endpos = array.length;
startpos = pos;
newitem = array[pos];
childpos = 2 * pos + 1;
while (childpos < endpos) {
rightpos = childpos + 1;
if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
childpos = rightpos;
}
array[pos] = array[childpos];
pos = childpos;
childpos = 2 * pos + 1;
}
array[pos] = newitem;
return _siftdown(array, startpos, pos, cmp);
};
Heap = (function() {
Heap.push = heappush;
Heap.pop = heappop;
Heap.replace = heapreplace;
Heap.pushpop = heappushpop;
Heap.heapify = heapify;
Heap.updateItem = updateItem;
Heap.nlargest = nlargest;
Heap.nsmallest = nsmallest;
function Heap(cmp) {
this.cmp = cmp != null ? cmp : defaultCmp;
this.nodes = [];
}
Heap.prototype.push = function(x) {
return heappush(this.nodes, x, this.cmp);
};
Heap.prototype.pop = function() {
return heappop(this.nodes, this.cmp);
};
Heap.prototype.peek = function() {
return this.nodes[0];
};
Heap.prototype.contains = function(x) {
return this.nodes.indexOf(x) !== -1;
};
Heap.prototype.replace = function(x) {
return heapreplace(this.nodes, x, this.cmp);
};
Heap.prototype.pushpop = function(x) {
return heappushpop(this.nodes, x, this.cmp);
};
Heap.prototype.heapify = function() {
return heapify(this.nodes, this.cmp);
};
Heap.prototype.updateItem = function(x) {
return updateItem(this.nodes, x, this.cmp);
};
Heap.prototype.clear = function() {
return this.nodes = [];
};
Heap.prototype.empty = function() {
return this.nodes.length === 0;
};
Heap.prototype.size = function() {
return this.nodes.length;
};
Heap.prototype.clone = function() {
var heap;
heap = new Heap();
heap.nodes = this.nodes.slice(0);
return heap;
};
Heap.prototype.toArray = function() {
return this.nodes.slice(0);
};
Heap.prototype.insert = Heap.prototype.push;
Heap.prototype.top = Heap.prototype.peek;
Heap.prototype.front = Heap.prototype.peek;
Heap.prototype.has = Heap.prototype.contains;
Heap.prototype.copy = Heap.prototype.clone;
return Heap;
})();
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
return define([], factory);
} else if (typeof exports === 'object') {
return module.exports = factory();
} else {
return root.Heap = factory();
}
})(this, function() {
return Heap;
});
}).call(this);
},{}],3:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var AStarFinder_1 = require("../finder/AStarFinder");
var BestFirstFinder_1 = require("../finder/BestFirstFinder");
//代价函数
var Heuristic_1 = require("../core/Heuristic");
//斜线运动方式
var ClassDef_1 = require("../core/ClassDef");
var DynamicFinder = /** @class */ (function () {
/**
* 动态寻路者
* @constructor
*/
function DynamicFinder() {
var optA = {
allowDiagonal: true,
dontCrossCorners: true,
diagonalMovement: ClassDef_1.DiagonalMovement.IfAtMostOneObstacle,
heuristic: Heuristic_1.default.octile,
weight: 1
};
var optB = {
allowDiagonal: true,
dontCrossCorners: true,
diagonalMovement: ClassDef_1.DiagonalMovement.IfAtMostOneObstacle,
weight: 1
};
this.aFinder = new AStarFinder_1.default(optA);
this.bFinder = new BestFirstFinder_1.default(optB);
}
/**
* 同步寻路方法
*/
DynamicFinder.prototype.findPathSync = function (startX, startY, endX, endY, grid) {
return this.aFinder.findPath(startX, startY, endX, endY, grid);
};
;
return DynamicFinder;
}());
exports.default = DynamicFinder;
},{"../core/ClassDef":5,"../core/Heuristic":7,"../finder/AStarFinder":9,"../finder/BestFirstFinder":10}],4:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* A node in grid.
* This class holds some basic information about a node and custom
* attributes may be added, depending on the algorithms' needs.
* @constructor
* @param {number} g - 当前节点到起始点的代价
* @param {number} h - 当前节点到终点的估价
* @param {number} f - 代价总和
* @param {boolean} opened - 节点是否已经开启
* @param {boolean} closed - 节点是否被关闭了
* @param {number} x - 网格上节点的x坐标。.
* @param {number} y - 网格上节点的y坐标。.
* @param {number} r - 代价系数
* @param {boolean} [walkable] - 这个节点是否可以行走.
*/
var Cell = /** @class */ (function () {
function Cell(x, y, walkable, r) {
/**
* The x coordinate of the node on the grid.
* @type number
*/
this.x = x;
/**
* The y coordinate of the node on the grid.
* @type number
*/
this.y = y;
/**
* Whether this node can be walked through.
* @type boolean
*/
this.walkable = (walkable === undefined ? true : walkable);
/**
* 默认的系数为1,这里指示达到目标点的难易程度
*/
this.r = (r === undefined ? 1 : r);
}
return Cell;
}());
exports.default = Cell;
},{}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//对角运动
var DiagonalMovement;
(function (DiagonalMovement) {
//总是
DiagonalMovement[DiagonalMovement["Always"] = 1] = "Always";
//绝不
DiagonalMovement[DiagonalMovement["Never"] = 2] = "Never";
//最多只有一个障碍时
DiagonalMovement[DiagonalMovement["IfAtMostOneObstacle"] = 3] = "IfAtMostOneObstacle";
//仅在没有任何障碍时
DiagonalMovement[DiagonalMovement["OnlyWhenNoObstacles"] = 4] = "OnlyWhenNoObstacles";
})(DiagonalMovement = exports.DiagonalMovement || (exports.DiagonalMovement = {}));
;
},{}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Cell_1 = require("./Cell");
var ClassDef_1 = require("./ClassDef");
/**代价网格 */
var Grid = /** @class */ (function () {
/**
* The Grid class, which serves as the encapsulation of the layout of the nodes.
* @constructor
* @param {number|Array<Array<(number|boolean)>>} width_or_matrix Number of columns of the grid, or matrix
* @param {number} height Number of rows of the grid.
* @param {Array<Array<(number|boolean)>>} [matrix] - A 0-1 matrix
* representing the walkable status of the nodes(0 or false for walkable).
* If the matrix is not supplied, all the nodes will be walkable. */
function Grid(x, y, size, width_or_matrix, height, matrix) {
var width;
if (typeof width_or_matrix !== 'object') {
width = width_or_matrix;
}
else {
height = width_or_matrix.length;
width = width_or_matrix[0].length;
matrix = width_or_matrix;
}
/**
* The number of columns of the grid.
* @type number
*/
this.width = width;
/**
* The number of rows of the grid.
* @type number
*/
this.height = height;
this.x = x;
this.y = y;
this.size = size;
/**
* A 2D array of nodes.
*/
this.nodes = this.buildNodes(width, height, matrix);
}
/**
* Build and return the nodes.
* @private
* @param {number} width
* @param {number} height
* @param {Array<Array<number|boolean>>} [matrix] - A 0-1 matrix representing
* the walkable status of the nodes.
* @see Grid
*/
Grid.prototype.buildNodes = function (width, height, matrix) {
var i, j, nodes = new Array(height);
for (i = 0; i < height; ++i) {
nodes[i] = new Array(width);
for (j = 0; j < width; ++j) {
nodes[i][j] = new Cell_1.default(j, i);
}
}
if (matrix === undefined) {
return nodes;
}
if (matrix.length !== height || matrix[0].length !== width) {
throw new Error('Matrix size does not fit');
}
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
if (matrix[i][j]) {
// 0, false, null will be walkable
// while others will be un-walkable
nodes[i][j].walkable = false;
}
}
}
return nodes;
};
;
Grid.prototype.getNodeAt = function (x, y) {
return this.nodes[y][x];
};
;
/**
* Determine whether the node at the given position is walkable.
* (Also returns false if the position is outside the grid.)
* @param {number} x - The x coordinate of the node.
* @param {number} y - The y coordinate of the node.
* @return {boolean} - The walkability of the node.
*/
Grid.prototype.isWalkableAt = function (x, y) {
return this.isInside(x, y) && this.nodes[y][x].walkable;
};
;
/**
* Determine whether the position is inside the grid.
* XXX: `grid.isInside(x, y)` is wierd to read.
* It should be `(x, y) is inside grid`, but I failed to find a better
* name for this method.
* @param {number} x
* @param {number} y
* @return {boolean}
*/
Grid.prototype.isInside = function (x, y) {
return (x >= 0 && x < this.width) && (y >= 0 && y < this.height);
};
;
/**
* Set whether the node on the given position is walkable.
* NOTE: throws exception if the coordinate is not inside the grid.
* @param {number} x - The x coordinate of the node.
* @param {number} y - The y coordinate of the node.
* @param {boolean} walkable - Whether the position is walkable.
*/
Grid.prototype.setWalkableAt = function (x, y, walkable) {
if (this.isInside(x, y))
this.nodes[y][x].walkable = walkable;
};
;
/**
* 设置代价系数,计算代价时将会乘以该值
* @param x
* @param y
* @param r
*/
Grid.prototype.setCostRate = function (x, y, r) {
if (this.isInside(x, y))
this.nodes[y][x].r = r;
};
/**
* 增加代价系数,计算代价时将会乘以该值
* @param x
* @param y
* @param r
*/
Grid.prototype.addCostRate = function (x, y, r) {
if (this.isInside(x, y))
this.nodes[y][x].r += r;
};
/**
* Get the neighbors of the given node.
*
* offsets diagonalOffsets:
* +---+---+---+ +---+---+---+
* | | 0 | | | 0 | | 1 |
* +---+---+---+ +---+---+---+
* | 3 | | 1 | | | | |
* +---+---+---+ +---+---+---+
* | | 2 | | | 3 | | 2 |
* +---+---+---+ +---+---+---+
*
* When allowDiagonal is true, if offsets[i] is valid, then
* diagonalOffsets[i] and
* diagonalOffsets[(i + 1) % 4] is valid.
* @param {Cell} node
* @param {DiagonalMovement} diagonalMovement
*/
Grid.prototype.getNeighbors = function (node, diagonalMovement) {
var x = node.x, y = node.y, neighbors = [], s0 = false, d0 = false, s1 = false, d1 = false, s2 = false, d2 = false, s3 = false, d3 = false, nodes = this.nodes;
// ↑
if (this.isWalkableAt(x, y - 1)) {
neighbors.push(nodes[y - 1][x]);
s0 = true;
}
// →
if (this.isWalkableAt(x + 1, y)) {
neighbors.push(nodes[y][x + 1]);
s1 = true;
}
// ↓
if (this.isWalkableAt(x, y + 1)) {
neighbors.push(nodes[y + 1][x]);
s2 = true;
}
// ←
if (this.isWalkableAt(x - 1, y)) {
neighbors.push(nodes[y][x - 1]);
s3 = true;
}
if (diagonalMovement === ClassDef_1.DiagonalMovement.Never) {
return neighbors;
}
if (diagonalMovement === ClassDef_1.DiagonalMovement.OnlyWhenNoObstacles) {
d0 = s3 && s0;
d1 = s0 && s1;
d2 = s1 && s2;
d3 = s2 && s3;
}
else if (diagonalMovement === ClassDef_1.DiagonalMovement.IfAtMostOneObstacle) {
d0 = s3 || s0;
d1 = s0 || s1;
d2 = s1 || s2;
d3 = s2 || s3;
}
else if (diagonalMovement === ClassDef_1.DiagonalMovement.Always) {
d0 = true;
d1 = true;
d2 = true;
d3 = true;
}
else {
throw new Error('Incorrect value of diagonalMovement');
}
// ↖
if (d0 && this.isWalkableAt(x - 1, y - 1)) {
neighbors.push(nodes[y - 1][x - 1]);
}
// ↗
if (d1 && this.isWalkableAt(x + 1, y - 1)) {
neighbors.push(nodes[y - 1][x + 1]);
}
// ↘
if (d2 && this.isWalkableAt(x + 1, y + 1)) {
neighbors.push(nodes[y + 1][x + 1]);
}
// ↙
if (d3 && this.isWalkableAt(x - 1, y + 1)) {
neighbors.push(nodes[y + 1][x - 1]);
}
return neighbors;
};
;
/**
* Get a clone of this grid.
* @return {Grid} Cloned grid.
*/
Grid.prototype.clone = function () {
var i, j, node, x = this.x, y = this.y, width = this.width, height = this.height, size = this.size, thisNodes = this.nodes, newGrid = new Grid(x, y, size, width, height), newNodes = new Array(height);
for (i = 0; i < height; ++i) {
newNodes[i] = new Array(width);
for (j = 0; j < width; ++j) {
node = thisNodes[i][j];
if (!node)
continue;
newNodes[i][j] = new Cell_1.default(j, i, node.walkable, node.r);
}
}
newGrid.nodes = newNodes;
return newGrid;
};
;
/**合并两个代价网格成为一个新的代价网格 */
Grid.prototype.merge = function (grid) {
var create;
var origin;
var target;
//填充的长度
var copyX, copyY;
var mergeX, mergeY;
var startX, startY;
var endX, endY;
//构建新的网格
var newX, newY, newW, newH, newSize;
//原网格覆盖的宽度和高度
var originW = this.width * this.size;
var originH = this.height * this.size;
//原网格最远端的坐标
var originX = this.x + originW;
var originY = this.y + originH;
//目标网格覆盖的宽度和高度
var targetW = grid.width * grid.size;
var targetH = grid.height * grid.size;
//目标网格最远端的坐标
var targetX = grid.x + targetW;
var targetY = grid.y + targetH;
//确定新网格的大小和位置
newSize = this.size;
newX = Math.min(this.x, grid.x);
newY = Math.min(this.y, grid.y);
newW = (Math.max(originX, targetX) - newX) / newSize;
newH = (Math.max(originY, targetY) - newY) / newSize;
create = new Grid(newX, newY, newSize, newW, newH);
origin = grid;
target = this;
mergeX = origin.width;
mergeY = origin.height;
copyX = target.width * (newSize / target.size);
copyY = target.height * (newSize / target.size);
startX = (origin.x - newX) / newSize;
startY = (origin.y - newY) / newSize;
endX = (target.x - newX) / newSize;
endY = (target.y - newY) / newSize;
//合并网格
for (var i = 0; i < mergeY; i++) {
for (var j = 0; j < mergeX; j++) {
var tmp = origin.nodes[i][j];
tmp.x = j + startX;
tmp.y = i + startY;
create.nodes[i + startY][j + startX] = tmp;
}
}
for (var i = 0; i < copyY; i++) {
for (var j = 0; j < copyX; j++) {
var tmp = target.nodes[i][j];
tmp.x = j + endX;
tmp.y = i + endY;
create.nodes[i + endY][j + endX] = tmp;
}
}
return create;
};
/**合并网格数组返回新的网格 */
Grid.Merge = function (arr) {
if (!arr || arr.length == 0)
return null;
if (arr.length == 1)
return arr[0].clone();
var create;
//构建新的网格
var x, y, w, h, size;
var grid;
grid = arr[0];
size = grid.size;
x = grid.x;
y = grid.y;
w = grid.x + grid.width * grid.size;
h = grid.y + grid.height * grid.size;
for (var i = 1; i < arr.length; i++) {
grid = arr[i];
x = Math.min(x, grid.x);
y = Math.min(y, grid.y);
w = Math.max(w, grid.x + grid.width * grid.size);
h = Math.max(h, grid.y + grid.height * grid.size);
}
w = (w - x) / size;
h = (h - y) / size;
create = new Grid(x, y, size, w, h);
for (var i = 0; i < arr.length; i++) {
grid = arr[i];
x = (grid.x - create.x) / size;
y = (grid.y - create.y) / size;
w = grid.width * (size / grid.size);
h = grid.height * (size / grid.size);
for (var i_1 = 0; i_1 < h; i_1++) {
for (var j = 0; j < w; j++) {
var tmp = grid.nodes[i_1][j];
tmp.x = j + x;
tmp.y = i_1 + y;
create.nodes[i_1 + y][j + x] = tmp;
}
}
}
return create;
};
return Grid;
}());
exports.default = Grid;
},{"./Cell":4,"./ClassDef":5}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* 代价函数
* @namespace pfg.Heuristic
* @description A collection of heuristic functions.
*/
var Heuristic = /** @class */ (function () {
function Heuristic() {
}
/**
* 曼哈顿距离,指的是直角折线的距离,即 X + Y
* Manhattan distance.
* @param {number} dx - Difference in x.
* @param {number} dy - Difference in y.
* @return {number} dx + dy
*/
Heuristic.manhattan = function (dx, dy) {
return dx + dy;
};
;
/**
* 欧氏距离,也就是直线距离
* Euclidean distance.
* @param {number} dx - Difference in x.
* @param {number} dy - Difference in y.
* @return {number} sqrt(dx * dx + dy * dy)
*/
Heuristic.euclidean = function (dx, dy) {
return Math.sqrt(dx * dx + dy * dy);
};
;
/**
* 八进制距离,斜线距离的特殊形式,允许存在折线的直线距离
* Octile distance.
* @param {number} dx - Difference in x.
* @param {number} dy - Difference in y.
* @return {number} (dx < dy) ? F * dx + dy : F * dy + dx;
*/
Heuristic.octile = function (dx, dy) {
//返归根号2
var F = Math.SQRT2 - 1;
return (dx < dy) ? F * dx + dy : F * dy + dx;
};
;
/**
* 切比雪夫距离,斜线距离的特殊形式,即X或者Y中取最大值
* Chebyshev distance.
* @param {number} dx - Difference in x.
* @param {number} dy - Difference in y.
* @return {number} max(dx, dy)
*/
Heuristic.chebyshev = function (dx, dy) {
return Math.max(dx, dy);
};
return Heuristic;
}());
exports.default = Heuristic;
;
},{}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Util = /** @class */ (function () {
function Util() {
}
/**
* 根据父记录进行回溯跟踪并返回路径。
* (包括开始节点和结束节点)
* 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
*/
Util.backtrace = function (node) {
var path = [[node.x, node.y]];
while (node.parent) {
node = node.parent;
path.push([node.x, node.y]);
}
//翻转路径数组
return path.reverse();
};
/**
* 从开始和结束节点回溯跟踪,并返回路径。
* (包括开始节点和结束节点)
* Backtrace from start and end node, and return the path.
* (including both start and end nodes)
* @param {Node}
* @param {Node}
*/
Util.biBacktrace = function (nodeA, nodeB) {
var pathA = this.backtrace(nodeA), pathB = this.backtrace(nodeB);
return pathA.concat(pathB.reverse());
};
/**
* 计算路径的长度。
* Compute the length of the path.
* @param {Array<Array<number>>} path The path
* @return {number} The length of the path
*/
Util.pathLength = function (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;
};
/**
* 给定开始和结束坐标,根据Bresenham的算法返回由这些坐标构成的直线上的所有坐标。
* 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
*/
Util.interpolate = function (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;
};
/**
* 给定一个压缩路径,返回一个新路径,该路径中包含所有插值段。
* 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
*/
Util.expandPath = function (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 = this.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;
};
/**
* 平滑给出的路径。
* 原始路径不会被修改;将返回一条新路径。
* Smoothen the give path.
* The original path will not be modified; a new path will be returned.
* @param {pfg.Grid} grid
* @param {Array<Array<number>>} path The path
*/
Util.smoothenPath = function (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 = this.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) {
var lastValidCoord = path[i - 1];
newPath.push(lastValidCoord);
sx = lastValidCoord[0];
sy = lastValidCoord[1];
}
}
newPath.push([x1, y1]);
return newPath;
};
/**
* 压缩路径,删除冗余节点,不改变形状,不修改原始路径,将返回一条新路径。
* 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
*/
Util.compressPath = function (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;
};
return Util;
}());
exports.default = Util;
},{}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//堆
var Heap = require("heap");
//工具
var Util_1 = require("../core/Util");
//代价函数
var Heuristic_1 = require("../core/Heuristic");
//斜线运动方式
var ClassDef_1 = require("../core/ClassDef");
var AStarFinder = /** @class */ (function () {
/**
* A* path-finder. Based upon https://github.com/bgrins/javascript-astar
* @constructor
* @param {Object} opt
* @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
* Deprecated, use diagonalMovement instead.
* @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching
* block corners. Deprecated, use diagonalMovement instead.
* @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement.
* @param {function} opt.heuristic Heuristic function to estimate the distance
* (defaults to manhattan).
* @param {number} opt.weight Weight to apply to the heuristic to allow for
* suboptimal paths, in order to speed up the search.
*/
function AStarFinder(opt) {
opt = opt || {};
this.allowDiagonal = opt.allowDiagonal;
this.dontCrossCorners = opt.dontCrossCorners;
this.heuristic = opt.heuristic || Heuristic_1.default.manhattan;
this.weight = opt.weight || 1;
this.diagonalMovement = opt.diagonalMovement;
if (!this.diagonalMovement) {
if (!this.allowDiagonal) {
this.diagonalMovement = ClassDef_1.DiagonalMovement.Never;
}
else {
if (this.dontCrossCorners) {
this.diagonalMovement = ClassDef_1.DiagonalMovement.OnlyWhenNoObstacles;
}
else {
this.diagonalMovement = ClassDef_1.DiagonalMovement.IfAtMostOneObstacle;
}
}
}
//是否运行使用对角线移动
// When diagonal movement is allowed the manhattan heuristic is not
//admissible. It should be octile instead
if (this.diagonalMovement === ClassDef_1.DiagonalMovement.Never) {
this.heuristic = opt.heuristic || Heuristic_1.default.manhattan;
}
else {
this.heuristic = opt.heuristic || Heuristic_1.default.octile;
}
}
/**
* 寻找并返回一条路径
* Find and return the the path.
* @param {number} startX - 起点X坐标
* @param {number} startY - 起点Y坐标
* @param {number} endX - 终点X坐标
* @param {number} endY - 终点Y坐标
* @param {Grid} grid - 网格地图
* @return {Array<Array<number>>} The path, including both start and
* end positions.
*/
AStarFinder.prototype.findPath = function (startX, startY, endX, endY, grid) {
startX = Math.round((startX - grid.x) / grid.size);
startY = Math.round((startY - grid.y) / grid.size);
endX = Math.round((endX - grid.x) / grid.size);
endY = Math.round((endY - grid.y) / grid.size);
if (startX == -1)
startX = 0;
if (startX == grid.width)
startX = grid.width - 1;
if (startY == -1)
startY = 0;
if (startY == grid.height)
startY = grid.height - 1;
if (endX == -1)
endX = 0;
if (endX == grid.width)
endX = grid.width - 1;
if (endY == -1)
endY = 0;
if (endY == grid.height)
endY = grid.height - 1;
//let width = grid.x + grid.width * grid.size;
//let height = grid.y + grid.height * grid.size;
//排除超出范围的寻路
if (startX < 0 ||
startX >= grid.width ||
startY < 0 ||
startY >= grid.height ||
endX < 0 ||
endX >= grid.width ||
endY < 0 ||
endY >= grid.height)
return;
//创建堆,交换条件为节点的代价总和
var openList = new Heap(function (nodeA, nodeB) {
return nodeA.f - nodeB.f;
}),
//创建开始节点
startNode = grid.getNodeAt(startX, startY),
//创建结束节点
endNode = grid.getNodeAt(endX, endY),
//设置代价函数
heuristic = this.heuristic,
//是否运行斜线运动
diagonalMovement = this.diagonalMovement,
//宽度
weight = this.weight,
//取绝对值函数
abs = Math.abs, SQRT2 = Math.SQRT2,
//临时变量
node, neighbors, neighbor, i, l, x, y, r, ng;
//设置起始节点的代价为0
// set the `g` and `f` value of the start node to be 0
startNode.g = 0;
startNode.f = 0;
// push the start node into the open list
//放入初始节点
openList.push(startNode);
//设置初始节点已经开启
startNode.opened = true;
//直到无法找到可以开启的格子
// while the open list is not empty
while (!openList.empty()) {
//弹出具有最小代价总和 f 值的节点位置。
// pop the position of node which has the minimum `f` value.
node = openList.pop();
node.closed = true;
//如果到达了结束节点
// if reached the end position, construct the path and return it
if (node === endNode) {
//根据父节点回溯跟踪返回路径
return Util_1.default.backtrace(endNode);
}
//获取附近的节点并遍历
// get neigbours of the current node
neighbors = grid.getNeighbors(node, diagonalMovement);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
//如果节点是关闭的则跳过
if (neighbor.closed) {
continue;
}
//相邻节点的坐标
x = neighbor.x;
y = neighbor.y;
//相邻节点的代价比率
r = neighbor.r;
//得到当前节点与邻近节点之间的距离,计算下一个代价 g
// get the distance between current node and the neighbor
// and calculate the next g score
//console.log("[P]"+x+","+y+"[系数]"+r);
ng = node.g + r * ((x - node.x === 0 || y - node.y === 0) ? 1 : SQRT2);
// 检查邻居是否还没有被检查,或者是否可以从当前节点以更小的成本到达
// 即为寻找代价最小的节点
// check if the neighbor has not been inspected yet, or
// can be reached with smaller cost from the current node
if (!neighbor.opened || ng < neighbor.g) {
//为临近节点设置到起点的代价
neighbor.g = ng;
//为临近节点设置到终点的估价
neighbor.h = neighbor.h || weight * heuristic(abs(x - endX), abs(y - endY));
//为临近节点设置新的代价总和
neighbor.f = neighbor.g + neighbor.h;
//设置临近节点的父节点为当前节点
neighbor.parent = node;
//临近节点是否已经开启
if (!neighbor.opened) {
//开启列表中放入临近节点
openList.push(neighbor);
//设置临近节点为开启
neighbor.opened = true;
}
else {
//发现可以用更少的成本到达临近节点。
//因为它的 f 值已经更新了,所以我们不得不更新其在开启列表中的位置
// the neighbor can be reached with smaller cost.
// Since its f value has been updated, we have to
// update its position in the open list
openList.updateItem(neighbor);
}
}
} // end for each neighbor
} // end while not open list empty
// fail to find the path
return [];
};
;
return AStarFinder;
}());
exports.default = AStarFinder;
},{"../core/ClassDef":5,"../core/Heuristic":7,"../core/Util":8,"heap":1}],10:[function(require,module,exports){
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var AStarFinder_1 = require("./AStarFinder");
var BestFirstFinder = /** @class */ (function (_super) {
__extends(BestFirstFinder, _super);
/**
* Best-First-Search path-finder.
* @constructor
* @extends AStarFinder
* @param {Object} opt 配置信息
* @param {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
* Deprecated, use diagonalMovement instead.
* @param {boolean} opt.dontCrossCorners Disallow diagonal movement touching
* block corners. Deprecated, use diagonalMovement instead.
* @param {DiagonalMovement} opt.diagonalMovement Allowed diagonal movement.
* @param {function} opt.heuristic Heuristic function to estimate the distance
* (defaults to manhattan).
*/
function BestFirstFinder(opt) {
var _this = _super.call(this, opt) || this;
var orig = _this.heuristic;
//放大原本的代价函数
_this.heuristic = function (dx, dy) {
return orig(dx, dy) * 1000000;
};
return _this;
}
return BestFirstFinder;
}(AStarFinder_1.default));
exports.default = BestFirstFinder;
},{"./AStarFinder":9}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Grid_1 = require("./core/Grid");
exports.Grid = Grid_1.default;
var ClassDef_1 = require("./core/ClassDef");
exports.DiagonalMovement = ClassDef_1.DiagonalMovement;
var DynamicFinder_1 = require("./app/DynamicFinder");
exports.DynamicFinder = DynamicFinder_1.default;
var Heuristic_1 = require("./core/Heuristic");
exports.Heuristic = Heuristic_1.default;
var AStarFinder_1 = require("./finder/AStarFinder");
exports.AStarFinder = AStarFinder_1.default;
var BestFirstFinder_1 = require("./finder/BestFirstFinder");
exports.BestFirstFinder = BestFirstFinder_1.default;
var Cell_1 = require("./core/Cell");
exports.Cell = Cell_1.default;
var Util_1 = require("./core/Util");
exports.Util = Util_1.default;
var Heap = require("heap");
exports.Heap = Heap;
function version() {
return "pfg@0.5.0";
}
exports.version = version;
},{"./app/DynamicFinder":3,"./core/Cell":4,"./core/ClassDef":5,"./core/Grid":6,"./core/Heuristic":7,"./core/Util":8,"./finder/AStarFinder":9,"./finder/BestFirstFinder":10,"heap":1}]},{},[11])(11)
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvaGVhcC9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9oZWFwL2xpYi9oZWFwLmpzIiwic3JjL2FwcC9EeW5hbWljRmluZGVyLnRzIiwic3JjL2NvcmUvQ2VsbC50cyIsInNyYy9jb3JlL0NsYXNzRGVmLnRzIiwic3JjL2NvcmUvR3JpZC50cyIsInNyYy9jb3JlL0hldXJpc3RpYy50cyIsInNyYy9jb3JlL1V0aWwudHMiLCJzcmMvZmluZGVyL0FTdGFyRmluZGVyLnRzIiwic3JjL2ZpbmRlci9CZXN0Rmlyc3RGaW5kZXIudHMiLCJzcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBOztBQ0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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